about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2019-06-28 23:07:55 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2019-06-28 23:07:55 +0000
commit11fd0bc3fdbe7b5eb9266a728a81d0bcac91fe32 (patch)
tree7c40f096dd973943ef563ec87b2a68d8205db4fb
parent89c6ec14eb7514630aea5abc4b90b51d1473d33a (diff)
downloadnetpbm-mirror-11fd0bc3fdbe7b5eb9266a728a81d0bcac91fe32.tar.gz
netpbm-mirror-11fd0bc3fdbe7b5eb9266a728a81d0bcac91fe32.tar.xz
netpbm-mirror-11fd0bc3fdbe7b5eb9266a728a81d0bcac91fe32.zip
Promote Stable to Super_stable
git-svn-id: http://svn.code.sf.net/p/netpbm/code/super_stable@3640 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--GNUmakefile206
-rw-r--r--analyzer/Makefile2
-rw-r--r--analyzer/pamfile.c26
-rw-r--r--analyzer/pammosaicknit.c253
-rw-r--r--analyzer/pamsharpmap.c4
-rw-r--r--analyzer/pamsharpness.c22
-rw-r--r--analyzer/pamslice.c2
-rw-r--r--analyzer/pamsumm.c4
-rw-r--r--analyzer/pamtilt.c167
-rw-r--r--analyzer/pgmhist.c455
-rw-r--r--analyzer/pgmtexture.c1338
-rw-r--r--analyzer/pnmhistmap.c342
-rw-r--r--analyzer/pnmpsnr.c453
-rw-r--r--analyzer/ppmhist.c369
-rw-r--r--buildtools/Makefile15
-rw-r--r--buildtools/README.pkg70
-rwxr-xr-xbuildtools/configure.pl691
-rwxr-xr-xbuildtools/empty_depend19
-rwxr-xr-xbuildtools/installnetpbm.pl429
-rwxr-xr-xbuildtools/installosf2
-rwxr-xr-xbuildtools/makeman20
-rw-r--r--buildtools/manpage.mk68
-rwxr-xr-xbuildtools/stamp-date2
-rw-r--r--common.mk150
-rw-r--r--config.mk.in72
-rw-r--r--converter/bmp.h56
-rw-r--r--converter/other/Makefile204
-rwxr-xr-xconverter/other/anytopnm7
-rw-r--r--converter/other/avstopam.c103
-rw-r--r--converter/other/bmepsoe.c560
-rw-r--r--converter/other/bmepsoe.h79
-rw-r--r--converter/other/bmptopnm.c363
-rw-r--r--converter/other/cameratopam/Makefile22
-rw-r--r--converter/other/cameratopam/camera.c375
-rw-r--r--converter/other/cameratopam/camera.h95
-rw-r--r--converter/other/cameratopam/cameratopam.c1237
-rw-r--r--converter/other/cameratopam/cameratopam.h6
-rw-r--r--converter/other/cameratopam/canon.c6
-rw-r--r--converter/other/cameratopam/canon.h11
-rw-r--r--converter/other/cameratopam/dng.c151
-rw-r--r--converter/other/cameratopam/dng.h4
-rw-r--r--converter/other/cameratopam/foveon.c37
-rw-r--r--converter/other/cameratopam/foveon.h13
-rw-r--r--converter/other/cameratopam/global_variables.h1
-rw-r--r--converter/other/cameratopam/identify.c2256
-rw-r--r--converter/other/cameratopam/identify.h4
-rw-r--r--converter/other/cameratopam/ljpeg.c238
-rw-r--r--converter/other/cameratopam/ljpeg.h14
-rw-r--r--converter/other/exif.c1216
-rw-r--r--converter/other/exif.h24
-rw-r--r--converter/other/fiasco/Makefile16
-rw-r--r--converter/other/fiasco/binerror.c8
-rw-r--r--converter/other/fiasco/codec/approx.c4
-rw-r--r--converter/other/fiasco/codec/coder.c161
-rw-r--r--converter/other/fiasco/codec/control.c6
-rw-r--r--converter/other/fiasco/codec/decoder.c1809
-rw-r--r--converter/other/fiasco/codec/dfiasco.c8
-rw-r--r--converter/other/fiasco/codec/domain-pool.c16
-rw-r--r--converter/other/fiasco/codec/ip.c4
-rw-r--r--converter/other/fiasco/codec/motion.c12
-rw-r--r--converter/other/fiasco/codec/mwfa.c17
-rw-r--r--converter/other/fiasco/codec/options.c7
-rw-r--r--converter/other/fiasco/codec/prediction.c6
-rw-r--r--converter/other/fiasco/codec/subdivide.c56
-rw-r--r--converter/other/fiasco/codec/tiling.c80
-rw-r--r--converter/other/fiasco/codec/wfa.h4
-rw-r--r--converter/other/fiasco/codec/wfalib.c11
-rw-r--r--converter/other/fiasco/config.h3
-rw-r--r--converter/other/fiasco/display.c15
-rw-r--r--converter/other/fiasco/fiascotopnm.c10
-rw-r--r--converter/other/fiasco/getopt.c10
-rw-r--r--converter/other/fiasco/input/basis.c2
-rw-r--r--converter/other/fiasco/input/read.c8
-rw-r--r--converter/other/fiasco/input/weights.c204
-rw-r--r--converter/other/fiasco/lib/arith.c4
-rw-r--r--converter/other/fiasco/lib/bit-io.c6
-rw-r--r--converter/other/fiasco/lib/dither.c32
-rw-r--r--converter/other/fiasco/lib/error.c39
-rw-r--r--converter/other/fiasco/lib/image.c6
-rw-r--r--converter/other/fiasco/lib/list.c6
-rw-r--r--converter/other/fiasco/lib/macros.h11
-rw-r--r--converter/other/fiasco/lib/misc.c33
-rw-r--r--converter/other/fiasco/lib/misc.h4
-rw-r--r--converter/other/fiasco/output/matrices.c8
-rw-r--r--converter/other/fiasco/output/weights.c293
-rw-r--r--converter/other/fiasco/params.c151
-rw-r--r--converter/other/fiasco/pnmtofiasco.c418
-rw-r--r--converter/other/fitstopnm.c203
-rw-r--r--converter/other/giftopnm.c1639
-rw-r--r--converter/other/hdifftopam.c4
-rw-r--r--converter/other/infotopam.c70
-rw-r--r--converter/other/ipdb.c384
-rw-r--r--converter/other/ipdb.h243
-rw-r--r--converter/other/jbig/ANNOUNCE243
-rw-r--r--converter/other/jbig/Makefile39
-rw-r--r--converter/other/jbig/README20
-rw-r--r--converter/other/jbig/README.Netpbm12
-rw-r--r--converter/other/jbig/jbig_tab.c428
-rw-r--r--converter/other/jbig/jbigtopnm.c2
-rw-r--r--converter/other/jbig/libjbig/ANNOUNCE172
-rw-r--r--converter/other/jbig/libjbig/COPYING339
-rw-r--r--converter/other/jbig/libjbig/Makefile24
-rw-r--r--converter/other/jbig/libjbig/include/jbig.h (renamed from converter/other/jbig/jbig.h)126
-rw-r--r--converter/other/jbig/libjbig/include/jbig_ar.h53
-rw-r--r--converter/other/jbig/libjbig/jbig.c (renamed from converter/other/jbig/jbig.c)1842
-rw-r--r--converter/other/jbig/libjbig/jbig.txt (renamed from converter/other/jbig/jbig.doc)409
-rw-r--r--converter/other/jbig/libjbig/jbig_ar.c417
-rw-r--r--converter/other/jbig/pnmtojbig.c2
-rw-r--r--converter/other/jpeg2000/Makefile26
-rw-r--r--converter/other/jpeg2000/jpeg2ktopam.c68
-rw-r--r--converter/other/jpeg2000/libjasper/README4
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_image.c8
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_seq.c3
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_stream.c9
-rw-r--r--converter/other/jpeg2000/libjasper/common.mk8
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_image.h40
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_types.h2
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig228
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/jp2_cod.c1055
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/jp2_dec.c8
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_bs.c4
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_bs.h2
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_cs.c2010
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_cs.h2
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_dec.c3524
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_dec.h2
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_enc.c4810
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_math.h2
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h2
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.c392
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c8
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.h4
-rw-r--r--converter/other/jpeg2000/pamtojpeg2k.c143
-rw-r--r--converter/other/jpegtopnm.c24
-rw-r--r--converter/other/pamrgbatopng.c150
-rw-r--r--converter/other/pamtoavs.c150
-rw-r--r--converter/other/pamtodjvurle.c4
-rw-r--r--converter/other/pamtofits.c8
-rw-r--r--converter/other/pamtogif.c14
-rw-r--r--converter/other/pamtohdiff.c4
-rw-r--r--converter/other/pamtohtmltbl.c4
-rw-r--r--converter/other/pamtompfont.c6
-rw-r--r--converter/other/pamtooctaveimg.c4
-rw-r--r--converter/other/pamtopam.c2
-rw-r--r--converter/other/pamtopdbimg.c810
-rw-r--r--converter/other/pamtopfm.c8
-rw-r--r--converter/other/pamtopng.c825
-rw-r--r--converter/other/pamtopnm.c36
-rw-r--r--converter/other/pamtosrf.c217
-rw-r--r--converter/other/pamtosvg/Makefile32
-rw-r--r--converter/other/pamtosvg/bitmap.c4
-rw-r--r--converter/other/pamtosvg/bitmap.h2
-rw-r--r--converter/other/pamtosvg/image-proc.c4
-rw-r--r--converter/other/pamtosvg/message.h16
-rw-r--r--converter/other/pamtosvg/pamtosvg.c14
-rw-r--r--converter/other/pamtosvg/pxl-outline.c1
-rw-r--r--converter/other/pamtosvg/thin-image.c6
-rw-r--r--converter/other/pamtosvg/vector.c1
-rw-r--r--converter/other/pamtotga.c6
-rw-r--r--converter/other/pamtotiff.c593
-rw-r--r--converter/other/pamtouil.c8
-rw-r--r--converter/other/pamtowinicon.c1177
-rw-r--r--converter/other/pamtoxvmini.c4
-rw-r--r--converter/other/pdbimgtopam.c774
-rw-r--r--converter/other/pfmtopam.c4
-rw-r--r--converter/other/pgmtopbm.c6
-rw-r--r--converter/other/pgmtoppm.c5
-rw-r--r--converter/other/pngtopam.c1237
-rw-r--r--converter/other/pngtopnm.c1255
-rw-r--r--converter/other/pngtxt.c468
-rw-r--r--converter/other/pngtxt.h14
-rw-r--r--converter/other/pngx.c757
-rw-r--r--converter/other/pngx.h274
-rw-r--r--converter/other/pnmtojpeg.c13
-rw-r--r--converter/other/pnmtopalm/Makefile16
-rw-r--r--converter/other/pnmtopalm/gen_palm_colormap.c4
-rw-r--r--converter/other/pnmtopalm/palm.h8
-rw-r--r--converter/other/pnmtopalm/palmcolormap.c8
-rw-r--r--converter/other/pnmtopalm/palmtopnm.c4
-rw-r--r--converter/other/pnmtopalm/pnmtopalm.c125
-rw-r--r--converter/other/pnmtopclxl.c1069
-rw-r--r--converter/other/pnmtopng.README101
-rw-r--r--converter/other/pnmtopng.c1439
-rw-r--r--converter/other/pnmtops.c1711
-rw-r--r--converter/other/pnmtorast.c501
-rw-r--r--converter/other/pnmtosgi.c513
-rw-r--r--converter/other/pnmtoxwd.c24
-rw-r--r--converter/other/ppmtopgm.c4
-rw-r--r--converter/other/pstopnm.c888
-rw-r--r--converter/other/rast.c83
-rw-r--r--converter/other/rast.h15
-rw-r--r--converter/other/rasttopnm.c703
-rw-r--r--converter/other/rletopnm.c154
-rw-r--r--converter/other/sgi.h8
-rw-r--r--converter/other/sgitopnm.c747
-rw-r--r--converter/other/srf.c653
-rw-r--r--converter/other/srf.h170
-rw-r--r--converter/other/srftopam.c226
-rw-r--r--converter/other/sunicontopnm.c172
-rw-r--r--converter/other/svgtopam.c18
-rw-r--r--converter/other/tiff.c6
-rw-r--r--converter/other/tifftopnm.c273
-rw-r--r--converter/other/winicon.h82
-rw-r--r--converter/other/winicontopam.c1286
-rw-r--r--converter/other/xwdtopnm.c6
-rw-r--r--converter/other/yuy2topam.c266
-rw-r--r--converter/pbm/Makefile17
-rw-r--r--converter/pbm/atktopbm.c425
-rw-r--r--converter/pbm/brushtopbm.c174
-rw-r--r--converter/pbm/cistopbm.c180
-rw-r--r--converter/pbm/cmuwm.h17
-rw-r--r--converter/pbm/cmuwmtopbm.c21
-rw-r--r--converter/pbm/escp2topbm.c384
-rw-r--r--converter/pbm/g3topbm.c93
-rw-r--r--converter/pbm/icontopbm.c159
-rw-r--r--converter/pbm/macp.h18
-rw-r--r--converter/pbm/macptopbm.c426
-rw-r--r--converter/pbm/pbmto10x.c117
-rw-r--r--converter/pbm/pbmtoascii.c324
-rw-r--r--converter/pbm/pbmtoatk.c69
-rw-r--r--converter/pbm/pbmtocis.c170
-rw-r--r--converter/pbm/pbmtocmuwm.c8
-rw-r--r--converter/pbm/pbmtoepsi.c128
-rw-r--r--converter/pbm/pbmtoepson.c61
-rw-r--r--converter/pbm/pbmtoescp2.c260
-rw-r--r--converter/pbm/pbmtog3.c8
-rw-r--r--converter/pbm/pbmtog3.test23
-rw-r--r--converter/pbm/pbmtogem.c252
-rw-r--r--converter/pbm/pbmtoibm23xx.c2
-rw-r--r--converter/pbm/pbmtolj.c6
-rw-r--r--converter/pbm/pbmtomacp.c655
-rw-r--r--converter/pbm/pbmtomatrixorbital.c35
-rw-r--r--converter/pbm/pbmtomgr.c6
-rw-r--r--converter/pbm/pbmtonokia.c17
-rw-r--r--converter/pbm/pbmtopi3.c164
-rw-r--r--converter/pbm/pbmtopk.c30
-rw-r--r--converter/pbm/pbmtoppa/Makefile17
-rw-r--r--converter/pbm/pbmtopsg3.c4
-rw-r--r--converter/pbm/pbmtoptx.c135
-rw-r--r--converter/pbm/pbmtosunicon.c (renamed from converter/pbm/pbmtoicon.c)87
-rw-r--r--converter/pbm/pbmtoxbm.c65
-rw-r--r--converter/pbm/pbmtoybm.c143
-rw-r--r--converter/pbm/pbmtozinc.c242
-rw-r--r--converter/pbm/pi3topbm.c224
-rw-r--r--converter/pbm/thinkjettopbm.l15
-rw-r--r--converter/pbm/xbmtopbm.c10
-rw-r--r--converter/pbm/ybmtopbm.c161
-rw-r--r--converter/pgm/Makefile4
-rw-r--r--converter/pgm/asciitopgm.c272
-rw-r--r--converter/pgm/fstopgm.c235
-rw-r--r--converter/pgm/pgmtolispm.c224
-rw-r--r--converter/pgm/pgmtosbig.c130
-rw-r--r--converter/pgm/pgmtost4.c104
-rw-r--r--converter/pgm/rawtopgm.c100
-rw-r--r--converter/pgm/sbigtopgm.c465
-rw-r--r--converter/pgm/st4topgm.c260
-rw-r--r--converter/ppm/411toppm.c219
-rw-r--r--converter/ppm/Makefile7
-rw-r--r--converter/ppm/eyuvtoppm.c291
-rwxr-xr-xconverter/ppm/hpcdtoppm/pcdovtoppm2
-rw-r--r--converter/ppm/ilbm.h6
-rw-r--r--converter/ppm/ilbmtoppm.c558
-rw-r--r--converter/ppm/pc1toppm.c2
-rw-r--r--converter/ppm/pcxtoppm.c16
-rw-r--r--converter/ppm/picttoppm.c671
-rw-r--r--converter/ppm/ppmtoacad.c5
-rw-r--r--converter/ppm/ppmtoapplevol.c95
-rw-r--r--converter/ppm/ppmtoarbtxt.c1530
-rw-r--r--converter/ppm/ppmtoascii.c236
-rw-r--r--converter/ppm/ppmtobmp.c89
-rw-r--r--converter/ppm/ppmtogif.c50
-rw-r--r--converter/ppm/ppmtoicr.c532
-rw-r--r--converter/ppm/ppmtoilbm.c1810
-rw-r--r--converter/ppm/ppmtompeg/BUGS2
-rw-r--r--converter/ppm/ppmtompeg/CHANGES2
-rw-r--r--converter/ppm/ppmtompeg/Makefile69
-rw-r--r--converter/ppm/ppmtompeg/bframe.c2
-rw-r--r--converter/ppm/ppmtompeg/bitio.c4
-rw-r--r--converter/ppm/ppmtompeg/bsearch.c3
-rw-r--r--converter/ppm/ppmtompeg/combine.c10
-rw-r--r--converter/ppm/ppmtompeg/docs/template.param2
-rw-r--r--converter/ppm/ppmtompeg/examples/template.param2
-rw-r--r--converter/ppm/ppmtompeg/frame.c2
-rw-r--r--converter/ppm/ppmtompeg/gethostname.c1
-rw-r--r--converter/ppm/ppmtompeg/gethostname_win32.c2
-rw-r--r--converter/ppm/ppmtompeg/headers/all.h3
-rw-r--r--converter/ppm/ppmtompeg/headers/ansi.h76
-rw-r--r--converter/ppm/ppmtompeg/headers/bitio.h1
-rw-r--r--converter/ppm/ppmtompeg/headers/dct.h8
-rw-r--r--converter/ppm/ppmtompeg/headers/frame.h21
-rw-r--r--converter/ppm/ppmtompeg/headers/frames.h2
-rw-r--r--converter/ppm/ppmtompeg/headers/frametype.h2
-rw-r--r--converter/ppm/ppmtompeg/headers/general.h10
-rw-r--r--converter/ppm/ppmtompeg/headers/huff.h5
-rw-r--r--converter/ppm/ppmtompeg/headers/jpeg.h4
-rw-r--r--converter/ppm/ppmtompeg/headers/mheaders.h23
-rw-r--r--converter/ppm/ppmtompeg/headers/motion_search.h3
-rw-r--r--converter/ppm/ppmtompeg/headers/mpeg.h5
-rw-r--r--converter/ppm/ppmtompeg/headers/mproto.h47
-rw-r--r--converter/ppm/ppmtompeg/headers/mtypes.h15
-rw-r--r--converter/ppm/ppmtompeg/headers/opts.h15
-rw-r--r--converter/ppm/ppmtompeg/headers/parallel.h1
-rw-r--r--converter/ppm/ppmtompeg/headers/param.h18
-rw-r--r--converter/ppm/ppmtompeg/headers/prototypes.h35
-rw-r--r--converter/ppm/ppmtompeg/headers/rate.h20
-rw-r--r--converter/ppm/ppmtompeg/headers/specifics.h9
-rw-r--r--converter/ppm/ppmtompeg/huff.c3
-rw-r--r--converter/ppm/ppmtompeg/input.c14
-rw-r--r--converter/ppm/ppmtompeg/jpeg.c24
-rw-r--r--converter/ppm/ppmtompeg/jrevdct.c9
-rw-r--r--converter/ppm/ppmtompeg/mfwddct.c180
-rw-r--r--converter/ppm/ppmtompeg/mpeg.c25
-rw-r--r--converter/ppm/ppmtompeg/opts.c6
-rw-r--r--converter/ppm/ppmtompeg/parallel.c86
-rw-r--r--converter/ppm/ppmtompeg/param.c67
-rw-r--r--converter/ppm/ppmtompeg/ppmtompeg.c28
-rw-r--r--converter/ppm/ppmtompeg/psearch.c2
-rw-r--r--converter/ppm/ppmtompeg/psocket.c44
-rw-r--r--converter/ppm/ppmtompeg/rate.c20
-rw-r--r--converter/ppm/ppmtompeg/readframe.c83
-rw-r--r--converter/ppm/ppmtompeg/rgbtoycc.c6
-rw-r--r--converter/ppm/ppmtompeg/specifics.c24
-rw-r--r--converter/ppm/ppmtopcx.c20
-rw-r--r--converter/ppm/ppmtopict.c860
-rw-r--r--converter/ppm/ppmtopjxl.c190
-rw-r--r--converter/ppm/ppmtorgb3.c8
-rw-r--r--converter/ppm/ppmtospu.c593
-rw-r--r--converter/ppm/ppmtoterm.c238
-rw-r--r--converter/ppm/ppmtowinicon.c20
-rw-r--r--converter/ppm/ppmtoxpm.c20
-rw-r--r--converter/ppm/ppmtoyuv.c141
-rw-r--r--converter/ppm/ppmtoyuvsplit.c12
-rw-r--r--converter/ppm/sldtoppm.c151
-rw-r--r--converter/ppm/sputoppm.c135
-rw-r--r--converter/ppm/tgatoppm.c2
-rw-r--r--converter/ppm/winicontoppm.c822
-rw-r--r--converter/ppm/xim.h2
-rw-r--r--converter/ppm/ximtoppm.c4
-rw-r--r--converter/ppm/xpmtoppm.c1149
-rw-r--r--converter/ppm/yuvsplittoppm.c16
-rw-r--r--converter/ppm/yuvtoppm.c255
-rw-r--r--doc/CONTRIBUTORS37
-rw-r--r--doc/COPYRIGHT.PATENT93
-rw-r--r--doc/HISTORY1472
-rw-r--r--doc/INSTALL160
-rw-r--r--doc/Netpbm.programming193
-rw-r--r--doc/TESTS362
-rw-r--r--doc/USERDOC8
-rw-r--r--doc/patent_summary100
-rw-r--r--editor/Makefile22
-rw-r--r--editor/pambackground.c4
-rw-r--r--editor/pamcomp.c511
-rw-r--r--editor/pamcut.c32
-rw-r--r--editor/pamcut.test10
-rw-r--r--editor/pamdice.c16
-rw-r--r--editor/pamdither.c320
-rw-r--r--editor/pamditherbw.c13
-rw-r--r--editor/pamedge.c5
-rw-r--r--editor/pamenlarge.c146
-rw-r--r--editor/pamenlarge.test8
-rw-r--r--editor/pamflip.test12
-rw-r--r--editor/pamflip/Makefile36
-rw-r--r--editor/pamflip/config.h7
-rw-r--r--editor/pamflip/flip.h16
-rw-r--r--editor/pamflip/pamflip.c (renamed from editor/pamflip.c)217
-rw-r--r--editor/pamflip/pamflip_sse.c429
-rw-r--r--editor/pamflip/pamflip_sse.h12
-rw-r--r--editor/pamfunc.c128
-rw-r--r--editor/pammasksharpen.c10
-rw-r--r--editor/pamperspective.c5
-rw-r--r--editor/pamrecolor.c528
-rw-r--r--editor/pamrubber.c1475
-rw-r--r--editor/pamscale.c552
-rw-r--r--editor/pamsistoaglyph.c19
-rw-r--r--editor/pamstretch.c2
-rw-r--r--editor/pamthreshold.c20
-rw-r--r--editor/pamundice.c35
-rw-r--r--editor/pamwipeout.c229
-rw-r--r--editor/pbmclean.c835
-rw-r--r--editor/pbmpscale.c550
-rw-r--r--editor/pgmdeshadow.c6
-rw-r--r--editor/pgmmedian.c4
-rw-r--r--editor/pnmcat.c20
-rw-r--r--editor/pnmcomp.c460
-rw-r--r--editor/pnmconvol.c3694
-rw-r--r--editor/pnmcrop.c6
-rwxr-xr-xeditor/pnmflip25
-rw-r--r--editor/pnmgamma.c14
-rw-r--r--editor/pnmhisteq.c253
-rw-r--r--editor/pnminvert.c21
-rw-r--r--editor/pnminvert.test15
-rwxr-xr-xeditor/pnmmargin54
-rw-r--r--editor/pnmmontage.c434
-rw-r--r--editor/pnmnlfilt.c2
-rw-r--r--editor/pnmnorm.c389
-rw-r--r--editor/pnmpad.c266
-rw-r--r--editor/pnmpaste.c58
-rwxr-xr-xeditor/pnmquant64
-rwxr-xr-xeditor/pnmquantall209
-rw-r--r--editor/pnmremap.c77
-rw-r--r--editor/pnmrotate.c2
-rw-r--r--editor/pnmscalefixed.c39
-rw-r--r--editor/pnmshear.c37
-rw-r--r--editor/pnmsmooth.c162
-rw-r--r--editor/pnmstitch.c20
-rw-r--r--editor/pnmtile.c2
-rw-r--r--editor/ppmbrighten.c4
-rw-r--r--editor/ppmchange.c2
-rw-r--r--editor/ppmcolormask.c12
-rw-r--r--editor/ppmdist.c29
-rw-r--r--editor/ppmdither.c704
-rw-r--r--editor/ppmdraw.c73
-rwxr-xr-xeditor/ppmfade32
-rw-r--r--editor/ppmmix.c179
-rwxr-xr-xeditor/ppmquant28
-rwxr-xr-xeditor/ppmquantall96
-rw-r--r--editor/ppmquantall.csh57
-rwxr-xr-xeditor/ppmshadow35
-rw-r--r--editor/specialty/Makefile2
-rw-r--r--editor/specialty/pamdeinterlace.c2
-rw-r--r--editor/specialty/pammixinterlace.c2
-rw-r--r--editor/specialty/pampaintspill.c500
-rw-r--r--editor/specialty/pgmabel.c6
-rw-r--r--editor/specialty/pgmmorphconv.c520
-rw-r--r--editor/specialty/pnmindex.c81
-rw-r--r--editor/specialty/pnmmercator.c430
-rw-r--r--editor/specialty/ppm3d.c4
-rw-r--r--editor/specialty/ppmglobe.c4
-rw-r--r--editor/specialty/ppmntsc.c311
-rw-r--r--editor/specialty/ppmrelief.c112
-rw-r--r--generator/Makefile7
-rw-r--r--generator/pamcrater.c514
-rw-r--r--generator/pamgauss.c20
-rw-r--r--generator/pamgradient.c8
-rw-r--r--generator/pamseq.c7
-rw-r--r--generator/pamshadedrelief.c250
-rw-r--r--generator/pamstereogram.c947
-rw-r--r--generator/pbmmake.c25
-rw-r--r--generator/pbmmake.test9
-rw-r--r--generator/pbmpage.c251
-rw-r--r--generator/pbmtext.c9
-rw-r--r--generator/pbmtextps.c43
-rwxr-xr-xgenerator/pgmcrater94
-rw-r--r--generator/pgmcrater.c382
-rw-r--r--generator/pgmkernel.c288
-rw-r--r--generator/pgmmake.c20
-rw-r--r--generator/pgmnoise.c105
-rw-r--r--generator/pgmramp.c62
-rw-r--r--generator/ppmcie.c351
-rw-r--r--generator/ppmcolors.c9
-rw-r--r--generator/ppmforge.c417
-rw-r--r--generator/ppmmake.c20
-rw-r--r--generator/ppmpat.c42
-rwxr-xr-xgenerator/ppmrainbow26
-rw-r--r--generator/ppmrough.c530
-rw-r--r--icon/Makefile31
-rw-r--r--icon/netpbm.icobin0 -> 3774 bytes
-rw-r--r--icon/netpbm.ppm12
-rw-r--r--lib/Makefile55
-rw-r--r--lib/colorname.c36
-rw-r--r--lib/libpam.c553
-rw-r--r--lib/libpamcolor.c17
-rw-r--r--lib/libpamd.c1524
-rw-r--r--lib/libpammap.c60
-rw-r--r--lib/libpamn.c46
-rw-r--r--lib/libpamread.c79
-rw-r--r--lib/libpamwrite.c26
-rw-r--r--lib/libpbm1.c79
-rw-r--r--lib/libpbm2.c95
-rw-r--r--lib/libpbm3.c166
-rw-r--r--lib/libpbmfont.c476
-rw-r--r--lib/libpbmvms.c734
-rw-r--r--lib/libpgm1.c104
-rw-r--r--lib/libpgm2.c18
-rw-r--r--lib/libpm.c477
-rw-r--r--lib/libpnm1.c36
-rw-r--r--lib/libpnm2.c4
-rw-r--r--lib/libpnm3.c2
-rw-r--r--lib/libppm1.c193
-rw-r--r--lib/libppm2.c17
-rw-r--r--lib/libppmcmap.c95
-rw-r--r--lib/libppmcolor.c134
-rw-r--r--lib/libppmd.c228
-rw-r--r--lib/libppmfloyd.c2
-rw-r--r--lib/libppmfuzzy.c4
-rw-r--r--lib/libsystem.c64
-rw-r--r--lib/libsystem_dummy.c2
-rw-r--r--lib/lum.h2
-rw-r--r--lib/pam.h56
-rw-r--r--lib/pamdraw.h317
-rw-r--r--lib/pammap.h6
-rw-r--r--lib/path.c4
-rw-r--r--lib/pbm.h22
-rw-r--r--lib/pbmfont.h2
-rw-r--r--lib/pm.h47
-rw-r--r--lib/pmfileio.c239
-rw-r--r--lib/pnm.h5
-rw-r--r--lib/ppm.h9
-rw-r--r--lib/ppmdfont.c2
-rw-r--r--lib/ppmdraw.h2
-rw-r--r--lib/rgb.txt10
-rw-r--r--lib/standardppmdfont.c2
-rw-r--r--lib/util/Makefile26
-rw-r--r--lib/util/bitio.c (renamed from lib/bitio.c)0
-rw-r--r--lib/util/bitio.h (renamed from lib/bitio.h)0
-rw-r--r--lib/util/filename.c2
-rw-r--r--lib/util/floatcode.h4
-rw-r--r--lib/util/intcode.h2
-rw-r--r--lib/util/io.c92
-rw-r--r--lib/util/io.h11
-rw-r--r--lib/util/mallocvar.c185
-rw-r--r--lib/util/mallocvar.h23
-rw-r--r--lib/util/matrix.c223
-rw-r--r--lib/util/matrix.h11
-rw-r--r--lib/util/nsleep.c8
-rw-r--r--lib/util/nsleep.h2
-rw-r--r--lib/util/nstring.c358
-rw-r--r--lib/util/nstring.h62
-rw-r--r--lib/util/pm_c_util.h67
-rw-r--r--lib/util/runlength.c374
-rw-r--r--lib/util/runlength.h51
-rw-r--r--lib/util/shhopt.c135
-rw-r--r--lib/util/shhopt.h53
-rw-r--r--lib/util/token.c80
-rw-r--r--lib/util/token.h11
-rw-r--r--lib/util/vasprintf.c35
-rw-r--r--lib/util/wordaccess.h51
-rw-r--r--lib/util/wordaccess_be_aligned.h35
-rw-r--r--lib/util/wordaccess_be_unaligned.h (renamed from lib/util/wordaccess_gcc3_be.h)7
-rw-r--r--lib/util/wordaccess_generic.h6
-rw-r--r--netpbm.c42
-rw-r--r--other/Makefile17
-rw-r--r--other/pamarith.c97
-rw-r--r--other/pambayer.c90
-rw-r--r--other/pamchannel.c25
-rw-r--r--other/pamdepth.c13
-rw-r--r--other/pamexec.c192
-rw-r--r--other/pamfix.c291
-rwxr-xr-xother/pamfixtrunc50
-rw-r--r--other/pamfixtrunc.c176
-rw-r--r--other/pamlookup.c186
-rw-r--r--other/pampick.c10
-rw-r--r--other/pamsplit.c23
-rw-r--r--other/pamstack.c11
-rw-r--r--other/pamsummcol.c2
-rw-r--r--other/pamunlookup.c250
-rw-r--r--other/pamvalidate.c86
-rw-r--r--other/pamx/Makefile48
-rw-r--r--other/pamx/pamx.c7
-rw-r--r--other/pamx/send.c6
-rw-r--r--other/pamx/window.c27
-rw-r--r--other/pnmcolormap.c8
-rw-r--r--other/ppmdcfont.c20
-rw-r--r--other/ppmsvgalib.c16
-rw-r--r--pm_config.in.h167
-rw-r--r--test/411toppm.ok1
-rwxr-xr-xtest/411toppm.test13
-rw-r--r--test/BLOCK4
-rwxr-xr-xtest/Execute-Tests308
-rw-r--r--test/Makefile28
-rw-r--r--test/Test-Order155
-rw-r--r--test/all-in-place.ok329
-rwxr-xr-xtest/all-in-place.test429
-rw-r--r--test/atari-roundtrip.ok3
-rwxr-xr-xtest/atari-roundtrip.test32
-rw-r--r--test/atk-roundtrip.ok1
-rwxr-xr-xtest/atk-roundtrip.test7
-rw-r--r--test/avs-roundtrip.ok1
-rwxr-xr-xtest/avs-roundtrip.test8
-rw-r--r--test/bmp-roundtrip.ok2
-rwxr-xr-xtest/bmp-roundtrip.test7
-rw-r--r--test/cis-roundtrip.ok2
-rwxr-xr-xtest/cis-roundtrip.test14
-rw-r--r--test/cmuw-roundtrip.ok1
-rwxr-xr-xtest/cmuw-roundtrip.test7
-rw-r--r--test/cut-paste-roundtrip.ok10
-rwxr-xr-xtest/cut-paste-roundtrip.test52
-rw-r--r--test/eyuvtoppm.ok1
-rwxr-xr-xtest/eyuvtoppm.test8
-rw-r--r--test/facesaver-roundtrip.ok1
-rwxr-xr-xtest/facesaver-roundtrip.test9
-rw-r--r--test/fits-roundtrip.ok1
-rwxr-xr-xtest/fits-roundtrip.test7
-rw-r--r--test/g3-roundtrip.ok3
-rwxr-xr-xtest/g3-roundtrip.test16
-rw-r--r--test/gem-roundtrip.ok1
-rwxr-xr-xtest/gem-roundtrip.test7
-rw-r--r--test/gif-quant-roundtrip.ok1
-rwxr-xr-xtest/gif-quant-roundtrip.test18
-rw-r--r--test/gif-roundtrip.ok11
-rwxr-xr-xtest/gif-roundtrip.test66
-rw-r--r--test/hdiff-roundtrip.ok1
-rwxr-xr-xtest/hdiff-roundtrip.test7
-rw-r--r--test/ilbm-roundtrip.ok10
-rwxr-xr-xtest/ilbm-roundtrip.test28
-rw-r--r--test/jbig-roundtrip.ok2
-rwxr-xr-xtest/jbig-roundtrip.test11
-rw-r--r--test/leaf-roundtrip.ok1
-rwxr-xr-xtest/leaf-roundtrip.test7
-rw-r--r--test/legacy-names.ok37
-rwxr-xr-xtest/legacy-names.test132
-rw-r--r--test/lookup-roundtrip.ok1
-rwxr-xr-xtest/lookup-roundtrip.test12
-rw-r--r--test/macp-roundtrip.ok5
-rwxr-xr-xtest/macp-roundtrip.test31
-rw-r--r--test/mda-roundtrip.ok2
-rwxr-xr-xtest/mda-roundtrip.test13
-rw-r--r--test/mgr-roundtrip.ok1
-rwxr-xr-xtest/mgr-roundtrip.test7
-rw-r--r--test/mrf-roundtrip.ok1
-rwxr-xr-xtest/mrf-roundtrip.test7
-rw-r--r--test/pad-crop-roundtrip.ok2
-rwxr-xr-xtest/pad-crop-roundtrip.test9
-rw-r--r--test/pambackground.ok3
-rwxr-xr-xtest/pambackground.test34
-rw-r--r--test/pamchannel.ok3
-rwxr-xr-xtest/pamchannel.test32
-rw-r--r--test/pamcrater.ok6
-rwxr-xr-xtest/pamcrater.test53
-rw-r--r--test/pamcut.ok4
-rwxr-xr-xtest/pamcut.test18
-rw-r--r--test/pamdepth-roundtrip.ok8
-rwxr-xr-xtest/pamdepth-roundtrip.test13
-rw-r--r--test/pamdice-roundtrip.ok1
-rwxr-xr-xtest/pamdice-roundtrip.test12
-rw-r--r--test/pamditherbw.ok4
-rwxr-xr-xtest/pamditherbw.test32
-rw-r--r--test/pamedge.ok77
-rwxr-xr-xtest/pamedge.test8
-rw-r--r--test/pamenlarge.ok4
-rwxr-xr-xtest/pamenlarge.test14
-rw-r--r--test/pamfile.ok5
-rwxr-xr-xtest/pamfile.test9
-rw-r--r--test/pamflip-roundtrip.ok12
-rwxr-xr-xtest/pamflip-roundtrip.test35
-rw-r--r--test/pamflip1.ok5
-rwxr-xr-xtest/pamflip1.test15
-rw-r--r--test/pamflip2.ok3
-rwxr-xr-xtest/pamflip2.test17
-rw-r--r--test/pamseq.ok1
-rwxr-xr-xtest/pamseq.test6
-rw-r--r--test/pamslice-roundtrip.ok4
-rwxr-xr-xtest/pamslice-roundtrip.test75
-rw-r--r--test/pamsumm.ok8
-rwxr-xr-xtest/pamsumm.test14
-rw-r--r--test/pamtopam.ok16
-rwxr-xr-xtest/pamtopam.test10
-rw-r--r--test/pbm-misc-converters.ok27
-rwxr-xr-xtest/pbm-misc-converters.test40
-rw-r--r--test/pbmclean.ok73
-rwxr-xr-xtest/pbmclean.test19
-rw-r--r--test/pbmmake.ok43
-rwxr-xr-xtest/pbmmake.test18
-rw-r--r--test/pbmminkowski.ok23
-rwxr-xr-xtest/pbmminkowski.test11
-rw-r--r--test/pbmpage.ok3
-rwxr-xr-xtest/pbmpage.test8
-rw-r--r--test/pbmpscale.ok50
-rwxr-xr-xtest/pbmpscale.test12
-rw-r--r--test/pbmtext.ok21
-rwxr-xr-xtest/pbmtext.test12
-rw-r--r--test/pbmtog3.ok11
-rwxr-xr-xtest/pbmtog3.test29
-rw-r--r--test/pbmupc.ok1
-rwxr-xr-xtest/pbmupc.test9
-rw-r--r--test/pcx-roundtrip.ok5
-rwxr-xr-xtest/pcx-roundtrip.test45
-rw-r--r--test/pfm-roundtrip.ok1
-rwxr-xr-xtest/pfm-roundtrip.test8
-rw-r--r--test/pgmbentley.ok1
-rwxr-xr-xtest/pgmbentley.test7
-rw-r--r--test/pgmhist.ok14
-rwxr-xr-xtest/pgmhist.test12
-rw-r--r--test/pgmmake.ok2
-rwxr-xr-xtest/pgmmake.test7
-rw-r--r--test/pgmnoise.ok1
-rwxr-xr-xtest/pgmnoise.test23
-rw-r--r--test/pgmramp.ok40
-rwxr-xr-xtest/pgmramp.test15
-rw-r--r--test/pgmtopgm.ok1
-rwxr-xr-xtest/pgmtopgm.test6
-rw-r--r--test/pgmtoppm.ok3
-rwxr-xr-xtest/pgmtoppm.test22
-rw-r--r--test/pi3-roundtrip.ok2
-rwxr-xr-xtest/pi3-roundtrip.test16
-rw-r--r--test/pict-roundtrip.ok1
-rwxr-xr-xtest/pict-roundtrip.test8
-rw-r--r--test/png-roundtrip.ok36
-rwxr-xr-xtest/png-roundtrip.test51
-rw-r--r--test/png-roundtrip2.ok4
-rwxr-xr-xtest/png-roundtrip2.test22
-rw-r--r--test/pnm-pam-roundtrip.ok2
-rwxr-xr-xtest/pnm-pam-roundtrip.test7
-rw-r--r--test/pnm-plain-roundtrip.ok2
-rwxr-xr-xtest/pnm-plain-roundtrip.test9
-rw-r--r--test/pnmcat.ok4
-rwxr-xr-xtest/pnmcat.test10
-rw-r--r--test/pnminvert-roundtrip.ok2
-rwxr-xr-xtest/pnminvert-roundtrip.test7
-rw-r--r--test/pnminvert.ok7
-rwxr-xr-xtest/pnminvert.test26
-rw-r--r--test/pnmpsnr.ok2
-rwxr-xr-xtest/pnmpsnr.test20
-rw-r--r--test/pnmremap1.ok1
-rwxr-xr-xtest/pnmremap1.test26
-rw-r--r--test/pnmremap2.ok3
-rwxr-xr-xtest/pnmremap2.test25
-rw-r--r--test/pnmshear.ok1
-rwxr-xr-xtest/pnmshear.test22
-rw-r--r--test/pnmtile.ok2
-rwxr-xr-xtest/pnmtile.test22
-rw-r--r--test/pnmtopnm-plain.ok48
-rwxr-xr-xtest/pnmtopnm-plain.test10
-rw-r--r--test/ppmbrighten.ok3
-rwxr-xr-xtest/ppmbrighten.test8
-rw-r--r--test/ppmchange-roundtrip.ok2
-rwxr-xr-xtest/ppmchange-roundtrip.test12
-rw-r--r--test/ppmchange.ok4
-rwxr-xr-xtest/ppmchange.test59
-rw-r--r--test/ppmcie.ok2
-rwxr-xr-xtest/ppmcie.test51
-rw-r--r--test/ppmdfont.ok2
-rwxr-xr-xtest/ppmdfont.test22
-rw-r--r--test/ppmdim.ok13
-rwxr-xr-xtest/ppmdim.test25
-rw-r--r--test/ppmdither.ok3
-rwxr-xr-xtest/ppmdither.test8
-rw-r--r--test/ppmforge.ok1
-rwxr-xr-xtest/ppmforge.test20
-rw-r--r--test/ppmgauss.ok81
-rwxr-xr-xtest/ppmgauss.test12
-rw-r--r--test/ppmhist.ok11
-rwxr-xr-xtest/ppmhist.test7
-rw-r--r--test/ppmmake.ok2
-rwxr-xr-xtest/ppmmake.test7
-rw-r--r--test/ppmmix.ok17
-rwxr-xr-xtest/ppmmix.test33
-rw-r--r--test/ppmpat.ok6
-rwxr-xr-xtest/ppmpat.test35
-rw-r--r--test/ppmrelief.ok3
-rwxr-xr-xtest/ppmrelief.test36
-rw-r--r--test/ppmrough.ok1
-rwxr-xr-xtest/ppmrough.test17
-rw-r--r--test/ppmtoarbtxt-roundtrip.ok2
-rwxr-xr-xtest/ppmtoarbtxt-roundtrip.test34
-rw-r--r--test/ppmtopgm.ok1
-rwxr-xr-xtest/ppmtopgm.test7
-rw-r--r--test/ppmtoppm.ok1
-rwxr-xr-xtest/ppmtoppm.test6
-rw-r--r--test/ppmwheel.ok2
-rwxr-xr-xtest/ppmwheel.test110
-rw-r--r--test/ps-alt-roundtrip.ok3
-rwxr-xr-xtest/ps-alt-roundtrip.test60
-rw-r--r--test/ps-roundtrip.ok15
-rwxr-xr-xtest/ps-roundtrip.test86
-rw-r--r--test/rgb3-roundtrip.ok6
-rwxr-xr-xtest/rgb3-roundtrip.test46
-rw-r--r--test/sbig-roundtrip.ok1
-rwxr-xr-xtest/sbig-roundtrip.test9
-rw-r--r--test/sgi-roundtrip.ok8
-rwxr-xr-xtest/sgi-roundtrip.test43
-rw-r--r--test/st4-roundtrip.ok1
-rwxr-xr-xtest/st4-roundtrip.test14
-rw-r--r--test/sunicon-roundtrip.ok1
-rwxr-xr-xtest/sunicon-roundtrip.test8
-rw-r--r--test/sunrast-roundtrip.ok1
-rwxr-xr-xtest/sunrast-roundtrip.test7
-rw-r--r--test/symmetry.ok12
-rwxr-xr-xtest/symmetry.test97
-rw-r--r--test/targa-roundtrip.ok3
-rwxr-xr-xtest/targa-roundtrip.test18
-rw-r--r--test/testgrid.pbm (renamed from testgrid.pbm)0
-rw-r--r--test/testimg.ppm (renamed from testimg.ppm)0
-rw-r--r--test/testrandom.c133
-rw-r--r--test/tiff-roundtrip.ok4
-rwxr-xr-xtest/tiff-roundtrip.test23
-rw-r--r--test/utahrle-roundtrip.ok2
-rwxr-xr-xtest/utahrle-roundtrip.test12
-rw-r--r--test/wbmp-roundtrip.ok1
-rwxr-xr-xtest/wbmp-roundtrip.test7
-rw-r--r--test/winicon-roundtrip.ok2
-rwxr-xr-xtest/winicon-roundtrip.test13
-rw-r--r--test/xbm-roundtrip.ok2
-rwxr-xr-xtest/xbm-roundtrip.test7
-rw-r--r--test/xpm-roundtrip.ok1
-rwxr-xr-xtest/xpm-roundtrip.test9
-rw-r--r--test/xv-roundtrip.ok1
-rwxr-xr-xtest/xv-roundtrip.test7
-rw-r--r--test/xwd-roundtrip.ok3
-rwxr-xr-xtest/xwd-roundtrip.test16
-rw-r--r--test/yuv-roundtrip.ok1
-rwxr-xr-xtest/yuv-roundtrip.test10
-rw-r--r--urt/Makefile10
-rw-r--r--urt/cmd_name.c3
-rw-r--r--urt/rle.h6
-rw-r--r--urt/rle_addhist.c12
-rw-r--r--urt/rle_config.h31
-rw-r--r--urt/rle_error.c1
-rw-r--r--urt/rle_getcom.c1
-rw-r--r--urt/rle_getrow.c690
-rw-r--r--urt/rle_getskip.c2
-rw-r--r--urt/rle_global.c2
-rw-r--r--urt/rle_hdr.c5
-rw-r--r--urt/rle_open_f.c17
-rw-r--r--urt/rle_put.h1
-rw-r--r--urt/rle_putcom.c4
-rw-r--r--urt/rle_putrow.c3
-rw-r--r--urt/rle_row_alc.c2
-rw-r--r--urt/scanargs.c38
-rw-r--r--version.mk4
-rwxr-xr-xvms/Add_List.com59
-rwxr-xr-xvms/Make_PBMplus.com519
-rwxr-xr-xvms/Make_PBMplusShr.com280
-rw-r--r--vms/NetPBM.TeX12115
-rw-r--r--vms/PBMplus.hlp6814
-rw-r--r--vms/RGB.txt738
-rwxr-xr-xvms/SetUp.com37
-rwxr-xr-xvms/stamp-date.com25
818 files changed, 72953 insertions, 59747 deletions
diff --git a/GNUmakefile b/GNUmakefile
index 17199c77..ca2181b3 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -1,15 +1,25 @@
 # Makefile for Netpbm
- 
+
 # Configuration should normally be done in the included file config.mk.
 
 # Targets in this file:
 #
 #   nonmerge:     Build everything, in the source directory.
-#   merge:        Build everything as merged executables, in the source dir
-#   package:      Make a package of Netpbm files ready to install
-#   
+#   merge:        Build everything as merged executables, in the source dir.
+#   package:      Make a package of Netpbm files ready to install.
+#
 #   deb:          Make a .deb file in the current dir.
 #
+#   check-tree:     Conduct tests on Netpbm files in the source dir. 
+#   check-package:  Conduct tests on packaged Netpbm files.
+#   check-install:  Conduct tests on installed Netpbm files.
+#   check:          Default check.  Synonym for check-package.
+#
+#   clean:        Delete target executables and intermediate objects.
+#   distclean:    Delete configuration files in addition to the above.
+#
+#   tags:         Generate/update an Emacs tags file, named TAGS.
+#   
 #   The default target is either "merge" or "nonmerge", as determined by
 #   the DEFAULT_TARGET variable set by config.mk.
 
@@ -64,7 +74,7 @@ include $(BUILDDIR)/config.mk
 
 PROG_SUBDIRS = converter analyzer editor generator other
 PRODUCT_SUBDIRS = lib $(PROG_SUBDIRS)
-SUPPORT_SUBDIRS = urt buildtools
+SUPPORT_SUBDIRS = urt icon buildtools test
 
 SUBDIRS = $(PRODUCT_SUBDIRS) $(SUPPORT_SUBDIRS)
 
@@ -100,8 +110,15 @@ nonmerge: $(PRODUCT_SUBDIRS:%=%/all)
 # make works for 'make all' in the top directory, but it may still fail
 # for the aforementioned reason for other invocations.
 
-$(SUBDIRS:%=%/all): pm_config.h inttypes_netpbm.h version.h
+$(SUBDIRS:%=%/all) lib/util/all: pm_config.h inttypes_netpbm.h version.h
 $(PROG_SUBDIRS:%=%/all): lib/all $(SUPPORT_SUBDIRS:%=%/all)
+lib/all: lib/util/all
+
+.PHONY: lib/util/all
+lib/util/all:
+	mkdir -p lib/util
+	$(MAKE) -C lib/util -f $(SRCDIR)/lib/util/Makefile \
+	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) all
 
 OMIT_CONFIG_RULE = 1
 OMIT_VERSION_H_RULE = 1
@@ -125,6 +142,14 @@ $(TYPEGEN) $(ENDIANGEN): $(BUILDDIR)/buildtools
 inttypes_netpbm.h: $(TYPEGEN)
 	$(TYPEGEN) >$@
 
+
+# testrandom is a utility program used by the make file below.
+TESTRANDOM = $(BUILDDIR)/test/testrandom
+
+$(TESTRANDOM): $(BUILDDIR)/test
+	$(MAKE) -C $(dir $@) -f $(SRCDIR)/test/Makefile \
+	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
+
 # We run a couple of programs on the build machine in computing the
 # contents of pm_config.h.  We need to give the user a way not to do
 # that or to override the results, because it doesn't work if he's
@@ -152,6 +177,17 @@ ifeq ($(HAVE_INT64),Y)
 else
 	echo "#define HAVE_INT64 0" >>$@
 endif	
+ifeq ($(WANT_SSE),Y)
+	echo "#define WANT_SSE 1" >>$@
+else
+	echo "#define WANT_SSE 0" >>$@
+endif	
+ifeq ($(DONT_HAVE_PROCESS_MGMT),Y)
+	echo "#define HAVE_FORK 0" >>$@
+else
+	echo "#define HAVE_FORK 1" >>$@
+endif
+	echo '#define RGB_DB_PATH "$(RGB_DB_PATH)"' >>$@
 	echo '/* pm_config.h.in FOLLOWS ... */' >>$@
 	cat $(SRCDIR)/pm_config.in.h >>$@
 	$(ENDIANGEN) >>$@
@@ -213,7 +249,7 @@ init_package:
 	@if [ -d $(PKGDIR) ]; then \
 	  echo "Directory $(PKGDIR) already exists.  Please specify a "; \
 	  echo "directory that can be created fresh, like this: "; \
-	  echo "  make package PKGDIR=/tmp/newnetpbm "; \
+	  echo "  make package pkgdir=/tmp/newnetpbm "; \
 	  false; \
 	  fi
 	mkdir $(PKGDIR)
@@ -241,10 +277,10 @@ endif
 
 .PHONY: install-merge install-nonmerge
 install-merge: install.merge install.lib install.data \
-	install.manweb install.man
+	install.manwebmain install.manweb install.man
 
 install-nonmerge: install.bin install.lib install.data \
-	install.manweb install.man
+	install.manwebmain install.manweb install.man
 
 .PHONY: merge
 merge: lib/all netpbm
@@ -265,23 +301,35 @@ endif
 ifneq ($(LINUXSVGALIB),NONE)
   MERGELIBS += $(LINUXSVGALIB)
 endif
-ifneq ($(X11LIB),NONE)
-  MERGELIBS += $(X11LIB)
+
+ifneq ($(shell pkg-config --modversion libpng$(PNGVER)),)
+  PNGLD = $(shell pkg-config --libs libpng$(PNGVER))
+else
+  ifneq ($(shell libpng$(PNGVER)-config --version),)
+    PNGLD = $(shell libpng$(PNGVER)-config --ldflags)
+  else
+    PNGLD = $(shell $(LIBOPT) $(LIBOPTR) $(PNGLIB) $(ZLIB))
+  endif
 endif
 
-ifeq ($(shell libpng$(PNGVER)-config --version),)
-  PNGLD = $(shell $(LIBOPT) $(LIBOPTR) $(PNGLIB) $(ZLIB))
+ifneq ($(shell pkg-config --modversion libxml-2.0),)
+  XML2LD=$(shell pkg-config --libs libxml-2.0)
 else
-  PNGLD = $(shell libpng$(PNGVER)-config --ldflags)
+  ifneq ($(shell xml2-config --version),)
+    XML2LD=$(shell xml2-config --libs)
+  else
+    XML2LD=
+  endif
 endif
 
-ifeq ($(shell xml2-config --version),)
-  XML2LD=
+ifneq ($(shell pkg-config x11 --libs),)
+  X11LD = $(shell pkg-config x11 --libs)
 else
-  XML2LD=$(shell xml2-config --libs)
+  X11LD = $(shell $(LIBOPT) $(LIBOPTR) $(X11LIB))
 endif
 
 
+
 # If URTLIB is BUNDLED_URTLIB, then we're responsible for building it, which
 # means it needs to be a dependency:
 ifeq ($(URTLIB),$(BUNDLED_URTLIB))
@@ -323,7 +371,7 @@ netpbm:%:%.o $(OBJECT_DEP) $(NETPBMLIB) $(URTLIBDEP) $(LIBOPT)
 # Note that LDFLAGS might contain -L options, so order is important.
 	$(LD) -o $@ $< $(OBJECT_LIST) \
           $(LDFLAGS) $(shell $(LIBOPT) $(NETPBMLIB) $(MERGELIBS)) \
-	  $(PNGLD) $(XML2LD) $(MATHLIB) $(NETWORKLD) $(LADD)
+	  $(PNGLD) $(XML2LD) $(X11LD) $(MATHLIB) $(NETWORKLD) $(LADD)
 
 netpbm.o: mergetrylist
 
@@ -345,8 +393,8 @@ else
 install.lib:
 endif
 
-.PHONY: install.manweb
-install.manweb: $(PKGDIR)/$(PKGMANDIR)/web/netpbm.url $(PKGDIR)/bin/doc.url
+.PHONY: install.manwebmain
+install.manwebmain: $(PKGDIR)/$(PKGMANDIR)/web/netpbm.url $(PKGDIR)/bin/doc.url
 
 $(PKGDIR)/$(PKGMANDIR)/web/netpbm.url: $(PKGDIR)/$(PKGMANDIR)/web
 	echo "$(NETPBM_DOCURL)" > $@
@@ -373,19 +421,19 @@ lib/install.hdr:
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/lib/Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@)
 
-ifeq ($(STATICLIB_TOO),y)
-BUILD_STATIC = y
+ifeq ($(STATICLIB_TOO),Y)
+BUILD_STATIC = Y
 else
   ifeq ($(NETPBMLIBTYPE),unixstatic)
-    BUILD_STATIC = y
+    BUILD_STATIC = Y
   else
-    BUILD_STATIC = n
+    BUILD_STATIC = N
   endif
 endif
 
 .PHONY: install.staticlib
 install.staticlib: 
-ifeq ($(BUILD_STATIC),y)
+ifeq ($(BUILD_STATIC),Y)
 	$(MAKE) -C lib -f $(SRCDIR)/lib/Makefile \
 	SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) install.staticlib 
 endif
@@ -401,12 +449,118 @@ install.sharedlibstub:
 deb:
 	buildtools/debian/mkdeb --buildtools=buildtools --pkgdir=$(PKGDIR)
 
+
+.PHONY: check
+.PHONY: check-tree
+.PHONY: check-package
+.PHONY: check-install
+
+# Test files in source tree.
+# This does not work when Netpbm is compiled in a separate build dir.
+
+check-tree : SRCBINDIRS :=./analyzer \
+./converter/other \
+./converter/other/cameratopam \
+./converter/other/fiasco \
+./converter/other/jbig \
+./converter/other/jpeg2000 \
+./converter/other/pamtosvg \
+./converter/other/pnmtopalm \
+./converter/pbm \
+./converter/pbm/pbmtoppa \
+./converter/pgm \
+./converter/ppm \
+./converter/ppm/hpcdtoppm \
+./converter/ppm/ppmtompeg \
+./converter/ppm \
+./editor \
+./editor/pamflip \
+./editor/specialty \
+./generator \
+./other \
+./other/pamx \
+./
+
+# Create colon-separated PATH list from the above.
+# Use realpath function (appears in GNU Make v.3.81) if available.
+
+# Kludge to test whether realpath is available:
+ifeq ($(realpath $(CURDIR)/.),$(CURDIR))
+  check-tree : RBINDIRS :=\
+    $(foreach dir,$(SRCBINDIRS),$(realpath $(BUILDDIR)/$(dir)))
+else
+  check-tree : RBINDIRS :=$(foreach dir,$(SRCBINDIRS),$(BUILDDIR)/$(dir))  
+endif
+
+# Kludge to express characters given special meanings by GNU Make.
+# See GNU Make texinfo manual "Function Call Syntax".
+empty :=
+space := $(empty) $(empty)
+colon :=:
+
+check-tree : PBM_TEST_PATH := $(subst $(space),$(colon),$(RBINDIRS))
+check-tree : PBM_LIBRARY_PATH ?= $(BUILDDIR)/lib
+check-tree : RGBDEF ?= $(SRCDIR)/lib/rgb.txt
+
+
+# Create RESULTDIR.
+# If it already exists, rename and covert to an archive directory.
+# Use numbered backup.
+# TODO: Renaming fails with old versions of mv which do not support -T.  
+
+resultdir-backup: FORCE
+	if [ -d $(RESULTDIR) ]; \
+	   then mv -T --backup=numbered $(RESULTDIR) $(RESULTDIR).bak; \
+	fi; \
+	mkdir -p $(RESULTDIR); \
+
+
+check-tree: $(TESTRANDOM) resultdir-backup
+	cd $(RESULTDIR); \
+	  CHECK_TYPE=tree \
+	  PBM_TEST_PATH=$(PBM_TEST_PATH) BUILDDIR=$(BUILDDIR) \
+	  LD_LIBRARY_PATH=$(PBM_LIBRARY_PATH):${LD_LIBRARY_PATH} \
+	  RGBDEF=$(RGBDEF) \
+	  $(SRCDIR)/test/Execute-Tests 2>&1
+
+# Execute-Tests needs to know BUILDDIR in order to locate testrandom.
+# This applies to all check varieties.
+
+# Check after the packaging stage
+# This works on typical Linux systems.
+# This is the default check.
+
+check-package : PBM_TEST_PATH := $(PKGDIR)/bin
+check-package : PBM_LIBRARY_PATH := $(PKGDIR)/lib
+check-package : RGBDEF ?= $(PKGDIR)/misc/rgb.txt
+check: check-package
+
+check-package: $(TESTRANDOM) resultdir-backup
+	cd $(RESULTDIR); \
+	  CHECK_TYPE=package \
+	  PBM_TEST_PATH=$(PBM_TEST_PATH) BUILDDIR=$(BUILDDIR) \
+	  LD_LIBRARY_PATH=$(PBM_LIBRARY_PATH):${LD_LIBRARY_PATH} \
+	  RGBDEF=$(RGBDEF) \
+	  $(SRCDIR)/test/Execute-Tests 2>&1
+
+
+# Check after install
+check-install: $(TESTRANDOM) resultdir-backup
+	cd $(RESULTDIR); \
+	  CHECK_TYPE=install \
+	  BUILDDIR=$(BUILDDIR) \
+	  RGBDEF=$(RGBDEF) \
+	  $(SRCDIR)/test/Execute-Tests 2>&1
+
+
+
 clean: localclean
 
 .PHONY: localclean
 localclean:
 	rm -f netpbm build_started build_complete
 	rm -f pm_config.h inttypes_netpbm.h version.h
+	rm -f *.deb
 
 # Note that removing config.mk must be the last thing we do,
 # because no other makes will work after that is done.
@@ -417,7 +571,7 @@ localdistclean: localclean
 	-rm -f TAGS
 	-rm -f config.mk
 
-# 'tags' generates/updates an Emacs tags file, anmed TAGS, in the current
+# 'tags' generates/updates an Emacs tags file, named TAGS, in the current
 # directory.  Use with Emacs command 'find-tag'.
 
 .PHONY: tags
diff --git a/analyzer/Makefile b/analyzer/Makefile
index ed180917..c96a6d0a 100644
--- a/analyzer/Makefile
+++ b/analyzer/Makefile
@@ -14,7 +14,7 @@ include $(BUILDDIR)/config.mk
 # This package is so big, it's useful even when some parts won't 
 # build.
 
-PORTBINARIES = pamfile pamslice pamsumm pamtilt \
+PORTBINARIES = pamfile pammosaicknit pamslice pamsumm pamtilt \
                pbmminkowski pgmhist pnmhistmap ppmhist pgmminkowski
 MATHBINARIES = pamsharpmap pamsharpness pgmtexture pnmpsnr 
 
diff --git a/analyzer/pamfile.c b/analyzer/pamfile.c
index 4fa81330..a7c3c5ac 100644
--- a/analyzer/pamfile.c
+++ b/analyzer/pamfile.c
@@ -16,7 +16,7 @@
 #include "shhopt.h"
 #include "pam.h"
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -30,14 +30,14 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to as as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -54,11 +54,13 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */
     opt.allowNegNum   = FALSE; /* We have no parms that are negative numbers */
     
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others */
 
     cmdlineP->inputFilespec = (const char **)&argv[1];
     cmdlineP->inputFileCount = argc - 1;
+
+    free(option_def);
 }
 
 
@@ -157,7 +159,7 @@ doOneImage(const char * const name,
         if (wantComments)
             dumpComments(comments);
     }
-    strfree(comments);
+    pm_strfree(comments);
 
     pnm_checkpam(&pam, PM_CHECK_BASIC, &checkRetval);
     if (allimages) {
@@ -171,7 +173,11 @@ doOneImage(const char * const name,
         
         pnm_freepamrow(tuplerow);
         
-        pnm_nextimage(fileP, eofP);
+        {
+            int eof;
+            pnm_nextimage(fileP, &eof);
+            *eofP = eof;
+        }
     }
 }
 
@@ -217,11 +223,11 @@ describeOneFile(const char * const name,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
diff --git a/analyzer/pammosaicknit.c b/analyzer/pammosaicknit.c
new file mode 100644
index 00000000..f0e4c7f9
--- /dev/null
+++ b/analyzer/pammosaicknit.c
@@ -0,0 +1,253 @@
+/* ----------------------------------------------------------------------
+ *
+ * Validate a mosaic knitting pattern
+ *
+ * By Scott Pakin <scott+pbm@pakin.org>
+ *
+ * ----------------------------------------------------------------------
+ *
+ * Copyright (C) 2010 Scott Pakin <scott+pbm@pakin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "mallocvar.h"
+#include "pam.h"
+
+int const max_skips = 3;      /* Maximum number of consecutive skips */
+
+/* Each pixel can be either black or white and can be valid or invalid. */
+typedef enum {
+    MK_VALID_BLACK,
+    MK_VALID_WHITE,
+    MK_INVALID_BLACK,
+    MK_INVALID_WHITE
+} mkPixelType;
+
+
+static void
+initializeInvalidColors(struct pam * const pamP,
+                        tuple *      const blackColorP,
+                        tuple *      const whiteColorP) {
+    tuple invalidBlack;
+    tuple invalidWhite;
+
+    invalidBlack = pnm_allocpamtuple(pamP);
+    invalidBlack[PAM_RED_PLANE] = pamP->maxval/3;
+    invalidBlack[PAM_GRN_PLANE] = 0;
+    invalidBlack[PAM_BLU_PLANE] = 0;
+    *blackColorP = invalidBlack;
+
+    invalidWhite = pnm_allocpamtuple(pamP);
+    invalidWhite[PAM_RED_PLANE] = pamP->maxval;
+    invalidWhite[PAM_GRN_PLANE] = pamP->maxval*3/4;
+    invalidWhite[PAM_BLU_PLANE] = pamP->maxval*3/4;
+    *whiteColorP = invalidWhite;
+}
+
+
+
+static void
+pamRowToKnitRow(struct pam *  const pamP,
+                const tuple * const pamRow,
+                mkPixelType * const knitRow) {
+
+    if (pamP->depth == 3) {
+        /* Convert from RGB. */
+        unsigned int col;
+
+        for (col = 0; col < pamP->width; ++col) {
+            double luminosity;
+
+            luminosity =
+                pamRow[col][0]*pnm_lumin_factor[0] +
+                pamRow[col][1]*pnm_lumin_factor[1] +
+                pamRow[col][2]*pnm_lumin_factor[2];
+            if (luminosity < pamP->maxval/2.0)
+                knitRow[col] = MK_VALID_BLACK;
+            else
+                knitRow[col] = MK_VALID_WHITE;
+        }
+    }
+    else {
+        /* Convert from either grayscale or black and white. */
+        unsigned int col;
+
+        for (col = 0; col < pamP->width; ++col) {
+            if (pamRow[col][0]*2 < pamP->maxval)
+                knitRow[col] = MK_VALID_BLACK;
+            else
+                knitRow[col] = MK_VALID_WHITE;
+        }
+    }
+}
+
+
+
+static void
+knitRowToPamRow(struct pam *        const pamP,
+                const mkPixelType * const knitRow,
+                tuple *             const pamRow,
+                tuple               const invalidBlack,
+                tuple               const invalidWhite) {
+
+    tuple        validBlack;
+    tuple        validWhite;
+    unsigned int col;
+
+    validBlack = pnm_allocpamtuple(pamP);
+    pnm_createBlackTuple(pamP, &validBlack);
+    validWhite = pnm_allocpamtuple(pamP);
+    validWhite[PAM_RED_PLANE] = pamP->maxval;
+    validWhite[PAM_GRN_PLANE] = pamP->maxval;
+    validWhite[PAM_BLU_PLANE] = pamP->maxval;
+
+    for (col = 0; col < pamP->width; ++col) {
+        switch (knitRow[col]) {
+          case MK_VALID_BLACK:
+              pnm_assigntuple(pamP, pamRow[col], validBlack);
+              break;
+
+          case MK_VALID_WHITE:
+              pnm_assigntuple(pamP, pamRow[col], validWhite);
+              break;
+
+          case MK_INVALID_BLACK:
+              pnm_assigntuple(pamP, pamRow[col], invalidBlack);
+              break;
+
+          case MK_INVALID_WHITE:
+              pnm_assigntuple(pamP, pamRow[col], invalidWhite);
+              break;
+
+          default:
+            abort();
+            break;
+        }
+    }
+
+    pnm_freepamtuple(validBlack);
+    pnm_freepamtuple(validWhite);
+}
+
+
+
+static void
+validateMosaicPattern(struct pam * const inPamP,
+                      struct pam * const outPamP) {
+/* --------------------------------------------------------------------------
+   Validate a mosaic knitting pattern.  A valid pattern starts with a
+   "black" row on the bottom and alternates "white" and "black" rows.
+   A "black" row can contain any arrangement of black pixels but no
+   more than max_skips consecutive white pixels.  A "white" row can
+   contain any arrangement of white pixels but no more than max_skips
+   consecutive black pixels.  Columns wrap horizontally, so that
+   characteristic is taken into consideration when tallying
+   consecutive pixels.  Invalid pixels are flagged with a red hue.  It
+   is assumed that the input image is fairly small (on the order of a
+   few tens of pixels in each dimension), so red pixels should stand
+   out fairly well when the image is zoomed in.
+--------------------------------------------------------------------------*/
+
+    tuple *       inPamRow;      /* One row of data read from the input file */
+    mkPixelType * knitRow;     /* Same as inPamRow but expressed as stitches */
+    tuple *       outPamRow;  /* One row of data to write to the output file */
+    tuple         invalidBlack; /* Color representing an invalid black input */
+    tuple         invalidWhite; /* Color representing an invalid white input */
+    mkPixelType   rowColor;     /* Base color of the current row */
+    unsigned int  row;
+
+    inPamRow = pnm_allocpamrow(inPamP);
+    outPamRow = pnm_allocpamrow(outPamP);
+    MALLOCARRAY(knitRow, inPamP->width);
+    initializeInvalidColors(outPamP, &invalidBlack, &invalidWhite);
+
+    rowColor = inPamP->height % 2 == 0 ? MK_VALID_WHITE : MK_VALID_BLACK;
+    for (row = 0; row < inPamP->height; ++row) {
+        unsigned int col;
+
+        pnm_readpamrow(inPamP, inPamRow);
+        pamRowToKnitRow(inPamP, inPamRow, knitRow);
+        for (col = 0; col < inPamP->width; ++col) {
+            if (knitRow[col] != rowColor) {
+                unsigned int runLength = 0;  /* Number of consecutive skips */
+
+                for (runLength = 0;
+                     runLength < inPamP->width &&
+                         knitRow[(col+runLength)%inPamP->width] != rowColor;
+                     ++runLength)
+                    ;
+                if (runLength > max_skips) {
+                    /* We have too many skips in a row -- mark them
+                       with the "invalid" color. */
+                    unsigned int badOffset;
+                    mkPixelType  badColor;
+
+                    badColor = rowColor == MK_VALID_WHITE ?
+                        MK_INVALID_BLACK : MK_INVALID_WHITE;
+                    for (badOffset = 0; badOffset < runLength; ++badOffset) {
+                        knitRow[(col+badOffset)%inPamP->width] = badColor;
+                    }
+                }
+                col += runLength - 1;
+            }
+        }
+        knitRowToPamRow(outPamP, knitRow, outPamRow,
+                        invalidBlack, invalidWhite);
+        pnm_writepamrow(outPamP, outPamRow);
+        rowColor = rowColor == MK_VALID_BLACK ?
+            MK_VALID_WHITE : MK_VALID_BLACK;
+    }
+
+    free(knitRow);
+    pnm_freepamrow(outPamRow);
+    pnm_freepamrow(inPamRow);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+    struct pam   inPam;
+    struct pam   outPam;
+    const char * inputFilename;
+    FILE       * inFileP;
+
+    pm_proginit(&argc, argv);
+
+    inputFilename = (argc > 1) ? argv[1] : "-";
+    inFileP = pm_openr(inputFilename);
+
+    pnm_readpaminit(inFileP, &inPam, PAM_STRUCT_SIZE(tuple_type));
+
+    outPam = inPam;
+    outPam.file = stdout;
+    outPam.format = PAM_FORMAT;
+    outPam.depth = 3;
+    outPam.maxval = 255;
+    outPam.bytes_per_sample = 1;
+    strcpy(outPam.tuple_type, PAM_PPM_TUPLETYPE);
+    pnm_writepaminit(&outPam);
+
+    validateMosaicPattern(&inPam, &outPam);
+
+    pm_closer(inFileP);
+    return 0;
+}
+
diff --git a/analyzer/pamsharpmap.c b/analyzer/pamsharpmap.c
index ddeefe14..2f31835b 100644
--- a/analyzer/pamsharpmap.c
+++ b/analyzer/pamsharpmap.c
@@ -49,7 +49,7 @@ parseCommandLine ( int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc(100*sizeof(optEntry));
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -65,7 +65,7 @@ parseCommandLine ( int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!contextSpec)
diff --git a/analyzer/pamsharpness.c b/analyzer/pamsharpness.c
index 8e2d9a3f..5943416f 100644
--- a/analyzer/pamsharpness.c
+++ b/analyzer/pamsharpness.c
@@ -21,8 +21,9 @@
 #include <math.h>
 
 #include "pm_c_util.h"
-#include "pam.h"
+#include "mallocvar.h"
 #include "shhopt.h"
+#include "pam.h"
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -35,8 +36,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine ( int argc, char ** argv,
-                   struct cmdlineInfo *cmdlineP ) {
+parseCommandLine(int argc, 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.  
@@ -47,8 +48,8 @@ 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));
-        /* Instructions to optParseOptions3 on how to parse our options.
+    optEntry * option_def;
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -56,6 +57,8 @@ parseCommandLine ( int argc, char ** argv,
 
     unsigned int contextSpec;
 
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0, "context",       OPT_UINT,   &cmdlineP->context,       
             &contextSpec,         0 );
@@ -64,7 +67,7 @@ parseCommandLine ( int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!contextSpec)
@@ -80,6 +83,8 @@ parseCommandLine ( int argc, char ** argv,
         cmdlineP->inputFilespec = "-";
     else
         cmdlineP->inputFilespec = argv[1];
+
+    free(option_def);
 }
 
 
@@ -89,13 +94,13 @@ computeSharpness(struct pam * const inpamP,
                  tuplen **    const tuplenarray,
                  double *     const sharpnessP) {
 
-    int row;
+    unsigned int row;
     double totsharp;
     
     totsharp = 0.0;
 
     for (row = 1; row < inpamP->height-1; ++row) {
-        int col;
+        unsigned int col;
         for (col = 1; col < inpamP->width-1; ++col) {
             int dy;
             for (dy = -1; dy <= 1; ++dy) {
@@ -150,5 +155,6 @@ main(int argc, char **argv) {
 
 	pnm_freepamarrayn(tuplenarray, &inpam);
     pm_close(ifP);
+
 	return 0;
 }
diff --git a/analyzer/pamslice.c b/analyzer/pamslice.c
index ab372c8a..db0ab9c0 100644
--- a/analyzer/pamslice.c
+++ b/analyzer/pamslice.c
@@ -68,7 +68,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (rowSpec && colSpec)
diff --git a/analyzer/pamsumm.c b/analyzer/pamsumm.c
index c9b8a7bf..c427fa7d 100644
--- a/analyzer/pamsumm.c
+++ b/analyzer/pamsumm.c
@@ -58,7 +58,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (sumSpec + minSpec + maxSpec > 1)
@@ -73,7 +73,7 @@ parseCommandLine(int argc, char ** const argv,
     } else if (maxSpec) {
         cmdlineP->function = FN_MAX;
     } else 
-        pm_error("You must specify one of -sum, -min, or -max");
+        pm_error("You must specify one of -sum, -min, -max, or -mean");
         
     if (argc-1 > 1)
         pm_error("Too many arguments (%d).  File spec is the only argument.",
diff --git a/analyzer/pamtilt.c b/analyzer/pamtilt.c
index d70f491b..302c6247 100644
--- a/analyzer/pamtilt.c
+++ b/analyzer/pamtilt.c
@@ -47,7 +47,7 @@ abandon(void) {
 
 
 static void
-parseCommandLine(int argc, char *argv[],
+parseCommandLine(int argc, const char ** const argv,
                  struct cmdlineInfo * const cmdlineP) {
 
     static optEntry option_def[50];
@@ -76,7 +76,7 @@ parseCommandLine(int argc, char *argv[],
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;          /* no short options used */
     opt.allowNegNum = FALSE;            /* don't allow negative values */
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
 
     if (cmdlineP->hstep < 1)
         pm_error("-hstep must be at least 1 column.");
@@ -129,11 +129,13 @@ static void
 load(const struct pam * const pamP,
      unsigned int       const hstep,
      sample ***         const pixelsP,
-     unsigned int *     const hsamplesP) {
+     unsigned int *     const hsampleCtP) {
 /*----------------------------------------------------------------------------
-  read file into memory, returning array of rows
+  Read part of the raster - to wit, every hstep'th column of every row.
+  Return that sampling of the raster as *pixelsP, and the resulting
+  array width (pixels in each row) as *hsampleCtP.
 -----------------------------------------------------------------------------*/
-    unsigned int const hsamples = 1 + (pamP->width - 1) / hstep;
+    unsigned int const hsampleCt = 1 + (pamP->width - 1) / hstep;
         /* use only this many cols */
 
     unsigned int row;
@@ -143,9 +145,6 @@ load(const struct pam * const pamP,
     tuplerow = pnm_allocpamrow(pamP);
     
     MALLOCARRAY(pixels, pamP->height);
-    if (pixels == NULL)
-        pm_error("Unable to allocate array of %u pixel rows",
-                 pamP->height);
 
     if (pixels == NULL)
         pm_error("Unable to allocate array of %u rows", pamP->height);
@@ -156,19 +155,19 @@ load(const struct pam * const pamP,
 
         pnm_readpamrow(pamP, tuplerow);
 
-        MALLOCARRAY(pixels[row], hsamples);
+        MALLOCARRAY(pixels[row], hsampleCt);
         if (pixels[row] == NULL)
             pm_error("Unable to allocate %u-sample array for Row %u",
-                     hsamples, row);
+                     hsampleCt, row);
 
         /* save every hstep'th column */
-        for (i = col = 0; i < hsamples; ++i, col += hstep)
+        for (i = col = 0; i < hsampleCt; ++i, col += hstep)
             pixels[row][i] = tuplerow[col][0];
     }
 
     pnm_freepamrow(tuplerow);
 
-    *hsamplesP = hsamples;
+    *hsampleCtP = hsampleCt;
     *pixelsP   = pixels;
 }
 
@@ -192,7 +191,7 @@ static void
 replacePixelValuesWithScaledDiffs(
     const struct pam * const pamP,
     sample **          const pixels,
-    unsigned int       const hsamples,
+    unsigned int       const hsampleCt,
     unsigned int       const dstep) {
 /*----------------------------------------------------------------------------
   Replace pixel values with scaled differences used for all
@@ -204,7 +203,7 @@ replacePixelValuesWithScaledDiffs(
 
     for (row = dstep; row < pamP->height; ++row) {
         unsigned int col;
-        for (col = 0; col < hsamples; ++col) {
+        for (col = 0; col < hsampleCt; ++col) {
             int const d = pixels[row - dstep][col] - pixels[row][col];
             unsigned int const absd = d < 0 ? -d : d;
             pixels[row - dstep][col] = m * absd;  /* scale the difference */
@@ -214,9 +213,46 @@ replacePixelValuesWithScaledDiffs(
 
 
 
+static unsigned long
+totalBrightness(sample **    const pixels,
+                unsigned int const hsampleCt,
+                unsigned int const startRow,
+                float        const dy) {
+/*----------------------------------------------------------------------------
+   Total brightness of samples in the line that goes from the left edge
+   of Row 'startRow' of 'pixels' down to the right at 'dy' rows per column.
+   
+   Note that 'dy' can be negative.
+
+   Assume that whatever 'dy' is, the sloping line thus described remains
+   within 'pixels'.
+-----------------------------------------------------------------------------*/
+    unsigned long total;
+    unsigned int x;
+    float y;
+        /* The vertical location in the 'pixels' region of the currently
+           analyzed pixel.  0 is the top of the image.  Note that while
+           'pixels' is discrete pixels, this is a location in continuous
+           space.  We consider the brightness of the pixel at 1.5 to be
+           the mean of the brightness of the pixel in row 1 and the pixel
+           in row 2.
+        */
+    for (x = 0, y = startRow + 0.5, total = 0;
+         x < hsampleCt;
+         ++x, y += dy) {
+
+        assert(y >= 0.0);  /* Because of entry conditions */
+
+        total += pixels[(unsigned)y][x];
+    }
+    return total;
+}
+
+
+
 static void
 scoreAngleRegion(sample **    const pixels,
-                 unsigned int const hsamples,
+                 unsigned int const hsampleCt,
                  unsigned int const startRow,
                  unsigned int const endRow,
                  unsigned int const vstep,
@@ -234,7 +270,7 @@ scoreAngleRegion(sample **    const pixels,
    Instead of a tilt angle, we have 'dy', the slope (downward) of the lines
    in our assumed tilt.
 -----------------------------------------------------------------------------*/
-    double const tscale  = 1.0 / hsamples;
+    double const tscale  = 1.0 / hsampleCt;
 
     unsigned int row;
     double sum;
@@ -245,17 +281,10 @@ scoreAngleRegion(sample **    const pixels,
         /* Number of lines that went into 'total' */
 
     for (row = startRow, sum = 0.0, n = 0; row < endRow; row += vstep) {
-        float o;
-        long t;     /* total brightness of the samples in the line */
-        double dt;  /* mean brightness of the samples in the line */
+        double const dt =
+            tscale * totalBrightness(pixels, hsampleCt, row, dy);
+            /* mean brightness of the samples in the line */
 
-        unsigned int i;
-
-        for (i = 0, t = 0, o = 0.5;
-             i < hsamples;
-             t += pixels[(int)(row + o)][i], ++i, o += dy) {
-        }
-        dt = tscale * t;
         sum += dt * dt;
         n += 1;
     }
@@ -270,20 +299,21 @@ scoreAngle(const struct pam * const pamP,
            sample **          const pixels,
            unsigned int       const hstep,
            unsigned int       const vstep,
-           unsigned int       const hsamples,
+           unsigned int       const hsampleCt,
            float              const angle,
            float *            const scoreP) {
 /*----------------------------------------------------------------------------
-  Calculate the score for assuming the image described by *pamP and
-  'pixels' is tilted down by angle 'angle' (in degrees) from what it
-  should be.  I.e. the angle from the top edge of the paper to the
-  lines of text in the image is 'angle'.
+  Calculate the score for assuming the image described by *pamP and 'pixels'
+  is tilted down by angle 'angle' (in degrees) from what it should be.
+  I.e. the angle from the top edge of the paper to the lines of text in the
+  image is 'angle'.  Positive means the text slopes down; negative means it
+  slopes up.
 
   The score is a measure of how bright the lines of the image are if
   we assume this angle.  If the angle is right, there should be lots
   of lines that are all white, because they are between lines of text.
   If the angle is wrong, many lines will cross over lines of text and
-  thus be less that all white.  A higher score indicates 'angle' is more
+  thus be less than all white.  A higher score indicates 'angle' is more
   likely to be the angle at which the image is tilted.
 
   If 'angle' is so great that not a single line goes all the way across the
@@ -291,7 +321,7 @@ scoreAngle(const struct pam * const pamP,
   every other case, it is nonnegative.
   
   'pixels' is NOT all the pixels in the image; it is just a sampling.
-  In each row, it contains only 'hsamples' pixels, sampled from the
+  In each row, it contains only 'hsampleCt' pixels, sampled from the
   image at intervals of 'hstep' pixels.  E.g if the image is 1000
   pixels wide, pixels might be only 10 pixels wide, containing columns
   0, 100, 200, etc. of the image.
@@ -300,10 +330,10 @@ scoreAngle(const struct pam * const pamP,
 -----------------------------------------------------------------------------*/
     float  const radians = (float)angle/360 * 2 * M_PI;
     float  const dy      = hstep * tan(radians);
-        /* How much a line sinks due to the tilt when we move one sample
+        /* How much a line sinks because of the tilt when we move one sample
            ('hstep' columns of the image) to the right.
         */
-    if (fabs(dy * hsamples) > pamP->height) {
+    if (fabs(dy * hsampleCt) > pamP->height) {
         /* This is so tilted that not a single line of the image fits
            entirely on the page, so we can't do the measurement.
         */
@@ -316,17 +346,17 @@ scoreAngle(const struct pam * const pamP,
                off the page and we can't follow them.
             */
             startRow = 0;
-            endRow = pamP->height - dy * hsamples;
+            endRow = (unsigned int)(pamP->height - dy * hsampleCt + 0.5);
         } else {
             /* Lines of image rise as you go right, so the topmost lines go
                off the page and we can't follow them.
             */
-            startRow = 0 - dy * hsamples;
+            startRow = (unsigned int)(0 - dy * hsampleCt + 0.5);
             endRow = pamP->height;
         }
         assert(endRow > startRow);  /* because of 'if (fabs(dy ...' */
 
-        scoreAngleRegion(pixels, hsamples, startRow, endRow, vstep, dy,
+        scoreAngleRegion(pixels, hsampleCt, startRow, endRow, vstep, dy,
                          scoreP);
     }
 }
@@ -339,7 +369,7 @@ getBestAngleLocal(
     sample **          const pixels,
     unsigned int       const hstep,
     unsigned int       const vstep,
-    unsigned int       const hsamples,
+    unsigned int       const hsampleCt,
     float              const minangle,
     float              const maxangle,
     float              const incr,
@@ -347,7 +377,10 @@ getBestAngleLocal(
     float *            const bestAngleP,
     float *            const qualityP) {
 /*----------------------------------------------------------------------------
-  find angle of highest score within a range
+  Find the angle that gives the highest score within the range
+  [minangle, maxangle].  Those are in degrees, with positive meaning
+  the subject is rotated clockwise on the page (so a horizontal line in
+  the subject would slope down across the page).
 -----------------------------------------------------------------------------*/
     int const nsamples = ((maxangle - minangle) / incr + 1.5);
 
@@ -369,7 +402,7 @@ getBestAngleLocal(
     total = 0;
     for (i = 0; i < nsamples; i++) {
         angle = minangle + i * incr;
-        scoreAngle(pamP, pixels, hstep, vstep, hsamples, angle, &score);
+        scoreAngle(pamP, pixels, hstep, vstep, hsampleCt, angle, &score);
         results[i] = score;
         if (score > bestscore ||
             (score == bestscore && fabs(angle) < fabs(bestangle))) {
@@ -402,26 +435,36 @@ getBestAngleLocal(
 
 
 static void
-readRelevantPixels(const char *   const inputFilename,
-                   unsigned int   const hstepReq,
-                   unsigned int   const vstepReq,
-                   unsigned int * const hstepP,
-                   unsigned int * const vstepP,
-                   sample ***     const pixelsP,
-                   struct pam *   const pamP,
-                   unsigned int * const hsamplesP) {
+readSampledPixels(const char *   const inputFilename,
+                  unsigned int   const hstepReq,
+                  unsigned int   const vstepReq,
+                  unsigned int * const hstepP,
+                  unsigned int * const vstepP,
+                  sample ***     const pixelsP,
+                  struct pam *   const pamP,
+                  unsigned int * const hsampleCtP) {
 /*----------------------------------------------------------------------------
-  load the image, saving only the pixels we might actually inspect
+  Read the image.
+
+  Sample the image and return the selected pixels as *pixelsP, which is
+  an array the same height as the image, but with only certain columns
+  sampled.  Return as *hsampleCtP the number of columns sampled (i.e. the
+  width of the *pixelsP array).
+
+  Return the horizontal step size used in the sampling as *hstepP.
+  Return the appropriate vertical step size as *vstepP.
 -----------------------------------------------------------------------------*/
     FILE * ifP;
     unsigned int hstep;
     unsigned int vstep;
 
     ifP = pm_openr(inputFilename);
+
     pnm_readpaminit(ifP, pamP, PAM_STRUCT_SIZE(tuple_type));
+
     computeSteps(pamP, hstepReq, vstepReq, &hstep, &vstep);
 
-    load(pamP, hstep, pixelsP, hsamplesP);
+    load(pamP, hstep, pixelsP, hsampleCtP);
 
     *hstepP = hstep;
     *vstepP = vstep;
@@ -436,7 +479,7 @@ getAngle(const struct pam * const pamP,
          sample **          const pixels,
          unsigned int       const hstep,
          unsigned int       const vstep,
-         unsigned int       const hsamples,
+         unsigned int       const hsampleCt,
          float              const maxangle,
          float              const astep,
          float              const qmin,
@@ -448,7 +491,7 @@ getAngle(const struct pam * const pamP,
     float da;
     float lastq;        /* quality (s/n ratio) of last measurement */
     
-    getBestAngleLocal(pamP, pixels, hstep, vstep, hsamples,
+    getBestAngleLocal(pamP, pixels, hstep, vstep, hsampleCt,
                       -maxangle, maxangle, astep, verbose,
                       &a, &lastq);
 
@@ -461,14 +504,14 @@ getAngle(const struct pam * const pamP,
 
     /* make a finer search in the neighborhood */
     da = astep / 10;
-    getBestAngleLocal(pamP, pixels, hstep, vstep, hsamples,
+    getBestAngleLocal(pamP, pixels, hstep, vstep, hsampleCt,
                       a - 9 * da, a + 9 * da, da, verbose,
                       &a, &lastq);
 
     /* iterate once more unless we don't need that much accuracy */
     if (!fast) {
         da /= 10;
-        getBestAngleLocal(pamP, pixels, hstep, vstep, hsamples,
+        getBestAngleLocal(pamP, pixels, hstep, vstep, hsampleCt,
                           a - 9 * da, a + 9 * da, da, verbose,
                           &a, &lastq);
     }
@@ -478,26 +521,26 @@ getAngle(const struct pam * const pamP,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char ** argv) {
 
     struct cmdlineInfo cmdline;
     struct pam pam;
     sample ** pixels;       /* pixel data */
-    unsigned int hsamples; /* horizontal samples used */
+    unsigned int hsampleCt; /* number of horizontal samples used */
     unsigned int hstep;    /* horizontal step size */
     unsigned int vstep;    /* vertical step size */
     float angle;
 
-    pgm_init(&argc, argv);              /* initialize netpbm system */
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    readRelevantPixels(cmdline.inputFilename, cmdline.hstep, cmdline.vstep,
-                       &hstep, &vstep, &pixels, &pam, &hsamples);
+    readSampledPixels(cmdline.inputFilename, cmdline.hstep, cmdline.vstep,
+                      &hstep, &vstep, &pixels, &pam, &hsampleCt);
 
-    replacePixelValuesWithScaledDiffs(&pam, pixels, hsamples, cmdline.dstep);
+    replacePixelValuesWithScaledDiffs(&pam, pixels, hsampleCt, cmdline.dstep);
 
-    getAngle(&pam, pixels, hstep, vstep, hsamples,
+    getAngle(&pam, pixels, hstep, vstep, hsampleCt,
              cmdline.maxangle, cmdline.astep, cmdline.qmin,
              cmdline.fast, cmdline.verbose, &angle);
 
diff --git a/analyzer/pgmhist.c b/analyzer/pgmhist.c
index 4790ecba..1e779655 100644
--- a/analyzer/pgmhist.c
+++ b/analyzer/pgmhist.c
@@ -20,18 +20,23 @@
 
 
 
-struct cmdline_info {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
     const char * inputFileName;  /* Filename of input files */
+    unsigned int machine;
+    unsigned int median;
+    unsigned int quartile;
+    unsigned int decile;
+    unsigned int forensic;
 };
 
 
 
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdline_info * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
@@ -39,64 +44,120 @@ parseCommandLine(int argc, const char ** argv,
     optStruct3 opt;  /* set by OPTENT3 */
     optEntry * option_def;
     unsigned int option_def_index;
-    
+
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
 
-    OPTENTINIT;
+    OPTENT3(0,   "machine",       OPT_FLAG,  NULL,
+            &cmdlineP->machine,             0);
+    OPTENT3(0,   "median",        OPT_FLAG,  NULL,
+            &cmdlineP->median,              0);
+    OPTENT3(0,   "quartile",      OPT_FLAG,  NULL,
+            &cmdlineP->quartile,            0);
+    OPTENT3(0,   "decile",        OPT_FLAG,  NULL,
+            &cmdlineP->decile,              0);
+    OPTENT3(0,   "forensic",      OPT_FLAG,  NULL,
+            &cmdlineP->forensic,            0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (argc-1 == 0) 
+    if (cmdlineP->median + cmdlineP->quartile + cmdlineP->decile > 1)
+        pm_error("You may specify only one of -median, -quartile, "
+                 "and -decile");
+
+    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];
+
+    free(option_def);
+}
+
+
+
+static gray
+universalMaxval(gray const maxval,
+                int  const format) {
+/*----------------------------------------------------------------------------
+  A maxval that makes it impossible for a pixel to be invalid in an image that
+  states it maxval as 'maxval' and has format 'format'.
+
+  E.g. in a one-byte-per-sample image, it's not possible to read a sample
+  value greater than 255, so a maxval of 255 makes it impossible for a sample
+  to be invalid.
+
+  But: we never go above 65535, which means our maxval isn't entirely
+  universal.  If the image is plain PGM, it could contain a pixel that
+  exceeds even that.
+-----------------------------------------------------------------------------*/
+    assert(0 < maxval && maxval < 65536);
+
+    if (format == RPGM_FORMAT) {
+        /*  Raw PGM stream has either one or two bytes per pixel, depending
+            upon its stated maxval.
+        */
+        if (maxval > 255)
+            return 65535;
+        else
+            return 255;
+    } else if (format == RPBM_FORMAT) {
+        /* A Raw PBM stream has one bit per pixel, which libnetpbm renders
+           as 0 or 255 when we read it.
+        */
+        assert(maxval == 255);
+        return 255;
+    } else {
+        /* A plain PGM or PBM stream has essentially unlimited range in the
+           tokens that are supposed to be sample values.  We arbitrarily draw
+           the line at 65535.
+        */
+        return 65535;
+    }
 }
 
 
 
 static void
-buildHistogram(FILE *          const ifP,
-               unsigned int ** const histP,
-               gray *          const maxvalP) {
+buildHistogram(FILE *               const ifP,
+               int                  const format,
+               unsigned int         const cols,
+               unsigned int         const rows,
+               gray                 const mmaxval,
+               unsigned long int ** const histP) {
+/*----------------------------------------------------------------------------
+   Compute the histogram of sample values in the input stream *ifP as *histP,
+   in newly malloced storage.
 
+   Assume the image maxval is 'mmaxval'.  Assume *ifP is positioned to the
+   start of the raster.
+-----------------------------------------------------------------------------*/
     gray * grayrow;
-    int rows, cols;
-    int format;
     unsigned int row;
     unsigned int i;
-    unsigned int * hist;  /* malloc'ed array */
-    gray maxval;
-
-    pgm_readpgminit(ifP, &cols, &rows, &maxval, &format);
-
-    if (UINT_MAX / cols < rows)
-        pm_error("Too many pixels (%u x %u) in image.  "
-                 "Maximum computable is %u",
-                 cols, rows, UINT_MAX);
+    unsigned long int * hist;  /* malloc'ed array */
 
     grayrow = pgm_allocrow(cols);
 
-    MALLOCARRAY(hist, maxval + 1);
+    MALLOCARRAY(hist, mmaxval + 1);
     if (hist == NULL)
         pm_error("out of memory");
 
-    for (i = 0; i <= maxval; ++i)
+    for (i = 0; i <= mmaxval; ++i)
         hist[i] = 0;
 
     for (row = 0; row < rows; ++row) {
         unsigned int col;
 
-        pgm_readpgmrow(ifP, grayrow, cols, maxval, format);
+        pgm_readpgmrow(ifP, grayrow, cols, mmaxval, format);
 
         for (col = 0; col < cols; ++col) {
             /* Because total pixels in image is limited: */
@@ -107,28 +168,108 @@ buildHistogram(FILE *          const ifP,
     }
     pgm_freerow(grayrow);
 
-    *histP   = hist;
-    *maxvalP = maxval;
+    *histP = hist;
 }
 
 
 
 static void
-countCumulative(unsigned int    const hist[],
-                gray            const maxval,
-                unsigned int ** const rcountP) {
+findQuantiles(unsigned int      const n,
+              unsigned long int const hist[],
+              unsigned long int const totalCt,
+              gray              const mmaxval,
+              gray *            const quantile) {
+/*----------------------------------------------------------------------------
+   Find the order-n quantiles (e.g. n == 4 means quartiles) of the pixel
+   sample values, given that hist[] is the histogram of them (hist[N] is the
+   number of pixels that have sample value N).
 
-    unsigned int * rcount;
-    unsigned int cumCount;
+   'mmaxval' is the highest index in hist[] (so its size is 'mmaxval' + 1,
+   and there are no pixels greater than 'mmaxval' in the image).
+
+   We return the ith quantile as quantile[i].  For example, for quartiles,
+   quantile[3] is the least sample value for which at least 3/4 of the pixels
+   are less than or equal to it.
+
+   quantile[] must be allocated at least to size 'n'.
+
+   'n' must not be more than 100.
+-----------------------------------------------------------------------------*/
+    unsigned int quantSeq;
+        /* 0 is first quantile, 1 is second quantile, etc. */
+
+    gray sampleVal;
+        /* As we increment through all the possible sample values, this
+           is the one we're considering now.
+        */
+    unsigned int cumCt;
+        /* The number of pixels that have sample value 'sampleVal' or less. */
+
+    assert(n > 1 && n <= 100);
+
+    sampleVal = 0;    /* initial value */
+    cumCt = hist[0];  /* initial value */
+
+    for (quantSeq = 1; quantSeq <= n; ++quantSeq) {
+        unsigned long int const q = totalCt / n; 
+        unsigned long int const r = totalCt % n;  
+        unsigned long int const quantCt = q*quantSeq + (r*quantSeq + n - 1)/n;
+       /* This is how many pixels are (ignoring quantization) in the
+          quantile.  E.g. for the 3rd quartile, it is 3/4 of the pixels
+          in the image.
+
+          This is equivalent to (float) totalCt * quantSeq / n rounded
+          upwards.  We use the int version in spite of complexities
+          for preventing overflow for slight innacuracies in floating
+          point arithmetic causes problems when used as loop counter
+          and array index.
+       */
+        assert(quantCt <= totalCt);
+
+        /* at sampleVal == mmaxval, cumCt == totalCt, so because
+           quantCt <= 'totalCt', 'sampleVal' cannot go above mmaxval.
+        */
+
+        while (cumCt < quantCt) {
+            ++sampleVal;
+            cumCt += hist[sampleVal];
+        }
+
+        assert(sampleVal <= mmaxval);
+
+        /* 'sampleVal' is the lowest sample value for which at least 'quantCt'
+           pixels have that sample value or less.  'cumCt' is the number
+           of pixels that have sample value 'sampleVal' or less.
+        */
+        quantile[quantSeq-1] = sampleVal;
+    }
+}
+
+
+
+static void
+countCumulative(unsigned long int    const hist[],
+                gray                 const mmaxval,
+                unsigned long int    const totalPixelCt,
+                unsigned long int ** const rcountP) {
+/*----------------------------------------------------------------------------
+   From the histogram hist[] (hist[N] is the number of pixels of sample
+   value N), compute the cumulative distribution *rcountP ((*rcountP)[N]
+   is the number of pixels of sample value N or higher).
+
+   *rcountP is newly malloced memory.
+-----------------------------------------------------------------------------*/
+    unsigned long int * rcount;
+    unsigned long int cumCount;
     int i;
-    
-    MALLOCARRAY(rcount, maxval + 1);
+
+    MALLOCARRAY(rcount, mmaxval + 1);
     if (rcount == NULL)
         pm_error("out of memory");
 
-    for (i = maxval, cumCount = 0; i >= 0; --i) {
+    for (i = mmaxval, cumCount = 0; i >= 0; --i) {
         /* Because total pixels in image is limited: */
-        assert(UINT_MAX - hist[i] >= cumCount);
+        assert(ULONG_MAX - hist[i] >= cumCount);
 
         cumCount += hist[i];
         rcount[i] = cumCount;
@@ -140,26 +281,215 @@ countCumulative(unsigned int    const hist[],
 
 
 static void
-report(unsigned int const hist[],
-       unsigned int const rcount[],
-       gray         const maxval) {
+reportHistHumanFriendly(unsigned long int const hist[],
+                        unsigned long int const rcount[],
+                        gray              const maxval) {
 
-    unsigned int const totalPixels = rcount[0];
-    unsigned int count;
+    unsigned long int const totalPixelCt = rcount[0];
+
+    unsigned int cumCount;
     unsigned int i;
 
-    printf("value  count  b%%      w%%   \n");
+    printf("value  count  b%%     w%%   \n");
     printf("-----  -----  ------  ------\n");
 
-    count = 0;
+    for (i = 0, cumCount = 0; i <= maxval; ++i) {
+        if (hist[i] > 0) {
+            cumCount += hist[i];
+            printf(
+                "%5d  %5ld  %5.3g%%  %5.3g%%\n", i, hist[i],
+                (float) cumCount * 100.0 / totalPixelCt,
+                (float) rcount[i] * 100.0 / totalPixelCt);
+        }
+    }
+}
 
-    for (i = 0; i <= maxval; ++i) {
+
+static void
+reportHistForensicHumanFriendly(unsigned long int const hist[],
+                                unsigned long int const rcount[],
+                                gray              const maxval,
+                                gray              const mmaxval) {
+
+    unsigned long int const totalPixelCt = rcount[0];
+
+    unsigned long int cumCount;
+    unsigned int i;
+
+    printf("value  count  b%%     w%%   \n");
+    printf("-----  -----  ------  ------\n");
+
+    for (i = 0, cumCount = 0; i <= maxval; ++i) {
         if (hist[i] > 0) {
-            count += hist[i];
+            cumCount += hist[i];
             printf(
-                "%5d  %5d  %5.3g%%  %5.3g%%\n", i, hist[i],
-                (float) count * 100.0 / totalPixels, 
-                (float) rcount[i] * 100.0 / totalPixels);
+                "%5d  %5ld  %5.3g%%  %5.3g%%\n", i, hist[i],
+                (float) cumCount * 100.0 / totalPixelCt,
+                (float) rcount[i] * 100.0 / totalPixelCt);
+        }
+    }
+    if (totalPixelCt > cumCount) {
+        printf("-----  -----\n");
+
+        for (i = maxval; i <= mmaxval; ++i) {
+            if (hist[i] > 0) {
+                cumCount += hist[i];
+                printf(
+                    "%5d  %5ld  %5.3g%%  %5.3g%%\n", i, hist[i],
+                    (float) cumCount * 100.0 / totalPixelCt,
+                    (float) rcount[i] * 100.0 / totalPixelCt);
+            }
+        }
+    }
+}
+
+
+
+static void
+reportHistMachineFriendly(unsigned long int const hist[],
+                          gray              const maxval) {
+
+    unsigned int i;
+
+    for (i = 0; i <= maxval; ++i) {
+        printf("%u %lu\n", i, hist[i]);
+    }
+}
+
+
+
+static void
+reportQuantilesMachineFriendly(gray         const quantile[],
+                               unsigned int const n) {
+
+    unsigned int i;
+
+    for (i = 0; i < n; ++i)
+        printf("%u\n", quantile[i]);
+}
+
+
+
+static void
+reportMedianHumanFriendly(gray const median) {
+
+    printf("Median: %5u\n", median);
+}
+
+
+
+static void
+reportQuartilesHumanFriendly(gray const quartile[]) {
+
+    unsigned int i;
+
+    printf("Quartiles:\n");
+
+    printf("Q    Value\n");
+    printf("---- -----\n");
+
+    for (i = 1; i <= 4; ++i)
+        printf("%3u%% %5u\n", 25*i, quartile[i-1]);
+}
+
+
+
+static void
+reportDecilesHumanFriendly(gray const decile[]) {
+
+    unsigned int i;
+
+    printf("Deciles:\n");
+
+    printf("Q    Value\n");
+    printf("---  -----\n");
+
+    for (i = 1; i <= 10; ++i)
+        printf("%3u%% %5u\n", 10*i, decile[i-1]);
+}
+
+
+
+static void
+summarizeInvalidPixels(unsigned long int const hist[],
+                       unsigned long int const rcount[],
+                       gray              const mmaxval,
+                       gray              const maxval) {
+/*----------------------------------------------------------------------------
+  Print total count of valid and invalid pixels, if there are any
+  invalid ones.
+-----------------------------------------------------------------------------*/
+    unsigned long int const invalidPixelCt = 
+        mmaxval > maxval ? rcount[maxval+1] : 0;
+
+    if (invalidPixelCt > 0) {
+        unsigned long int const totalPixelCt = rcount[0];
+        unsigned long int const validPixelCt = totalPixelCt - invalidPixelCt;
+
+        printf("\n");
+        printf("** Image stream contains invalid sample values "
+               "(above maxval %u)\n", maxval);
+        printf("Valid sample values:   %lu (%5.4g%%)\n",
+               validPixelCt,   (float)validPixelCt / totalPixelCt * 100.0);
+        printf("Invalid sample values: %lu (%5.4g%%)\n",
+               invalidPixelCt, (float)invalidPixelCt / totalPixelCt * 100.0);
+    }
+}
+
+
+
+static void
+reportFromHistogram(const unsigned long int * const hist,
+                    gray                      const mmaxval,
+                    gray                      const maxval,
+                    unsigned long int         const totalPixelCt,
+                    struct CmdlineInfo        const cmdline) {
+/*----------------------------------------------------------------------------
+   Analyze histogram 'hist', which has 'mmaxval' buckets, and report
+   what we find.
+
+   'maxval' is the maxval that the image states (but note that we tolerate
+   invalid sample values greater than maxval, which could be as high as
+   'mmaxval').
+
+   'cmdline' tells what kind of reporting to do.
+-----------------------------------------------------------------------------*/
+
+    if (cmdline.median) {
+        gray median[2];
+        findQuantiles(2, hist, totalPixelCt, mmaxval, median);
+        if (cmdline.machine)
+            reportQuantilesMachineFriendly(median, 1);
+        else
+            reportMedianHumanFriendly(median[0]);
+    } else if (cmdline.quartile) {
+        gray quartile[4];
+        findQuantiles(4, hist, totalPixelCt, mmaxval, quartile);
+        if (cmdline.machine)
+            reportQuantilesMachineFriendly(quartile, 4);
+        else
+            reportQuartilesHumanFriendly(quartile);
+    } else if (cmdline.decile) {
+        gray decile[10];
+        findQuantiles(10, hist, totalPixelCt, mmaxval, decile);
+        if (cmdline.machine)
+            reportQuantilesMachineFriendly(decile, 10);
+        else
+            reportDecilesHumanFriendly(decile);
+    } else {
+        if (cmdline.machine)
+            reportHistMachineFriendly(hist, mmaxval);
+        else {
+            unsigned long int * rcount; /* malloc'ed array */
+            countCumulative(hist, mmaxval, totalPixelCt, &rcount);
+            if (cmdline.forensic)
+                reportHistForensicHumanFriendly(hist, rcount, maxval, mmaxval);
+            else
+                reportHistHumanFriendly(hist, rcount, maxval);
+
+            summarizeInvalidPixels(hist, rcount, mmaxval, maxval);
+
+            free(rcount);
         }
     }
 }
@@ -169,11 +499,19 @@ report(unsigned int const hist[],
 int
 main(int argc, const char ** argv) {
 
-    struct cmdline_info cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
+    int rows, cols;
+    int format;
     gray maxval;
-    unsigned int * rcount; /* malloc'ed array */
-    unsigned int * hist;   /* malloc'ed array */
+        /* Stated maxval of the image */
+    gray mmaxval;
+        /* Maxval we assume, which may be greater than the stated maxval
+           so that we can process invalid pixels in the image that exceed
+           the maxval.
+        */
+    unsigned long int totalPixelCt;
+    unsigned long int * hist;   /* malloc'ed array */
 
     pm_proginit(&argc, argv);
 
@@ -181,18 +519,23 @@ main(int argc, const char ** argv) {
 
     ifP = pm_openr(cmdline.inputFileName);
 
-    buildHistogram(ifP, &hist, &maxval);
+    pgm_readpgminit(ifP, &cols, &rows, &maxval, &format);
+
+    if (ULONG_MAX / cols < rows)
+        pm_error("Too many pixels (%u x %u) in image.  "
+                 "Maximum computable is %lu",
+                 cols, rows, ULONG_MAX);
+
+    totalPixelCt = cols * rows;
 
-    countCumulative(hist, maxval, &rcount);
+    mmaxval = cmdline.forensic ? universalMaxval(maxval, format) : maxval;
 
-    report(hist, rcount, maxval);
+    buildHistogram(ifP, format, cols, rows, mmaxval, &hist);
+
+    reportFromHistogram(hist, mmaxval, maxval, totalPixelCt, cmdline);
 
-    free(rcount);
     free(hist);
     pm_close(ifP);
 
     return 0;
 }
-
-
-
diff --git a/analyzer/pgmtexture.c b/analyzer/pgmtexture.c
index e9a03af5..07317336 100644
--- a/analyzer/pgmtexture.c
+++ b/analyzer/pgmtexture.c
@@ -1,4 +1,4 @@
-/* pgmtexture.c - calculate textural features on a portable graymap
+/* pgmtexture.c - calculate textural features of a PGM image
 **
 ** Author: James Darrell McCauley
 **         Texas Agricultural Experiment Station
@@ -41,13 +41,19 @@
 **
 ** 05 Oct 05 - Marc Breithecker <Marc.Breithecker@informatik.uni-erlangen.de>
 **             Fix calculation or normalizing constants for d > 1.
+** 9 Jul 11  - Francois P. S. Luus <fpsluus@gmail.com> supplied fix for sum
+**             variance calculation (use F6:savg instead of F8:sentropy in
+**             F7:svar equation).
+
+
 */
 
+#include <assert.h>
 #include <math.h>
 
 #include "pm_c_util.h"
-#include "pgm.h"
 #include "mallocvar.h"
+#include "pgm.h"
 
 #define RADIX 2.0
 #define EPSILON 0.000000001
@@ -67,32 +73,60 @@
 #define F13 "Meas of Correlation-2 "
 #define F14 "Max Correlation Coeff "
 
-#define SIGN(x,y) ((y)<0 ? -fabs(x) : fabs(x))
-#define DOT fprintf(stderr,".")
-#define SWAP(a,b) {y=(a);(a)=(b);(b)=y;}
+#define SWAP(a,b) do {float const y=(a);(a)=(b);(b)=y;} while (0)
+
+
+
+static float
+sign(float const x,
+     float const y) {
+
+    return y < 0 ? -fabs(x) : fabs(x);
+}
+
+
+
+static bool const sortit = FALSE;
 
 
-static bool sortit = FALSE;
 
 static float *
-vector (int nl, int nh)
-{
-    float *v;
+vector(unsigned int const nl,
+       unsigned int const nh) {
+
+    float * v;
+
+    assert(nh >= nl);
 
     MALLOCARRAY(v, (unsigned) (nh - nl + 1));
+
     if (v == NULL)
         pm_error("Unable to allocate memory for a vector.");
+
     return v - nl;
 }
 
 
+
 static float **
-matrix (int nrl, int nrh, int ncl, int nch)
+matrix (unsigned int const nrl,
+        unsigned int const nrh,
+        unsigned int const ncl,
+        unsigned int const nch) {
+/*----------------------------------------------------------------------------
+  Allocate a float matrix with range [nrl..nrh][ncl..nch]
+
+  We do some seedy C here, subtracting an arbitrary integer from a pointer and
+  calling the result a pointer.  It normally works because the only way we'll
+  use that pointer is by adding that same integer or something greater to it.
 
-/* Allocates a float matrix with range [nrl..nrh][ncl..nch] */
-{
-    int i;
-    float **m;
+  The point of this is not to allocate memory for matrix elements that will
+  never be referenced (row < nrl or column < ncl).
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    float ** m;
+
+    assert(nrh >= nrl);
 
     /* allocate pointers to rows */
     MALLOCARRAY(m, (unsigned) (nrh - nrl + 1));
@@ -101,92 +135,102 @@ matrix (int nrl, int nrh, int ncl, int nch)
 
     m -= ncl;
 
+    assert (nch >= ncl);
+
     /* allocate rows and set pointers to them */
-    for (i = nrl; i <= nrh; i++)
-    {
+    for (i = nrl; i <= nrh; ++i) {
         MALLOCARRAY(m[i], (unsigned) (nch - ncl + 1));
         if (m[i] == NULL)
             pm_error("Unable to allocate memory for a matrix row.");
         m[i] -= ncl;
     }
+
     /* return pointer to array of pointers to rows */
     return m;
 }
 
+
+
 static void 
-results (const char * const c, const float * const a)
-{
-    int i;
+results (const char *  const name,
+         const float * const a) {
+
+    unsigned int i;
+
+    fprintf(stdout, "%s", name);
 
-    DOT;
-    fprintf (stdout, "%s", c);
     for (i = 0; i < 4; ++i)
-        fprintf (stdout, "% 1.3e ", a[i]);
-    fprintf (stdout, "% 1.3e\n", (a[0] + a[1] + a[2] + a[3]) / 4);
+        fprintf(stdout, "% 1.3e ", a[i]);
+
+    fprintf(stdout, "% 1.3e\n", (a[0] + a[1] + a[2] + a[3]) / 4);
 }
 
+
+
 static void 
-simplesrt (int n, float arr[])
-{
-    int i, j;
+simplesrt (unsigned int  const n,
+           float *       const arr) {
+
+    unsigned int j;
     float a;
 
-    for (j = 2; j <= n; j++)
-    {
+    for (j = 2; j <= n; ++j) {
+        unsigned int i;
+
         a = arr[j];
-        i = j - 1;
-        while (i > 0 && arr[i] > a)
-        {
-            arr[i + 1] = arr[i];
-            i--;
+        i = j;
+
+        while (i > 1 && arr[i-1] > a) {
+            arr[i] = arr[i-1];
+            --i;
         }
-        arr[i + 1] = a;
+        arr[i] = a;
     }
 }
 
+
+
 static void 
-mkbalanced (float **a, int n)
-{
-    int last, j, i;
-    float s, r, g, f, c, sqrdx;
+mkbalanced (float **     const a,
+            unsigned int const n) {
+
+    float const sqrdx = SQR(RADIX);
+
+    unsigned int last, i;
+    float s, r, g, f, c;
 
-    sqrdx = RADIX * RADIX;
     last = 0;
-    while (last == 0)
-    {
+    while (last == 0) {
         last = 1;
-        for (i = 1; i <= n; i++)
-        {
+        for (i = 1; i <= n; ++i) {
+            unsigned int j;
             r = c = 0.0;
-            for (j = 1; j <= n; j++)
-                if (j != i)
-                {
+            for (j = 1; j <= n; ++j) {
+                if (j != i) {
                     c += fabs (a[j][i]);
                     r += fabs (a[i][j]);
                 }
-            if (c && r)
-            {
+            }
+            if (c && r) {
                 g = r / RADIX;
                 f = 1.0;
                 s = c + r;
-                while (c < g)
-                {
+                while (c < g) {
                     f *= RADIX;
                     c *= sqrdx;
                 }
                 g = r * RADIX;
-                while (c > g)
-                {
+                while (c > g) {
                     f /= RADIX;
                     c /= sqrdx;
                 }
-                if ((c + r) / f < 0.95 * s)
-                {
+                if ((c + r) / f < 0.95 * s) {
+                    unsigned int j;
                     last = 0;
                     g = 1.0 / f;
-                    for (j = 1; j <= n; j++)
+                    for (j = 1; j <= n; ++j)
                         a[i][j] *= g;
-                    for (j = 1; j <= n; j++)
+                    for (j = 1; j <= n; ++j)
                         a[j][i] *= f;
                 }
             }
@@ -195,43 +239,43 @@ mkbalanced (float **a, int n)
 }
 
 
+
 static void 
-reduction (float **a, int n)
-{
-    int m, j, i;
-    float y, x;
+reduction (float **     const a,
+           unsigned int const n) {
 
-    for (m = 2; m < n; m++)
-    {
+    unsigned int m;
+
+    for (m = 2; m < n; ++m) {
+        unsigned int j;
+        unsigned int i;
+        float x;
         x = 0.0;
         i = m;
-        for (j = m; j <= n; j++)
-        {
-            if (fabs (a[j][m - 1]) > fabs (x))
-            {
+        for (j = m; j <= n; ++j) {
+            if (fabs(a[j][m - 1]) > fabs(x)) {
                 x = a[j][m - 1];
                 i = j;
             }
         }
-        if (i != m)
-        {
-            for (j = m - 1; j <= n; j++)
-                SWAP (a[i][j], a[m][j])  
-                    for (j = 1; j <= n; j++)
-                        SWAP (a[j][i], a[j][m]) 
-                            a[j][i] = a[j][i];
+        if (i != m) {
+            for (j = m - 1; j <= n; ++j)
+                SWAP(a[i][j], a[m][j]);
+            for (j = 1; j <= n; j++)
+                SWAP(a[j][i], a[j][m]); 
+            a[j][i] = a[j][i];
         }
-        if (x)
-        {
-            for (i = m + 1; i <= n; i++)
-            {
-                if ((y = a[i][m - 1]))
-                {
+        if (x != 0.0) {
+            unsigned int i;
+            for (i = m + 1; i <= n; ++i) {
+                float y;
+                y = a[i][m - 1];
+                if (y) {
                     y /= x;
                     a[i][m - 1] = y;
-                    for (j = m; j <= n; j++)
+                    for (j = m; j <= n; ++j)
                         a[i][j] -= y * a[m][j];
-                    for (j = 1; j <= n; j++)
+                    for (j = 1; j <= n; ++j)
                         a[j][m] += y * a[j][i];
                 }
             }
@@ -241,128 +285,140 @@ reduction (float **a, int n)
 
 
 
+static float
+norm(float **     const a,
+     unsigned int const n) {
+
+    float anorm;
+    unsigned int i;
+
+    for (i = 2, anorm = fabs(a[1][1]); i <= n; ++i) {
+        unsigned int j;
+        for (j = (i - 1); j <= n; ++j)
+            anorm += fabs(a[i][j]);
+    }
+    return anorm;
+}
+
+
+
 static void 
-hessenberg (float **a, int n, float wr[], float wi[])
-
-{
-    int nn, m, l, k, j, its, i, mmin;
-    float z, y, x, w, v, u, t, s, r, q, p, anorm;
-
-    anorm = fabs (a[1][1]);
-    for (i = 2; i <= n; i++)
-        for (j = (i - 1); j <= n; j++)
-            anorm += fabs (a[i][j]);
-    nn = n;
-    t = 0.0;
-    while (nn >= 1)
-    {
+hessenberg(float **     const a,
+           unsigned int const n,
+           float *      const wr,
+           float *      const wi) {
+
+    float const anorm = norm(a, n);
+
+    int nn;
+    float t;
+
+    assert(n >= 1);
+
+    for (nn = n, t = 0.0; nn >= 1; ) {
+        unsigned int its;
+        int l;
         its = 0;
-        do
-        {
-            for (l = nn; l >= 2; l--)
-            {
+        do {
+            float x;
+            for (l = nn; l >= 2; --l) {
+                float s;
                 s = fabs (a[l - 1][l - 1]) + fabs (a[l][l]);
                 if (s == 0.0)
                     s = anorm;
                 if ((float) (fabs (a[l][l - 1]) + s) == s)
                     break;
             }
+            assert(nn >= 1);
             x = a[nn][nn];
-            if (l == nn)
-            {
+            if (l == nn) {
                 wr[nn] = x + t;
                 wi[nn--] = 0.0;
-            }
-            else
-            {
-                y = a[nn - 1][nn - 1];
-                w = a[nn][nn - 1] * a[nn - 1][nn];
-                if (l == (nn - 1))
-                {
-                    p = 0.5 * (y - x);
-                    q = p * p + w;
-                    z = sqrt (fabs (q));
+            } else {
+                float w, y;
+                y = a[nn - 1][nn - 1];  /* initial value */
+                w = a[nn][nn - 1] * a[nn - 1][nn];  /* initial value */
+                if (l == (nn - 1)) {
+                    float const p = 0.5 * (y - x);
+                    float const q = p * p + w;
+                    float const z = sqrt(fabs(q));
                     x += t;
-                    if (q >= 0.0)
-                    {
-                        z = p + SIGN (z, p); 
-                        wr[nn - 1] = wr[nn] = x + z;
-                        if (z)
-                            wr[nn] = x - w / z;
+                    if (q >= 0.0) {
+                        float const z2 = p + sign(z, p); 
+                        wr[nn - 1] = wr[nn] = x + z2;
+                        if (z2)
+                            wr[nn] = x - w / z2;
                         wi[nn - 1] = wi[nn] = 0.0;
-                    }
-                    else
-                    {
+                    } else {
                         wr[nn - 1] = wr[nn] = x + p;
                         wi[nn - 1] = -(wi[nn] = z);
                     }
                     nn -= 2;
-                }
-                else
-                {
+                } else {
+                    int i, k, m;
+                    float p, q, r;
                     if (its == 30)
                         pm_error("Too many iterations to required "
                                  "to find %s.  Giving up", F14);
-                    if (its == 10 || its == 20)
-                    {
+                    if (its == 10 || its == 20) {
+                        int i;
+                        float s;
                         t += x;
-                        for (i = 1; i <= nn; i++)
+                        for (i = 1; i <= nn; ++i)
                             a[i][i] -= x;
-                        s = fabs (a[nn][nn - 1]) + fabs (a[nn - 1][nn - 2]);
+                        s = fabs(a[nn][nn - 1]) + fabs(a[nn - 1][nn - 2]);
                         y = x = 0.75 * s;
                         w = -0.4375 * s * s;
                     }
                     ++its;
-                    for (m = (nn - 2); m >= l; m--)
-                    {
-                        z = a[m][m];
+                    for (m = (nn - 2); m >= l; --m) {
+                        float const z = a[m][m];
+                        float s, u, v;
                         r = x - z;
                         s = y - z;
                         p = (r * s - w) / a[m + 1][m] + a[m][m + 1];
                         q = a[m + 1][m + 1] - z - r - s;
                         r = a[m + 2][m + 1];
-                        s = fabs (p) + fabs (q) + fabs (r);
+                        s = fabs(p) + fabs(q) + fabs(r);
                         p /= s;
                         q /= s;
                         r /= s;
                         if (m == l)
                             break;
-                        u = fabs (a[m][m - 1]) * (fabs (q) + fabs (r));
-                        v = fabs (p) * (fabs (a[m - 1][m - 1]) + fabs (z) + 
-                                        fabs (a[m + 1][m + 1]));
-                        if ((float) (u + v) == v)
+                        u = fabs(a[m][m - 1]) * (fabs(q) + fabs(r));
+                        v = fabs(p) * (fabs(a[m - 1][m - 1]) + fabs(z) + 
+                                       fabs(a[m + 1][m + 1]));
+                        if (u + v == v)
                             break;
                     }
-                    for (i = m + 2; i <= nn; i++)
-                    {
+                    for (i = m + 2; i <= nn; ++i) {
                         a[i][i - 2] = 0.0;
                         if (i != (m + 2))
                             a[i][i - 3] = 0.0;
                     }
-                    for (k = m; k <= nn - 1; k++)
-                    {
-                        if (k != m)
-                        {
+                    for (k = m; k <= nn - 1; ++k) {
+                        float s;
+                        if (k != m) {
                             p = a[k][k - 1];
                             q = a[k + 1][k - 1];
                             r = 0.0;
                             if (k != (nn - 1))
                                 r = a[k + 2][k - 1];
-                            if ((x = fabs (p) + fabs (q) + fabs (r)))
-                            {
+                            if ((x = fabs(p) + fabs(q) + fabs(r))) {
                                 p /= x;
                                 q /= x;
                                 r /= x;
                             }
                         }
-                        if ((s = SIGN (sqrt (p * p + q * q + r * r), p))) 
-                        {
-                            if (k == m)
-                            {
+                        s = sign(sqrt(SQR(p) + SQR(q) + SQR(r)), p);
+                        if (s) {
+                            int const mmin = nn < k + 3 ? nn : k + 3;
+                            float z;
+                            int j;
+                            if (k == m) {
                                 if (l != m)
                                     a[k][k - 1] = -a[k][k - 1];
-                            }
-                            else
+                            } else
                                 a[k][k - 1] = -s * x;
                             p += s;
                             x = p / s;
@@ -370,23 +426,18 @@ hessenberg (float **a, int n, float wr[], float wi[])
                             z = r / s;
                             q /= p;
                             r /= p;
-                            for (j = k; j <= nn; j++)
-                            {
+                            for (j = k; j <= nn; ++j) {
                                 p = a[k][j] + q * a[k + 1][j];
-                                if (k != (nn - 1))
-                                {
+                                if (k != (nn - 1)) {
                                     p += r * a[k + 2][j];
                                     a[k + 2][j] -= p * z;
                                 }
                                 a[k + 1][j] -= p * y;
                                 a[k][j] -= p * x;
                             }
-                            mmin = nn < k + 3 ? nn : k + 3;
-                            for (i = l; i <= mmin; i++)
-                            {
+                            for (i = l; i <= mmin; ++i) {
                                 p = x * a[i][k] + y * a[i][k + 1];
-                                if (k != (nn - 1))
-                                {
+                                if (k != (nn - 1)) {
                                     p += z * a[i][k + 2];
                                     a[i][k + 2] -= p * r;
                                 }
@@ -404,428 +455,497 @@ hessenberg (float **a, int n, float wr[], float wi[])
 
 
 static float 
-f1_asm (float **P, int Ng)
-
-/* Angular Second Moment */
-{
-    int i, j;
-    float sum = 0;
-
-    for (i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-            sum += P[i][j] * P[i][j];
-
+f1_a2m(float **     const p,
+       unsigned int const ng) {
+/*----------------------------------------------------------------------------
+  Angular Second Moment
+
+  The angular second-moment feature (ASM) f1 is a measure of homogeneity of
+  the image. In a homogeneous image, there are very few dominant gray-tone
+  transitions. Hence the P matrix for such an image will have fewer entries of
+  large magnitude.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    float sum;
+
+    for (i = 0, sum = 0.0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j)
+            sum += p[i][j] * p[i][j];
+    }
     return sum;
-
-    /*
-     * The angular second-moment feature (ASM) f1 is a measure of homogeneity
-     * of the image. In a homogeneous image, there are very few dominant
-     * gray-tone transitions. Hence the P matrix for such an image will have
-     * fewer entries of large magnitude.
-     */
 }
 
 
-static float 
-f2_contrast (float **P, int Ng)
-
-/* Contrast */
-{
-    int i, j, n;
-    float sum = 0, bigsum = 0;
 
-    for (n = 0; n < Ng; ++n)
-    {
-        for (i = 0; i < Ng; ++i)
-            for (j = 0; j < Ng; ++j)
+static float 
+f2_contrast(float **     const p,
+            unsigned int const ng) {
+/*----------------------------------------------------------------------------
+   Contrast
+   
+   The contrast feature is a difference moment of the P matrix and is a
+   measure of the contrast or the amount of local variations present in an
+   image.
+-----------------------------------------------------------------------------*/
+    unsigned int n;
+    float bigsum;
+
+    for (n = 0, bigsum = 0.0; n < ng; ++n) {
+        unsigned int i;
+        float sum;
+        for (i = 0, sum = 0.0; i < ng; ++i) {
+            unsigned int j;
+            for (j = 0; j < ng; ++j) {
                 if ((i - j) == n || (j - i) == n)
-                    sum += P[i][j];
-        bigsum += n * n * sum;
-
-        sum = 0;
+                    sum += p[i][j];
+            }
+        }
+        bigsum += SQR(n) * sum;
     }
     return bigsum;
-
-    /*
-     * The contrast feature is a difference moment of the P matrix and is a
-     * measure of the contrast or the amount of local variations present in an
-     * image.
-     */
 }
 
-static float 
-f3_corr (float **P, int Ng)
 
-/* Correlation */
-{
-    int i, j;
-    float sum_sqrx = 0, sum_sqry = 0, tmp, *px;
-    float meanx =0 , meany = 0 , stddevx, stddevy;
 
-    px = vector (0, Ng);
-    for (i = 0; i < Ng; ++i)
+static float 
+f3_corr(float **     const p,
+        unsigned int const ng) {
+/*----------------------------------------------------------------------------
+   Correlation
+
+   This correlation feature is a measure of gray-tone linear-dependencies in
+   the image.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    float sumSqrx, sumSqry, tmp;
+    float * px;
+    float meanx, meany, stddevx, stddevy;
+
+    sumSqrx = 0.0; sumSqry = 0.0;
+    meanx = 0.0; meany = 0.0;
+
+    px = vector(0, ng);
+    for (i = 0; i < ng; ++i)
         px[i] = 0;
 
-    /*
-     * px[i] is the (i-1)th entry in the marginal probability matrix obtained
-     * by summing the rows of p[i][j]
-     */
-    for (i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-            px[i] += P[i][j];
-
+    /* px[i] is the (i-1)th entry in the marginal probability matrix obtained
+       by summing the rows of p[i][j]
+    */
+    for (i = 0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j)
+            px[i] += p[i][j];
+    }
 
     /* Now calculate the means and standard deviations of px and py */
-    /*- fix supplied by J. Michael Christensen, 21 Jun 1991 */
-    /*- further modified by James Darrell McCauley, 16 Aug 1991 
-     *     after realizing that meanx=meany and stddevx=stddevy
-     */
-    for (i = 0; i < Ng; ++i)
-    {
-        meanx += px[i]*i;
-        sum_sqrx += px[i]*i*i;
+    for (i = 0; i < ng; ++i) {
+        meanx += px[i] * i;
+        sumSqrx += px[i] * SQR(i);
     }
+
     meany = meanx;
-    sum_sqry = sum_sqrx;
-    stddevx = sqrt (sum_sqrx - (meanx * meanx));
+    sumSqry = sumSqrx;
+    stddevx = sqrt(sumSqrx - (SQR(meanx)));
     stddevy = stddevx;
 
     /* Finally, the correlation ... */
-    for (tmp = 0, i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-            tmp += i*j*P[i][j];
-
+    for (i = 0, tmp = 0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j)
+            tmp += i * j * p[i][j];
+    }
     return (tmp - meanx * meany) / (stddevx * stddevy);
-    /*
-     * This correlation feature is a measure of gray-tone linear-dependencies
-     * in the image.
-     */
 }
 
 
-static float 
-f4_var (float **P, int Ng)
-
-/* Sum of Squares: Variance */
-{
-    int i, j;
-    float mean = 0, var = 0;
-
-    /*- Corrected by James Darrell McCauley, 16 Aug 1991
-     *  calculates the mean intensity level instead of the mean of
-     *  cooccurrence matrix elements 
-     */
-    for (i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-            mean += i * P[i][j];
-
-    for (i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-            var += (i + 1 - mean) * (i + 1 - mean) * P[i][j];
 
+static float 
+f4_var (float **     const p,
+        unsigned int const ng) {
+/*----------------------------------------------------------------------------
+  Sum of Squares: Variance
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    float mean, var;
+
+    for (i = 0, mean = 0.0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j)
+            mean += i * p[i][j];
+    }
+    for (i = 0, var = 0.0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j)
+            var += (i + 1 - mean) * (i + 1 - mean) * p[i][j];
+    }
     return var;
 }
 
-static float 
-f5_idm (float **P, int Ng)
-
-/* Inverse Difference Moment */
-{
-    int i, j;
-    float idm = 0;
 
-    for (i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-            idm += P[i][j] / (1 + (i - j) * (i - j));
 
+static float 
+f5_idm (float **     const p,
+        unsigned int const ng) {
+/*----------------------------------------------------------------------------
+  Inverse Difference Moment
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    float idm;
+
+    for (i = 0, idm = 0.0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j)
+            idm += p[i][j] / (1 + (i - j) * (i - j));
+    }
     return idm;
 }
 
-static float 
-Pxpy[2 * (PGM_MAXMAXVAL+1) + 1];
-
-static float 
-f6_savg (float **P, int Ng)
-
-/* Sum Average */
-{
-    int i, j;
-    float savg = 0;
 
-    for (i = 0; i <= 2 * Ng; ++i)
-        Pxpy[i] = 0;
 
-    for (i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-            Pxpy[i + j + 2] += P[i][j];
-    for (i = 2; i <= 2 * Ng; ++i)
-        savg += i * Pxpy[i];
+static float 
+f6_savg (float **     const p,
+         unsigned int const ng) {
+/*----------------------------------------------------------------------------
+   Sum Average
+-----------------------------------------------------------------------------*/
+    float pxpy[2 * (PGM_MAXMAXVAL+1) + 1];
+    unsigned int i;
+    float savg;
+
+    assert(2*ng < ARRAY_SIZE(pxpy));
+
+    for (i = 0; i <= 2 * ng; ++i)
+        pxpy[i] = 0.0;
+
+    for (i = 0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j)
+            pxpy[i + j + 2] += p[i][j];
+    }
+    for (i = 2, savg = 0.0; i <= 2 * ng; ++i)
+        savg += i * pxpy[i];
 
     return savg;
 }
 
 
-static float 
-f7_svar (float **P, int Ng, float S) {
-/* Sum Variance */
-    int i, j;
-    float var = 0;
-
-    for (i = 0; i <= 2 * Ng; ++i)
-        Pxpy[i] = 0;
-
-    for (i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-            Pxpy[i + j + 2] += P[i][j];
 
-    for (i = 2; i <= 2 * Ng; ++i)
-        var += (i - S) * (i - S) * Pxpy[i];
+static float 
+f7_svar (float **     const p,
+         unsigned int const ng,
+         float        const s) {
+/*----------------------------------------------------------------------------
+   Sum Variance
+-----------------------------------------------------------------------------*/
+    float pxpy[2 * (PGM_MAXMAXVAL+1) + 1];
+    unsigned int i;
+    float var;
+
+    assert(2*ng < ARRAY_SIZE(pxpy));
+
+    for (i = 0; i <= 2 * ng; ++i)
+        pxpy[i] = 0;
+
+    for (i = 0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j)
+            pxpy[i + j + 2] += p[i][j];
+    }
+    for (i = 2, var = 0.0; i <= 2 * ng; ++i)
+        var += (i - s) * (i - s) * pxpy[i];
 
     return var;
 }
 
-static float 
-f8_sentropy (float **P, int Ng)
-
-/* Sum Entropy */
-{
-    int i, j;
-    float sentropy = 0;
 
-    for (i = 0; i <= 2 * Ng; ++i)
-        Pxpy[i] = 0;
 
-    for (i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-            Pxpy[i + j + 2] += P[i][j];
-
-    for (i = 2; i <= 2 * Ng; ++i)
-        sentropy -= Pxpy[i] * log10 (Pxpy[i] + EPSILON);
+static float 
+f8_sentropy (float **     const p,
+             unsigned int const ng) {
+/*----------------------------------------------------------------------------
+   Sum Entropy
+-----------------------------------------------------------------------------*/
+    float pxpy[2 * (PGM_MAXMAXVAL+1) + 1];
+    unsigned int i;
+    float sentropy;
+
+    assert(2*ng < ARRAY_SIZE(pxpy));
+
+    for (i = 0; i <= 2 * ng; ++i)
+        pxpy[i] = 0;
+
+    for (i = 0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j)
+            pxpy[i + j + 2] += p[i][j];
+    }
+    for (i = 2, sentropy = 0.0; i <= 2 * ng; ++i)
+        sentropy -= pxpy[i] * log10(pxpy[i] + EPSILON);
 
     return sentropy;
 }
 
 
-static float 
-f9_entropy (float **P, int Ng)
-
-/* Entropy */
-{
-    int i, j;
-    float entropy = 0;
-
-    for (i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-            entropy += P[i][j] * log10 (P[i][j] + EPSILON);
 
+static float 
+f9_entropy (float **     const p,
+            unsigned int const ng) {
+/*----------------------------------------------------------------------------
+   Entropy
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    float entropy;
+
+    for (i = 0, entropy = 0.0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j)
+            entropy += p[i][j] * log10(p[i][j] + EPSILON);
+    }
     return -entropy;
 }
 
 
-static float 
-f10_dvar (float **P, int Ng)
-
-/* Difference Variance */
-{
-    int i, j;
-    double tmp;
-    double sum = 0, sum_sqr = 0, var = 0;
-
-    for (i = 0; i <= 2 * Ng; ++i)
-        Pxpy[i] = 0;
-
-    for (i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-            Pxpy[abs (i - j)] += P[i][j];
 
+static float 
+f10_dvar (float **     const p,
+          unsigned int const ng) {
+/*----------------------------------------------------------------------------
+   Difference Variance
+-----------------------------------------------------------------------------*/
+    double pxpy[PGM_MAXMAXVAL + 1];
+    unsigned int i;
+    double sqrNg;  /* Square of 'ng' */
+    double sum;
+    double sumSqr;
+    double var;
+
+    assert(ng <= ARRAY_SIZE(pxpy));
+
+    for (i = 0; i < ng; ++i)
+        pxpy[i] = 0;
+
+    for (i = 0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j)
+            pxpy[abs(i - j)] += p[i][j];
+    }
     /* Now calculate the variance of Pxpy (Px-y) */
-    for (i = 0; i < Ng; ++i)
-    {
-        sum += Pxpy[i];
-        sum_sqr += Pxpy[i] * Pxpy[i];
+    for (i = 0, sum = 0.0, sumSqr = 0.0; i < ng; ++i) {
+        sum += pxpy[i];
+        sumSqr += SQR(pxpy[i]);
     }
-    tmp = Ng * Ng;
-    var = ((tmp * sum_sqr) - (sum * sum)) / (tmp * tmp);
+    sqrNg = SQR(ng);
+    var = (sqrNg * sumSqr - SQR(sum)) / SQR(sqrNg);
 
     return var;
 }
 
-static float 
-f11_dentropy (float **P, int Ng)
-    
-/* Difference Entropy */
-{
-    int i, j;
-    float sum = 0;
-
-    for (i = 0; i <= 2 * Ng; ++i)
-        Pxpy[i] = 0;
 
-    for (i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-            Pxpy[abs (i - j)] += P[i][j];
 
-    for (i = 0; i < Ng; ++i)
-        sum += Pxpy[i] * log10 (Pxpy[i] + EPSILON);
+static float 
+f11_dentropy (float **     const p,
+              unsigned int const ng) {
+/*----------------------------------------------------------------------------
+   Difference Entropy
+-----------------------------------------------------------------------------*/
+    float pxpy[2 * (PGM_MAXMAXVAL+1) + 1];
+    unsigned int i;
+    float sum;
+
+    assert(2*ng < ARRAY_SIZE(pxpy));
+
+    for (i = 0; i <= 2 * ng; ++i)
+        pxpy[i] = 0;
+
+    for (i = 0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j)
+            pxpy[abs(i - j)] += p[i][j];
+    }
+    for (i = 0, sum = 0.0; i < ng; ++i)
+        sum += pxpy[i] * log10(pxpy[i] + EPSILON);
 
     return -sum;
 }
 
-static float 
-f12_icorr (float **P, int Ng)
 
-/* Information Measures of Correlation */
-{
-    int i, j;
-    float *px, *py;
-    float hx = 0, hy = 0, hxy = 0, hxy1 = 0, hxy2 = 0;
 
-    px = vector (0, Ng);
-    py = vector (0, Ng);
+static float 
+f12_icorr (float **     const p,
+           unsigned int const ng) {
+/*----------------------------------------------------------------------------
+  Information Measures of Correlation
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    float * px;
+    float * py;
+    float hx, hy, hxy, hxy1, hxy2;
+
+    px = vector(0, ng);
+    py = vector(0, ng);
 
     /*
      * px[i] is the (i-1)th entry in the marginal probability matrix obtained
      * by summing the rows of p[i][j]
      */
-    for (i = 0; i < Ng; ++i)
-    {
-        for (j = 0; j < Ng; ++j)
-        {
-            px[i] += P[i][j];
-            py[j] += P[i][j];
+    for (i = 0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j) {
+            px[i] += p[i][j];
+            py[j] += p[i][j];
         }
     }
 
-    for (i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-        {
-            hxy1 -= P[i][j] * log10 (px[i] * py[j] + EPSILON);
-            hxy2 -= px[i] * py[j] * log10 (px[i] * py[j] + EPSILON);
-            hxy -= P[i][j] * log10 (P[i][j] + EPSILON);
-        }
+    hx = 0.0; hy = 0.0; hxy = 0.0; hxy1 = 0.0; hxy2 = 0.0;
 
+    for (i = 0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j) {
+            hxy1 -= p[i][j] * log10(px[i] * py[j] + EPSILON);
+            hxy2 -= px[i] * py[j] * log10(px[i] * py[j] + EPSILON);
+            hxy  -= p[i][j] * log10 (p[i][j] + EPSILON);
+        }
+    }
     /* Calculate entropies of px and py - is this right? */
-    for (i = 0; i < Ng; ++i)
-    {
-        hx -= px[i] * log10 (px[i] + EPSILON);
-        hy -= py[i] * log10 (py[i] + EPSILON);
+    for (i = 0; i < ng; ++i) {
+        hx -= px[i] * log10(px[i] + EPSILON);
+        hy -= py[i] * log10(py[i] + EPSILON);
     }
-/*  fprintf(stderr,"hxy1=%f\thxy=%f\thx=%f\thy=%f\n",hxy1,hxy,hx,hy); */
-    return ((hxy - hxy1) / (hx > hy ? hx : hy));
+    return (hxy - hxy1) / (hx > hy ? hx : hy);
 }
 
-static float 
-f13_icorr (float **P, int Ng)
 
-/* Information Measures of Correlation */
-{
-    int i, j;
-    float *px, *py;
-    float hx = 0, hy = 0, hxy = 0, hxy1 = 0, hxy2 = 0;
 
-    px = vector (0, Ng);
-    py = vector (0, Ng);
+static float 
+f13_icorr (float **     const p, 
+           unsigned int const ng) {
+/*----------------------------------------------------------------------------
+  Information Measures of Correlation
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    float * px;
+    float * py;
+    float hx, hy, hxy, hxy1, hxy2;
+
+    px = vector(0, ng);
+    py = vector(0, ng);
 
     /*
      * px[i] is the (i-1)th entry in the marginal probability matrix obtained
      * by summing the rows of p[i][j]
      */
-    for (i = 0; i < Ng; ++i)
-    {
-        for (j = 0; j < Ng; ++j)
-        {
-            px[i] += P[i][j];
-            py[j] += P[i][j];
+    for (i = 0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j) {
+            px[i] += p[i][j];
+            py[j] += p[i][j];
         }
     }
 
-    for (i = 0; i < Ng; ++i)
-        for (j = 0; j < Ng; ++j)
-        {
-            hxy1 -= P[i][j] * log10 (px[i] * py[j] + EPSILON);
-            hxy2 -= px[i] * py[j] * log10 (px[i] * py[j] + EPSILON);
-            hxy -= P[i][j] * log10 (P[i][j] + EPSILON);
-        }
+    hx = 0.0; hy = 0.0; hxy = 0.0; hxy1 = 0.0; hxy2 = 0.0;
 
+    for (i = 0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j) {
+            hxy1 -= p[i][j] * log10(px[i] * py[j] + EPSILON);
+            hxy2 -= px[i] * py[j] * log10(px[i] * py[j] + EPSILON);
+            hxy  -= p[i][j] * log10(p[i][j] + EPSILON);
+        }
+    }
     /* Calculate entropies of px and py */
-    for (i = 0; i < Ng; ++i)
-    {
+    for (i = 0; i < ng; ++i) {
         hx -= px[i] * log10 (px[i] + EPSILON);
         hy -= py[i] * log10 (py[i] + EPSILON);
     }
-    /* fprintf(stderr,"hx=%f\thxy2=%f\n",hx,hxy2); */
-    return (sqrt (fabs (1 - exp (-2.0 * (hxy2 - hxy)))));
+    return sqrt(fabs(1 - exp (-2.0 * (hxy2 - hxy))));
 }
 
-static float 
-f14_maxcorr (float **P, int Ng)
 
-/* Returns the Maximal Correlation Coefficient */
-{
-    int i, j, k;
-    float *px, *py, **Q;
-    float *x, *iy, tmp;
 
-    px = vector (0, Ng);
-    py = vector (0, Ng);
-    Q = matrix (1, Ng + 1, 1, Ng + 1);
-    x = vector (1, Ng);
-    iy = vector (1, Ng);
+static float 
+f14_maxcorr (float **     const p,
+             unsigned int const ng) {
+/*----------------------------------------------------------------------------
+  The Maximal Correlation Coefficient
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    float *px, *py;
+    float ** q;
+    float * x;
+    float * iy;
+    float tmp;
+
+    px = vector(0, ng);
+    py = vector(0, ng);
+    q = matrix(1, ng + 1, 1, ng + 1);
+    x = vector(1, ng);
+    iy = vector(1, ng);
 
     /*
      * px[i] is the (i-1)th entry in the marginal probability matrix obtained
      * by summing the rows of p[i][j]
      */
-    for (i = 0; i < Ng; ++i)
-    {
-        for (j = 0; j < Ng; ++j)
-        {
-            px[i] += P[i][j];
-            py[j] += P[i][j];
+    for (i = 0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j) {
+            px[i] += p[i][j];
+            py[j] += p[i][j];
         }
     }
 
-    /* Find the Q matrix */
-    for (i = 0; i < Ng; ++i)
-    {
-        for (j = 0; j < Ng; ++j)
-        {
-            Q[i + 1][j + 1] = 0;
-            for (k = 0; k < Ng; ++k)
-                Q[i + 1][j + 1] += P[i][k] * P[j][k] / px[i] / py[k];
+    /* Compute the Q matrix */
+    for (i = 0; i < ng; ++i) {
+        unsigned int j;
+        for (j = 0; j < ng; ++j) {
+            unsigned int k;
+            q[i + 1][j + 1] = 0;
+            for (k = 0; k < ng; ++k)
+                q[i + 1][j + 1] += p[i][k] * p[j][k] / px[i] / py[k];
         }
     }
 
     /* Balance the matrix */
-    mkbalanced (Q, Ng);
+    mkbalanced(q, ng);
     /* Reduction to Hessenberg Form */
-    reduction (Q, Ng);
+    reduction(q, ng);
     /* Finding eigenvalue for nonsymetric matrix using QR algorithm */
-    hessenberg (Q, Ng, x, iy);
+    hessenberg(q, ng, x, iy);
     if (sortit)
-        simplesrt(Ng,x);
-    /* Returns the sqrt of the second largest eigenvalue of Q */
-    for (i = 2, tmp = x[1]; i <= Ng; ++i)
+        simplesrt(ng, x);
+
+    /* Return the sqrt of the second largest eigenvalue of q */
+    for (i = 2, tmp = x[1]; i <= ng; ++i)
         tmp = (tmp > x[i]) ? tmp : x[i];
-    return sqrt (x[Ng - 1]);
+
+    return sqrt(x[ng - 1]);
 }
 
+
+
 int
-main (int argc, char *argv[]) {
-    FILE *ifp;
-    register gray **grays;
-    int tone[PGM_MAXMAXVAL+1], R0, R45, R90, angle, d = 1, x, y;
-    int argn, rows, cols, row, col;
-    int itone, jtone, tones;
-    float **P_matrix0, **P_matrix45, **P_matrix90, **P_matrix135;
-    float ASM[4], contrast[4], corr[4], var[4], idm[4], savg[4];
+main (int argc, const char ** argv) {
+
+    FILE * ifP;
+    gray ** grays;
+    unsigned int tone[PGM_MAXMAXVAL+1];
+    unsigned int r0, r45, r90;
+    unsigned int d;
+    unsigned int x, y;
+    unsigned int row;
+    int rows, cols;
+    int argn;
+    unsigned int itone;
+    unsigned int toneCt;
+    float ** p_matrix0, ** p_matrix45, ** p_matrix90, ** p_matrix135;
+    float a2m[4], contrast[4], corr[4], var[4], idm[4], savg[4];
     float sentropy[4], svar[4], entropy[4], dvar[4], dentropy[4];
     float icorr[4], maxcorr[4];
     gray maxval;
+    unsigned int i;
     const char * const usage = "[-d <d>] [pgmfile]";
 
-
-    pgm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     argn = 1;
 
@@ -835,7 +955,7 @@ main (int argc, char *argv[]) {
         if ( argv[argn][1] == 'd' )
         {
             ++argn;
-            if ( argn == argc || sscanf( argv[argn], "%d", &d ) != 1 )
+            if ( argn == argc || sscanf( argv[argn], "%u", &d ) != 1 )
                 pm_usage( usage );
         }
         else
@@ -845,204 +965,210 @@ main (int argc, char *argv[]) {
 
     if ( argn < argc )
     {
-        ifp = pm_openr( argv[argn] );
+        ifP = pm_openr( argv[argn] );
         ++argn;
     }
     else
-        ifp = stdin;
+        ifP = stdin;
 
     if ( argn != argc )
         pm_usage( usage );
 
-    grays = pgm_readpgm (ifp, &cols, &rows, &maxval);
-    pm_close (ifp);
+    d = 1;
 
-    if (maxval > PGM_MAXMAXVAL) 
-        pm_error("The maxval of the image (%d) is too high.  \n"
-                 "This program's maximum is %d.", maxval, PGM_MAXMAXVAL);
+    grays = pgm_readpgm(ifP, &cols, &rows, &maxval);
+    pm_close (ifP);
 
     /* Determine the number of different gray scales (not maxval) */
-    for (row = PGM_MAXMAXVAL; row >= 0; --row)
-        tone[row] = -1;
-    for (row = rows - 1; row >= 0; --row)
+    for (i = 0; i <= PGM_MAXMAXVAL; ++i)
+        tone[i] = -1;
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
         for (col = 0; col < cols; ++col)
             tone[grays[row][col]] = grays[row][col];
-    for (row = PGM_MAXMAXVAL, tones = 0; row >= 0; --row)
-        if (tone[row] != -1)
-            tones++;
-    pm_message("(Image has %d graylevels.)", tones);
+    }
+    for (i = 0, toneCt = 0; i <= PGM_MAXMAXVAL; ++i) {
+        if (tone[i] != -1)
+            ++toneCt;
+    }
+    pm_message("(Image has %u gray levels.)", toneCt);
 
     /* Collapse array, taking out all zero values */
-    for (row = 0, itone = 0; row <= PGM_MAXMAXVAL; row++)
+    for (row = 0, itone = 0; row <= PGM_MAXMAXVAL; ++row)
         if (tone[row] != -1)
             tone[itone++] = tone[row];
     /* Now array contains only the gray levels present (in ascending order) */
 
     /* Allocate memory for gray-tone spatial dependence matrix */
-    P_matrix0 = matrix (0, tones, 0, tones);
-    P_matrix45 = matrix (0, tones, 0, tones);
-    P_matrix90 = matrix (0, tones, 0, tones);
-    P_matrix135 = matrix (0, tones, 0, tones);
-    for (row = 0; row < tones; ++row)
-        for (col = 0; col < tones; ++col)
-        {
-            P_matrix0[row][col] = P_matrix45[row][col] = 0;
-            P_matrix90[row][col] = P_matrix135[row][col] = 0;
+    p_matrix0   = matrix (0, toneCt, 0, toneCt);
+    p_matrix45  = matrix (0, toneCt, 0, toneCt);
+    p_matrix90  = matrix (0, toneCt, 0, toneCt);
+    p_matrix135 = matrix (0, toneCt, 0, toneCt);
+
+    for (row = 0; row < toneCt; ++row) {
+        unsigned int col;
+        for (col = 0; col < toneCt; ++col) {
+            p_matrix0 [row][col] = p_matrix45 [row][col] = 0;
+            p_matrix90[row][col] = p_matrix135[row][col] = 0;
         }
+    }
+    if (d > cols)
+        pm_error("Image is narrower (%u columns) "
+                 "than specified distance (%u)", cols, d);
 
     /* Find gray-tone spatial dependence matrix */
-    fprintf (stderr, "(Computing spatial dependence matrix...");
-    for (row = 0; row < rows; ++row)
-        for (col = 0; col < cols; ++col)
-            for (x = 0, angle = 0; angle <= 135; angle += 45)
-            {
+    pm_message("Computing spatial dependence matrix...");
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            unsigned int angle;
+            for (angle = 0, x = 0; angle <= 135; angle += 45) {
                 while (tone[x] != grays[row][col])
-                    x++;
-                if (angle == 0 && col + d < cols)
-                {
+                    ++x;
+                if (angle == 0 && col + d < cols) {
                     y = 0;
                     while (tone[y] != grays[row][col + d])
-                        y++;
-                    P_matrix0[x][y]++;
-                    P_matrix0[y][x]++;
+                        ++y;
+                    ++p_matrix0[x][y];
+                    ++p_matrix0[y][x];
                 }
-                if (angle == 90 && row + d < rows)
-                {
+                if (angle == 90 && row + d < rows) {
                     y = 0;
                     while (tone[y] != grays[row + d][col])
-                        y++;
-                    P_matrix90[x][y]++;
-                    P_matrix90[y][x]++;
+                        ++y;
+                    ++p_matrix90[x][y];
+                    ++p_matrix90[y][x];
                 }
-                if (angle == 45 && row + d < rows && col - d >= 0)
-                {
+                if (angle == 45 && row + d < rows && col >= d) {
                     y = 0;
                     while (tone[y] != grays[row + d][col - d])
-                        y++;
-                    P_matrix45[x][y]++;
-                    P_matrix45[y][x]++;
+                        ++y;
+                    ++p_matrix45[x][y];
+                    ++p_matrix45[y][x];
                 }
-                if (angle == 135 && row + d < rows && col + d < cols)
-                {
+                if (angle == 135 && row + d < rows && col + d < cols) {
                     y = 0;
                     while (tone[y] != grays[row + d][col + d])
-                        y++;
-                    P_matrix135[x][y]++;
-                    P_matrix135[y][x]++;
+                        ++y;
+                    ++p_matrix135[x][y];
+                    ++p_matrix135[y][x];
                 }
             }
+        }
+    }
     /* Gray-tone spatial dependence matrices are complete */
 
     /* Find normalizing constants */
-    R0 = 2 * rows * (cols - d);
-    R45 = 2 * (rows - d) * (cols - d);
-    R90 = 2 * (rows - d) * cols;
+    r0  = 2 * rows * (cols - d);
+    r45 = 2 * (rows - d) * (cols - d);
+    r90 = 2 * (rows - d) * cols;
 
     /* Normalize gray-tone spatial dependence matrix */
-    for (itone = 0; itone < tones; ++itone)
-        for (jtone = 0; jtone < tones; ++jtone)
-        {
-            P_matrix0[itone][jtone] /= R0;
-            P_matrix45[itone][jtone] /= R45;
-            P_matrix90[itone][jtone] /= R90;
-            P_matrix135[itone][jtone] /= R45;
+    for (itone = 0; itone < toneCt; ++itone) {
+        unsigned int jtone;
+        for (jtone = 0; jtone < toneCt; ++jtone) {
+            p_matrix0[itone][jtone]   /= r0;
+            p_matrix45[itone][jtone]  /= r45;
+            p_matrix90[itone][jtone]  /= r90;
+            p_matrix135[itone][jtone] /= r45;
         }
-
-    fprintf (stderr, " done.)\n");
-    fprintf (stderr, "(Computing textural features");
-    fprintf (stdout, "\n");
-    DOT;
-    fprintf (stdout,
-             "%s         0         45         90        135        Avg\n",
-             BL);
-
-    ASM[0] = f1_asm (P_matrix0, tones);
-    ASM[1] = f1_asm (P_matrix45, tones);
-    ASM[2] = f1_asm (P_matrix90, tones);
-    ASM[3] = f1_asm (P_matrix135, tones);
-    results (F1, ASM);
-
-    contrast[0] = f2_contrast (P_matrix0, tones);
-    contrast[1] = f2_contrast (P_matrix45, tones);
-    contrast[2] = f2_contrast (P_matrix90, tones);
-    contrast[3] = f2_contrast (P_matrix135, tones);
-    results (F2, contrast);
-
-
-    corr[0] = f3_corr (P_matrix0, tones);
-    corr[1] = f3_corr (P_matrix45, tones);
-    corr[2] = f3_corr (P_matrix90, tones);
-    corr[3] = f3_corr (P_matrix135, tones);
-    results (F3, corr);
-
-    var[0] = f4_var (P_matrix0, tones);
-    var[1] = f4_var (P_matrix45, tones);
-    var[2] = f4_var (P_matrix90, tones);
-    var[3] = f4_var (P_matrix135, tones);
-    results (F4, var);
-
-
-    idm[0] = f5_idm (P_matrix0, tones);
-    idm[1] = f5_idm (P_matrix45, tones);
-    idm[2] = f5_idm (P_matrix90, tones);
-    idm[3] = f5_idm (P_matrix135, tones);
-    results (F5, idm);
-
-    savg[0] = f6_savg (P_matrix0, tones);
-    savg[1] = f6_savg (P_matrix45, tones);
-    savg[2] = f6_savg (P_matrix90, tones);
-    savg[3] = f6_savg (P_matrix135, tones);
-    results (F6, savg);
-
-    sentropy[0] = f8_sentropy (P_matrix0, tones);
-    sentropy[1] = f8_sentropy (P_matrix45, tones);
-    sentropy[2] = f8_sentropy (P_matrix90, tones);
-    sentropy[3] = f8_sentropy (P_matrix135, tones);
-    svar[0] = f7_svar (P_matrix0, tones, savg[0]);
-    svar[1] = f7_svar (P_matrix45, tones, savg[1]);
-    svar[2] = f7_svar (P_matrix90, tones, savg[2]);
-    svar[3] = f7_svar (P_matrix135, tones, savg[3]);
-    results (F7, svar);
-    results (F8, sentropy);
-
-    entropy[0] = f9_entropy (P_matrix0, tones);
-    entropy[1] = f9_entropy (P_matrix45, tones);
-    entropy[2] = f9_entropy (P_matrix90, tones);
-    entropy[3] = f9_entropy (P_matrix135, tones);
-    results (F9, entropy);
-
-    dvar[0] = f10_dvar (P_matrix0, tones);
-    dvar[1] = f10_dvar (P_matrix45, tones);
-    dvar[2] = f10_dvar (P_matrix90, tones);
-    dvar[3] = f10_dvar (P_matrix135, tones);
-    results (F10, dvar);
-
-    dentropy[0] = f11_dentropy (P_matrix0, tones);
-    dentropy[1] = f11_dentropy (P_matrix45, tones);
-    dentropy[2] = f11_dentropy (P_matrix90, tones);
-    dentropy[3] = f11_dentropy (P_matrix135, tones);
+    }
+    pm_message(" ...done.");
+
+    pm_message("Computing textural features ...");
+
+    fprintf(stdout, "\n");
+    fprintf(stdout,
+            "%s         0         45         90        135        Avg\n",
+            BL);
+
+    a2m[0] = f1_a2m(p_matrix0,   toneCt);
+    a2m[1] = f1_a2m(p_matrix45,  toneCt);
+    a2m[2] = f1_a2m(p_matrix90,  toneCt);
+    a2m[3] = f1_a2m(p_matrix135, toneCt);
+    results(F1, a2m);
+
+    contrast[0] = f2_contrast(p_matrix0,   toneCt);
+    contrast[1] = f2_contrast(p_matrix45,  toneCt);
+    contrast[2] = f2_contrast(p_matrix90,  toneCt);
+    contrast[3] = f2_contrast(p_matrix135, toneCt);
+    results(F2, contrast);
+
+
+    corr[0] = f3_corr(p_matrix0,   toneCt);
+    corr[1] = f3_corr(p_matrix45,  toneCt);
+    corr[2] = f3_corr(p_matrix90,  toneCt);
+    corr[3] = f3_corr(p_matrix135, toneCt);
+    results(F3, corr);
+
+    var[0] = f4_var(p_matrix0,   toneCt);
+    var[1] = f4_var(p_matrix45,  toneCt);
+    var[2] = f4_var(p_matrix90,  toneCt);
+    var[3] = f4_var(p_matrix135, toneCt);
+    results(F4, var);
+
+
+    idm[0] = f5_idm(p_matrix0,   toneCt);
+    idm[1] = f5_idm(p_matrix45,  toneCt);
+    idm[2] = f5_idm(p_matrix90,  toneCt);
+    idm[3] = f5_idm(p_matrix135, toneCt);
+    results(F5, idm);
+
+    savg[0] = f6_savg(p_matrix0,  toneCt);
+    savg[1] = f6_savg(p_matrix45,  toneCt);
+    savg[2] = f6_savg(p_matrix90,  toneCt);
+    savg[3] = f6_savg(p_matrix135, toneCt);
+    results(F6, savg);
+
+    svar[0] = f7_svar(p_matrix0,   toneCt, savg[0]);
+    svar[1] = f7_svar(p_matrix45,  toneCt, savg[1]);
+    svar[2] = f7_svar(p_matrix90,  toneCt, savg[2]);
+    svar[3] = f7_svar(p_matrix135, toneCt, savg[3]);
+    results(F7, svar);
+
+    sentropy[0] = f8_sentropy(p_matrix0,   toneCt);
+    sentropy[1] = f8_sentropy(p_matrix45,  toneCt);
+    sentropy[2] = f8_sentropy(p_matrix90,  toneCt);
+    sentropy[3] = f8_sentropy(p_matrix135, toneCt);
+    results(F8, sentropy);
+
+    entropy[0] = f9_entropy(p_matrix0,   toneCt);
+    entropy[1] = f9_entropy(p_matrix45,  toneCt);
+    entropy[2] = f9_entropy(p_matrix90,  toneCt);
+    entropy[3] = f9_entropy(p_matrix135, toneCt);
+    results(F9, entropy);
+
+    dvar[0] = f10_dvar(p_matrix0,   toneCt);
+    dvar[1] = f10_dvar(p_matrix45,  toneCt);
+    dvar[2] = f10_dvar(p_matrix90,  toneCt);
+    dvar[3] = f10_dvar(p_matrix135, toneCt);
+    results(F10, dvar);
+
+    dentropy[0] = f11_dentropy(p_matrix0,   toneCt);
+    dentropy[1] = f11_dentropy(p_matrix45,  toneCt);
+    dentropy[2] = f11_dentropy(p_matrix90,  toneCt);
+    dentropy[3] = f11_dentropy(p_matrix135, toneCt);
     results (F11, dentropy);
 
-    icorr[0] = f12_icorr (P_matrix0, tones);
-    icorr[1] = f12_icorr (P_matrix45, tones);
-    icorr[2] = f12_icorr (P_matrix90, tones);
-    icorr[3] = f12_icorr (P_matrix135, tones);
-    results (F12, icorr);
-
-    icorr[0] = f13_icorr (P_matrix0, tones);
-    icorr[1] = f13_icorr (P_matrix45, tones);
-    icorr[2] = f13_icorr (P_matrix90, tones);
-    icorr[3] = f13_icorr (P_matrix135, tones);
-    results (F13, icorr);
-
-    maxcorr[0] = f14_maxcorr (P_matrix0, tones);
-    maxcorr[1] = f14_maxcorr (P_matrix45, tones);
-    maxcorr[2] = f14_maxcorr (P_matrix90, tones);
-    maxcorr[3] = f14_maxcorr (P_matrix135, tones);
-    results (F14, maxcorr);
-
-
-    fprintf (stderr, " done.)\n");
+    icorr[0] = f12_icorr(p_matrix0,   toneCt);
+    icorr[1] = f12_icorr(p_matrix45,  toneCt);
+    icorr[2] = f12_icorr(p_matrix90,  toneCt);
+    icorr[3] = f12_icorr(p_matrix135, toneCt);
+    results(F12, icorr);
+
+    icorr[0] = f13_icorr(p_matrix0,   toneCt);
+    icorr[1] = f13_icorr(p_matrix45,  toneCt);
+    icorr[2] = f13_icorr(p_matrix90,  toneCt);
+    icorr[3] = f13_icorr(p_matrix135, toneCt);
+    results(F13, icorr);
+
+    maxcorr[0] = f14_maxcorr(p_matrix0,   toneCt);
+    maxcorr[1] = f14_maxcorr(p_matrix45,  toneCt);
+    maxcorr[2] = f14_maxcorr(p_matrix90,  toneCt);
+    maxcorr[3] = f14_maxcorr(p_matrix135, toneCt);
+    results(F14, maxcorr);
+
+    pm_message(" ...done.");
 
     return 0;
 }
diff --git a/analyzer/pnmhistmap.c b/analyzer/pnmhistmap.c
index 7e504734..fc1bbbde 100644
--- a/analyzer/pnmhistmap.c
+++ b/analyzer/pnmhistmap.c
@@ -17,6 +17,7 @@
  * - Deal properly with maxvals other than 256
  */
 
+#include <assert.h>
 #include <string.h>
 
 #include "pm_c_util.h"
@@ -26,8 +27,6 @@
 
 static double const epsilon = .00001;
 
-#define SCALE_H(value) (hscale_unity ? (value) : (int)((value) * hscale))
-
 enum wantedColor {WANT_RED=0, WANT_GRN=1, WANT_BLU=2};
 
 struct cmdlineInfo {
@@ -53,14 +52,14 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -93,7 +92,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!lvalSpec)
@@ -118,7 +117,7 @@ parseCommandLine(int argc, char ** argv,
         cmdlineP->inputFilespec = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
-                 "specified %d", argc-1);
+                 "specified %u", argc-1);
     else
         cmdlineP->inputFilespec = argv[1];
 }
@@ -127,7 +126,7 @@ parseCommandLine(int argc, char ** argv,
 
 static unsigned int
 maxSlotCount(const unsigned int * const hist,
-             unsigned int         const hist_width,
+             unsigned int         const histWidth,
              bool                 const no_white,
              bool                 const no_black) {
 /*----------------------------------------------------------------------------
@@ -138,7 +137,7 @@ maxSlotCount(const unsigned int * const hist,
     unsigned int i;
 
     unsigned int const start = (no_black ? 1 : 0);
-    unsigned int const finish = (no_white ? hist_width - 1 : hist_width);
+    unsigned int const finish = (no_white ? histWidth - 1 : histWidth);
     for (hmax = 0, i = start; i < finish; ++i)
         if (hmax < hist[i])
             hmax = hist[i];
@@ -150,36 +149,52 @@ maxSlotCount(const unsigned int * const hist,
 
 static void
 clipHistogram(unsigned int * const hist,
-              unsigned int   const hist_width,
+              unsigned int   const histWidth,
               unsigned int   const hmax) {
 
     unsigned int i;
 
-    for (i = 0; i < hist_width; ++i)
+    for (i = 0; i < histWidth; ++i)
         hist[i] = MIN(hmax, hist[i]);
 }
 
 
 
 static void
-pgm_hist(FILE *       const ifP,
-         int          const cols,
-         int          const rows,
-         xelval       const maxval,
-         int          const format,
-         bool         const dots,
-         bool         const no_white,
-         bool         const no_black,
-         bool         const verbose,
-         xelval       const startval,
-         xelval       const endval,
-         unsigned int const hist_width,
-         unsigned int const hist_height,
-         bool         const clipSpec,
-         unsigned int const clipCount,
-         double       const hscale) {
-
-    bool const hscale_unity = hscale - 1 < epsilon;
+countComp(xelval         const value,
+          xelval         const startval,
+          xelval         const endval,
+          unsigned int   const histWidth,
+          unsigned int * const hist) {
+
+    double const hscale = (float)(histWidth-1) / (endval - startval - 1);
+
+    if (value >= startval && value < endval) {
+        unsigned int const bin = ROUNDU((value-startval) * hscale);
+
+        assert(bin < histWidth);
+        ++hist[bin];
+    }
+}
+
+
+
+static void
+pgmHist(FILE *       const ifP,
+        int          const cols,
+        int          const rows,
+        xelval       const maxval,
+        int          const format,
+        bool         const dots,
+        bool         const no_white,
+        bool         const no_black,
+        bool         const verbose,
+        xelval       const startval,
+        xelval       const endval,
+        unsigned int const histWidth,
+        unsigned int const histHeight,
+        bool         const clipSpec,
+        unsigned int const clipCount) {
 
     gray * grayrow;
     bit ** bits;
@@ -188,15 +203,15 @@ pgm_hist(FILE *       const ifP,
     double vscale;
     unsigned int hmax;
     
-    MALLOCARRAY(ghist, hist_width);
+    MALLOCARRAY(ghist, histWidth);
     if (ghist == NULL)
-        pm_error("Not enough memory for histogram array (%d bytes)",
-                  hist_width * sizeof(int));
-    bits = pbm_allocarray(hist_width, hist_height);
+        pm_error("Not enough memory for histogram array (%u bytes)",
+                 histWidth * (unsigned)sizeof(int));
+    bits = pbm_allocarray(histWidth, histHeight);
     if (bits == NULL)
-        pm_error("no space for output array (%d bits)",
-                 hist_width * hist_height);
-    memset(ghist, 0, hist_width * sizeof(ghist[0]));
+        pm_error("no space for output array (%u bits)",
+                 histWidth * histHeight);
+    memset(ghist, 0, histWidth * sizeof(ghist[0]));
 
     /* read the pixel values into the histogram arrays */
     grayrow = pgm_allocrow(cols);
@@ -206,12 +221,8 @@ pgm_hist(FILE *       const ifP,
 
     for (i = rows; i > 0; --i) {
         pgm_readpgmrow (ifP, grayrow, cols, maxval, format);
-        for (j = cols-1; j >= 0; --j) {
-            int const value = grayrow[j];
-
-            if (value >= startval && value <= endval)
-                ++ghist[SCALE_H(value-startval)];
-        }
+        for (j = cols-1; j >= 0; --j)
+            countComp(grayrow[j], startval, endval, histWidth, ghist);
     }
     pgm_freerow(grayrow);
 
@@ -221,33 +232,35 @@ pgm_hist(FILE *       const ifP,
     if (clipSpec)
         hmax = clipCount;
     else 
-        hmax = maxSlotCount(ghist, hist_width, no_white, no_black);
+        hmax = maxSlotCount(ghist, histWidth, no_white, no_black);
+
+    assert(hmax > 0);
 
     if (verbose)
         pm_message("Done: height = %u", hmax);
 
-    clipHistogram(ghist, hist_width, hmax);
+    clipHistogram(ghist, histWidth, hmax);
 
-    vscale = (double) hist_height / hmax;
+    vscale = (double) histHeight / hmax;
 
-    for (i = 0; i < hist_width; ++i) {
-        int mark = hist_height - (int)(vscale * ghist[i]);
+    for (i = 0; i < histWidth; ++i) {
+        int mark = histHeight - (int)(vscale * ghist[i]);
         for (j = 0; j < mark; ++j)
             bits[j][i] = PBM_BLACK;
-        if (j < hist_height)
+        if (j < histHeight)
             bits[j++][i] = PBM_WHITE;
-        for ( ; j < hist_height; ++j)
+        for ( ; j < histHeight; ++j)
             bits[j][i] = dots ? PBM_BLACK : PBM_WHITE;
     }
 
-    pbm_writepbm(stdout, bits, hist_width, hist_height, 0);
+    pbm_writepbm(stdout, bits, histWidth, histHeight, 0);
 }
 
 
 
 static unsigned int
 maxSlotCountAll(unsigned int *       const hist[3],
-                unsigned int         const hist_width,
+                unsigned int         const histWidth,
                 bool                 const no_white,
                 bool                 const no_black) {
 /*----------------------------------------------------------------------------
@@ -264,7 +277,7 @@ maxSlotCountAll(unsigned int *       const hist[3],
         if (hist[color])
             hmax = MAX(hmax, 
                        maxSlotCount(hist[color], 
-                                    hist_width, no_white, no_black));
+                                    histWidth, no_white, no_black));
     
     return hmax;
 }
@@ -273,107 +286,132 @@ maxSlotCountAll(unsigned int *       const hist[3],
 
 static void
 createHist(bool             const colorWanted[3],
-           unsigned int     const hist_width,
+           unsigned int     const histWidth,
            unsigned int * (* const histP)[3]) {
 /*----------------------------------------------------------------------------
    Allocate the histogram arrays and set each slot count to zero.
 -----------------------------------------------------------------------------*/
     unsigned int color;
 
-    for (color = 0; color < 3; ++color)
+    for (color = 0; color < 3; ++color) {
         if (colorWanted[color]) {
             unsigned int * hist;
             unsigned int i;
-            MALLOCARRAY(hist, hist_width);
+            MALLOCARRAY(hist, histWidth);
             if (hist == NULL)
-                pm_error ("Not enough memory for histogram arrays (%u bytes)",
-                          hist_width * sizeof(int) * 3);
+                pm_error("Not enough memory for histogram arrays (%u bytes)",
+                         histWidth * (unsigned)sizeof(hist[0]) * 3);
 
-            for (i = 0; i < hist_width; ++i)
+            for (i = 0; i < histWidth; ++i)
                 hist[i] = 0;
             (*histP)[color] = hist;
         } else
             (*histP)[color] = NULL;
+    }
 }
 
 
 
 static void
 clipHistogramAll(unsigned int * const hist[3],
-                 unsigned int   const hist_width,
+                 unsigned int   const histWidth,
                  unsigned int   const hmax) {
 
     unsigned int color;
 
     for (color = 0; color < 3; ++color)
         if (hist[color])
-            clipHistogram(hist[color], hist_width, hmax);
+            clipHistogram(hist[color], histWidth, hmax);
 }
 
 
 
 static void
-ppm_hist(FILE *       const ifP,
-         int          const cols,
-         int          const rows,
-         xelval       const maxval,
-         int          const format,
-         bool         const dots,
-         bool         const no_white,
-         bool         const no_black,
-         bool         const colorWanted[3],
-         bool         const verbose,
-         xelval       const startval,
-         xelval       const endval,
-         unsigned int const hist_width,
-         unsigned int const hist_height,
-         bool         const clipSpec,
-         unsigned int const clipCount,
-         double       const hscale) {
-
-    bool const hscale_unity = hscale - 1 < epsilon;
-
+fillPpmBins(FILE *          const ifP,
+            unsigned int    const cols,
+            unsigned int    const rows,
+            xelval          const maxval,
+            int             const format,
+            bool            const colorWanted[3],
+            bool            const verbose,
+            xelval          const startval,
+            xelval          const endval,
+            unsigned int    const histWidth,
+            unsigned int ** const hist) {
+/*----------------------------------------------------------------------------
+   For each wanted color component, given by colorWanted[], hist[color] is the
+   histogram.  Each histogram as 'histWidth' bins; we ignore color component
+   values less than 'startval' and greater than or equal to 'endval' and
+   spread the rest evenly across the 'histWidth' bins.
+
+   We get the color component values from the PNM image on *ifP,
+   which is positioned to the raster, whose format is described
+   by 'cols', 'rows', 'maxval', and 'format'.
+-----------------------------------------------------------------------------*/
     pixel * pixrow;
-    pixel ** pixels;
-    int i, j;
-    unsigned int * hist[3];  /* Subscript is enum wantedColor */
-    double vscale;
-    unsigned int hmax;
-
-    createHist(colorWanted, hist_width, &hist);
+    unsigned int row;
 
-    if ((pixels = ppm_allocarray (hist_width, hist_height)) == NULL)
-        pm_error("no space for output array (%d pixels)",
-                 hist_width * hist_height);
-    for (i = 0; i < hist_height; ++i)
-        memset(pixels[i], 0, hist_width * sizeof(pixels[i][0]));
-
-    /* read the pixel values into the histogram arrays */
     pixrow = ppm_allocrow(cols);
 
     if (verbose)
         pm_message("making histogram...");
 
-    for (i = rows; i > 0; --i) {
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
         ppm_readppmrow(ifP, pixrow, cols, maxval, format);
-        for (j = cols-1; j >= 0; --j) {
-            int value;
-
-            if (colorWanted[WANT_RED] && 
-                (value = PPM_GETR(pixrow[j])) >= startval && 
-                value <= endval)
-                hist[WANT_RED][SCALE_H(value-startval)]++;
-            if (colorWanted[WANT_GRN] && 
-                (value = PPM_GETG(pixrow[j])) >= startval && 
-                value <= endval)
-                hist[WANT_GRN][SCALE_H(value-startval)]++;
-            if (colorWanted[WANT_BLU] && 
-                (value = PPM_GETB(pixrow[j])) >= startval && 
-                value <= endval)
-                hist[WANT_BLU][SCALE_H(value-startval)]++;
+        for (col = 0; col < cols; ++col) {
+            if (colorWanted[WANT_RED])
+                countComp(PPM_GETR(pixrow[col]),
+                          startval, endval, histWidth, hist[WANT_RED]);
+
+            if (colorWanted[WANT_GRN])
+                countComp(PPM_GETG(pixrow[col]),
+                          startval, endval, histWidth, hist[WANT_GRN]);
+
+            if (colorWanted[WANT_BLU])
+                countComp(PPM_GETB(pixrow[col]),
+                          startval, endval, histWidth, hist[WANT_BLU]);
         }
     }
     ppm_freerow(pixrow);
+}
+
+
+
+static void
+ppmHist(FILE *       const ifP,
+        unsigned int const cols,
+        unsigned int const rows,
+        xelval       const maxval,
+        int          const format,
+        bool         const dots,
+        bool         const no_white,
+        bool         const no_black,
+        bool         const colorWanted[3],
+        bool         const verbose,
+        xelval       const startval,
+        xelval       const endval,
+        unsigned int const histWidth,
+        unsigned int const histHeight,
+        bool         const clipSpec,
+        unsigned int const clipCount) {
+
+    pixel ** pixels;
+    unsigned int i;
+    unsigned int * hist[3];  /* Subscript is enum wantedColor */
+    double vscale;
+    unsigned int hmax;
+
+    createHist(colorWanted, histWidth, &hist);
+
+    if ((pixels = ppm_allocarray (histWidth, histHeight)) == NULL)
+        pm_error("no space for output array (%u pixels)",
+                 histWidth * histHeight);
+    for (i = 0; i < histHeight; ++i)
+        memset(pixels[i], 0, histWidth * sizeof(pixels[i][0]));
+
+    fillPpmBins(ifP, cols, rows, maxval, format, colorWanted, verbose,
+                startval, endval, histWidth, hist);
 
     /* find the highest-valued slot and set the vertical scale value */
     if (verbose)
@@ -381,22 +419,24 @@ ppm_hist(FILE *       const ifP,
     if (clipSpec)
         hmax = clipCount;
     else 
-        hmax = maxSlotCountAll(hist, hist_width, no_white, no_black);
+        hmax = maxSlotCountAll(hist, histWidth, no_white, no_black);
 
-    clipHistogramAll(hist, hist_width, hmax);
+    assert(hmax > 0);
 
-    vscale = (double) hist_height / hmax;
-    if (verbose)
-        pm_message("Done: height = %d, vertical scale factor = %g", 
+    clipHistogramAll(hist, histWidth, hmax);
+
+    vscale = (double) histHeight / hmax;
+    if (verbose && pm_have_float_format())
+        pm_message("Done: height = %u, vertical scale factor = %g", 
                    hmax, vscale);
 
-    for (i = 0; i < hist_width; ++i) {
+    for (i = 0; i < histWidth; ++i) {
         if (hist[WANT_RED]) {
             unsigned int j;
             bool plotted;
             plotted = FALSE;
-            for (j = hist_height - (int)(vscale * hist[WANT_RED][i]); 
-                 j < hist_height && !plotted; 
+            for (j = histHeight - (int)(vscale * hist[WANT_RED][i]); 
+                 j < histHeight && !plotted; 
                  ++j) {
                 PPM_PUTR(pixels[j][i], maxval);
                 plotted = dots;
@@ -406,8 +446,8 @@ ppm_hist(FILE *       const ifP,
             unsigned int j;
             bool plotted;
             plotted = FALSE;
-            for (j = hist_height - (int)(vscale * hist[WANT_GRN][i]); 
-                 j < hist_height && !plotted; 
+            for (j = histHeight - (int)(vscale * hist[WANT_GRN][i]); 
+                 j < histHeight && !plotted; 
                  ++j) {
                 PPM_PUTG(pixels[j][i], maxval);
                 plotted = dots;
@@ -417,33 +457,46 @@ ppm_hist(FILE *       const ifP,
             unsigned int j;
             bool plotted;
             plotted = FALSE;
-            for (j = hist_height - (int)(vscale * hist[WANT_BLU][i]); 
-                 j < hist_height && !plotted; 
+            for (j = histHeight - (int)(vscale * hist[WANT_BLU][i]); 
+                 j < histHeight && !plotted; 
                  ++j) {
                 PPM_PUTB(pixels[j][i], maxval);
                 plotted = dots;
             }
         }
     }
-    ppm_writeppm(stdout, pixels, hist_width, hist_height, maxval, 0);
+    ppm_writeppm(stdout, pixels, histWidth, histHeight, maxval, 0);
+}
+
+
+
+static void
+reportScale(unsigned int const histWidth,
+            unsigned int const range,
+            bool         const verbose) {
+
+    double const hscale = (float)(histWidth-1) / (range-1);
+
+    if (hscale - 1.0 < epsilon && verbose && pm_have_float_format())
+        pm_message("Horizontal scale factor: %g", hscale);
 }
 
 
 
 int
-main(int argc, char ** argv) {
+main(int argc, const char ** argv) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
     int cols, rows;
     xelval maxval;
     int format;
-    unsigned int hist_width;
+    unsigned int histWidth;
     unsigned int range;
-    double hscale;
-    int hmax;
+    unsigned int hmax;
+    xelval startval, endval;
 
-    pnm_init (&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -451,34 +504,33 @@ main(int argc, char ** argv) {
 
     pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
 
-    range = MIN(maxval, cmdline.rval) - cmdline.lval + 1;
+    startval = cmdline.lval;
+    endval   = MIN(maxval, cmdline.rval) + 1;
+
+    range = endval - startval;
 
     if (cmdline.widthSpec)
-        hist_width = cmdline.width;
+        histWidth = cmdline.width;
     else
-        hist_width = range;
-
-    hscale = (float)(hist_width-1) / (range-1);
-    if (hscale - 1.0 < epsilon && cmdline.verbose)
-        pm_message("Horizontal scale factor: %g (maxval = %u)", 
-                   hscale, maxval);
+        histWidth = range;
 
+    reportScale(histWidth, range, cmdline.verbose);
     if (cmdline.nmaxSpec)
-        hmax = cols * rows / hist_width * cmdline.nmax;
+        hmax = cols * rows / histWidth * cmdline.nmax;
 
     switch (PNM_FORMAT_TYPE(format)) {
     case PPM_TYPE:
-        ppm_hist(ifP, cols, rows, maxval, format,
-                 cmdline.dots, cmdline.white, cmdline.black,
-                 cmdline.colorWanted,
-                 cmdline.verbose, cmdline.lval, cmdline.rval, 
-                 hist_width, cmdline.height, cmdline.nmaxSpec, hmax, hscale);
+        ppmHist(ifP, cols, rows, maxval, format,
+                cmdline.dots, cmdline.white, cmdline.black,
+                cmdline.colorWanted,
+                cmdline.verbose, startval, endval,
+                histWidth, cmdline.height, cmdline.nmaxSpec, hmax);
         break;
     case PGM_TYPE:
-        pgm_hist(ifP, cols, rows, maxval, format,
-                 cmdline.dots, cmdline.white, cmdline.black,
-                 cmdline.verbose, cmdline.lval, cmdline.rval,
-                 hist_width, cmdline.height, cmdline.nmaxSpec, hmax, hscale);
+        pgmHist(ifP, cols, rows, maxval, format,
+                cmdline.dots, cmdline.white, cmdline.black,
+                cmdline.verbose, startval, endval,
+                histWidth, cmdline.height, cmdline.nmaxSpec, hmax);
         break;
     case PBM_TYPE:
         pm_error("Cannot do a histogram of a a PBM file");
diff --git a/analyzer/pnmpsnr.c b/analyzer/pnmpsnr.c
index 1160fff6..af74e8c8 100644
--- a/analyzer/pnmpsnr.c
+++ b/analyzer/pnmpsnr.c
@@ -8,30 +8,91 @@
  *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
  */
 
+#include <assert.h>
 #include <string.h>
 #include <stdio.h>
 #include <math.h>
 
 #include "pm_c_util.h"
+#include "mallocvar.h"
 #include "nstring.h"
 #include "pam.h"
+#include "shhopt.h"
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFile1Name;  /* Name of first input file */
+    const char * inputFile2Name;  /* Name of second input file */
+    unsigned int rgb;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to as as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to pm_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 OPTENT3 */
+    OPTENT3(0,   "rgb",      OPT_FLAG,  NULL, &cmdlineP->rgb,       0);
+
+    opt.opt_table     = option_def;
+    opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */
+    opt.allowNegNum   = FALSE; /* We have no parms that are negative numbers */
+    
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others */
+
+    if (argc-1 < 2) 
+        pm_error("Takes two arguments:  names of the two files to compare");
+    else {
+        cmdlineP->inputFile1Name = argv[1];
+        cmdlineP->inputFile2Name = argv[2];
+
+        if (argc-1 > 2)
+            pm_error("Too many arguments (%u).  The only arguments are "
+                     "the names of the two files to compare", argc-1);
+    }
+
+    free(option_def);
+}
+
 
-#define MAXFILES 16
 
 static int
-udiff(unsigned int const subtrahend, unsigned int const subtractor) {
-    return subtrahend-subtractor;
+udiff(unsigned int const subtrahend,
+      unsigned int const subtractor) {
+
+    return subtrahend - subtractor;
 }
 
 
+
 static double
 square(double const arg) {
-    return(arg*arg);
+    return(arg * arg);
 }
 
 
+
 static void
-validate_input(const struct pam pam1, const struct pam pam2) {
+validateInput(struct pam const pam1,
+              struct pam const pam2) {
 
     if (pam1.width != pam2.width)
         pm_error("images are not the same width, so can't be compared.  "
@@ -52,159 +113,325 @@ validate_input(const struct pam pam1, const struct pam pam2) {
                  "maxval of one of them.",
                  (unsigned int) pam1.maxval, (unsigned int) pam2.maxval);
 
-    if (strcmp(pam1.tuple_type, pam2.tuple_type) != 0)
+    if (!streq(pam1.tuple_type, pam2.tuple_type))
         pm_error("images are not of the same type.  The tuple types are "
                  "'%s' and '%s', respectively.",
                  pam1.tuple_type, pam2.tuple_type);
 
-    if (strcmp(pam1.tuple_type, PAM_PBM_TUPLETYPE) != 0 &&
-        strcmp(pam1.tuple_type, PAM_PGM_TUPLETYPE) != 0 &&
-        strcmp(pam1.tuple_type, PAM_PPM_TUPLETYPE) != 0)
+    if (!streq(pam1.tuple_type, PAM_PBM_TUPLETYPE) &&
+        !streq(pam1.tuple_type, PAM_PGM_TUPLETYPE) &&
+        !streq(pam1.tuple_type, PAM_PPM_TUPLETYPE))
         pm_error("Images are not of a PNM type.  Tuple type is '%s'",
                  pam1.tuple_type);
 }
 
 
+enum ColorSpaceId {
+    COLORSPACE_GRAYSCALE,
+    COLORSPACE_YCBCR,
+    COLORSPACE_RGB
+};
 
-static void
-psnr_color(tuple    const tuple1,
-           tuple    const tuple2,
-           double * const ySqDiffP, 
-           double * const cbSqDiffP,
-           double * const crSqDiffP) {
+typedef struct {
+
+    enum ColorSpaceId id;
+
+    unsigned int componentCt;
+
+    const char * componentName[3];
+        /* Only first 'componentCt' elements are valid */
+
+} ColorSpace;
+
+
+struct SqDiff {
+/*----------------------------------------------------------------------------
+   The square-differences of the components of two pixels, for some
+   component set.
+-----------------------------------------------------------------------------*/
+    double sqDiff[3];
+};
+
+
+
+static struct SqDiff
+zeroSqDiff() {
+
+    struct SqDiff retval;
+    unsigned int i;
+
+    for (i = 0; i < 3; ++i)
+        retval.sqDiff[i] = 0.0;
+
+    return retval;
+}
+
+
+
+static struct SqDiff
+sqDiffSum(ColorSpace    const colorSpace,
+          struct SqDiff const addend,
+          struct SqDiff const adder) {
+
+    struct SqDiff retval;
+    unsigned int i;
+
+    for (i = 0; i < colorSpace.componentCt; ++i)
+        retval.sqDiff[i] = addend.sqDiff[i] + adder.sqDiff[i];
+
+    return retval;
+}
+
+
+
+#define Y_INDEX  0
+#define CB_INDEX 1
+#define CR_INDEX 2
+
+static ColorSpace
+yCbCrColorSpace() {
+
+    ColorSpace retval;
+
+    retval.id = COLORSPACE_YCBCR;
+
+    retval.componentCt = 3;
+
+    retval.componentName[Y_INDEX]  = "Y";
+    retval.componentName[CR_INDEX] = "CR";
+    retval.componentName[CB_INDEX] = "CB";
+
+    return retval;
+}
+
+
+
+static struct SqDiff
+sqDiffYCbCr(tuple    const tuple1,
+            tuple    const tuple2) {
+
+    struct SqDiff retval;
 
     double y1, y2, cb1, cb2, cr1, cr2;
     
     pnm_YCbCrtuple(tuple1, &y1, &cb1, &cr1);
     pnm_YCbCrtuple(tuple2, &y2, &cb2, &cr2);
     
-    *ySqDiffP  = square(y1  - y2);
-    *cbSqDiffP = square(cb1 - cb2);
-    *crSqDiffP = square(cr1 - cr2);
+    retval.sqDiff[Y_INDEX]  = square(y1  - y2);
+    retval.sqDiff[CB_INDEX] = square(cb1 - cb2);
+    retval.sqDiff[CR_INDEX] = square(cr1 - cr2);
+
+    return retval;
+}
+
+
+
+#define R_INDEX 0
+#define G_INDEX 1
+#define B_INDEX 2
+
+
+
+static ColorSpace
+rgbColorSpace() {
+
+    ColorSpace retval;
+
+    retval.id = COLORSPACE_RGB;
+
+    retval.componentCt = 3;
+
+    retval.componentName[R_INDEX] = "Red";
+    retval.componentName[G_INDEX] = "Green";
+    retval.componentName[B_INDEX] = "Blue";
+
+    return retval;
+}
+
+
+
+static struct SqDiff
+sqDiffRgb(tuple    const tuple1,
+          tuple    const tuple2) {
+
+    struct SqDiff retval;
+
+    retval.sqDiff[R_INDEX] =
+        square((int)tuple1[PAM_RED_PLANE]  - (int)tuple2[PAM_RED_PLANE]);
+    retval.sqDiff[G_INDEX] =
+        square((int)tuple1[PAM_GRN_PLANE]  - (int)tuple2[PAM_GRN_PLANE]);
+    retval.sqDiff[B_INDEX] =
+        square((int)tuple1[PAM_BLU_PLANE]  - (int)tuple2[PAM_BLU_PLANE]);
+
+    return retval;
+}
+
+
+
+static ColorSpace
+grayscaleColorSpace() {
+
+    ColorSpace retval;
+
+    retval.id = COLORSPACE_GRAYSCALE;
+
+    retval.componentCt = 1;
+
+    retval.componentName[Y_INDEX]  = "luminance";
+
+    return retval;
+}
+
+
+
+static struct SqDiff
+sqDiffGrayscale(tuple    const tuple1,
+                tuple    const tuple2) {
+
+    struct SqDiff sqDiff;
+
+    sqDiff.sqDiff[Y_INDEX] = square(udiff(tuple1[0], tuple2[0]));
+
+    return sqDiff;
+}
+
+
+
+static struct SqDiff
+sumSqDiffFromRaster(struct pam * const pam1P,
+                    struct pam * const pam2P,
+                    ColorSpace   const colorSpace) {
+
+    struct SqDiff sumSqDiff;
+    tuple *tuplerow1, *tuplerow2;  /* malloc'ed */
+    unsigned int row;
+
+    tuplerow1 = pnm_allocpamrow(pam1P);
+    tuplerow2 = pnm_allocpamrow(pam2P);
+    
+    sumSqDiff = zeroSqDiff();
+
+    for (row = 0; row < pam1P->height; ++row) {
+        unsigned int col;
+        
+        pnm_readpamrow(pam1P, tuplerow1);
+        pnm_readpamrow(pam2P, tuplerow2);
+
+        assert(pam1P->width == pam2P->width);
+
+        for (col = 0; col < pam1P->width; ++col) {
+            struct SqDiff sqDiff;
+
+            switch (colorSpace.id) {
+            case COLORSPACE_GRAYSCALE:
+                sqDiff = sqDiffGrayscale(tuplerow1[col], tuplerow2[col]);
+                break;
+            case COLORSPACE_YCBCR:
+                sqDiff = sqDiffYCbCr(tuplerow1[col], tuplerow2[col]);
+                break;
+            case COLORSPACE_RGB:
+                sqDiff = sqDiffRgb(tuplerow1[col], tuplerow2[col]);
+                break;
+            }
+            sumSqDiff = sqDiffSum(colorSpace, sumSqDiff, sqDiff);
+        }
+    }
+
+    pnm_freepamrow(tuplerow1);
+    pnm_freepamrow(tuplerow2);
+
+    return sumSqDiff;
 }
 
 
 
 static void
-reportPsnr(struct pam const pam,
-           double     const ySumSqDiff, 
-           double     const crSumSqDiff,
-           double     const cbSumSqDiff,
-           char       const filespec1[],
-           char       const filespec2[]) {
+reportPsnr(struct pam    const pam,
+           struct SqDiff const sumSqDiff,
+           ColorSpace    const colorSpace,
+           const char *  const fileName1,
+           const char *  const fileName2) {
+
+    double const maxSumSqDiff = square(pam.maxval) * pam.width * pam.height;
+        /* Maximum possible sum square difference, i.e. the sum of the squares
+           of the sample differences between an entirely white image and
+           entirely black image of the given dimensions.
+        */
+
+    unsigned int i;
 
-    bool const color = streq(pam.tuple_type, PAM_PPM_TUPLETYPE);
 
     /* The PSNR is the ratio of the maximum possible mean square difference
-       to the actual mean square difference.
+       to the actual mean square difference, which is also the ratio of
+       the maximum possible sum square difference to the actual sum square
+       difference.
+   
+       Note that in the important special case that the images are
+       identical, the sum square differences are identically 0.0.
+       No precision error; no rounding error.
     */
-    double const yPsnr =
-        square(pam.maxval) / (ySumSqDiff / (pam.width * pam.height));
 
-    /* Note that in the important special case that the images are
-       identical, the sum square differences are identically 0.0.  No
-       precision error; no rounding error.
-    */
+    pm_message("PSNR between '%s' and '%s':", fileName1, fileName2);
 
-    if (color) {
-        double const cbPsnr =
-            square(pam.maxval) / (cbSumSqDiff / (pam.width * pam.height));
-        double const crPsnr =
-            square(pam.maxval) / (crSumSqDiff / (pam.width * pam.height));
+    for (i = 0; i < colorSpace.componentCt; ++i) {
+        const char * label;
 
-        pm_message("PSNR between %s and %s:", filespec1, filespec2);
-        if (ySumSqDiff > 0)
-            pm_message("Y  color component: %.2f dB", 10 * log10(yPsnr));
-        else
-            pm_message("Y color component does not differ.");
-        if (cbSumSqDiff > 0)
-            pm_message("Cb color component: %.2f dB", 10 * log10(cbPsnr));
-        else
-            pm_message("Cb color component does not differ.");
-        if (crSumSqDiff > 0)
-            pm_message("Cr color component: %.2f dB", 10 * log10(crPsnr));
-        else
-            pm_message("Cr color component does not differ.");
-    } else {
-        if (ySumSqDiff > 0)
-            pm_message("PSNR between %s and %s: %.2f dB",
-                       filespec1, filespec2, 10 * log10(yPsnr));
+        pm_asprintf(&label, "%s:", colorSpace.componentName[i]);
+
+        if (sumSqDiff.sqDiff[i] > 0)
+            pm_message("  %-6.6s %.2f dB",
+                       label,
+                       10 * log10(maxSumSqDiff/sumSqDiff.sqDiff[i]));
         else
-            pm_message("Images %s and %s don't differ.",
-                       filespec1, filespec2);
+            pm_message("  %-6.6s no difference", label);
+
+        pm_strfree(label);
     }
 }
 
 
 
 int
-main (int argc, char **argv) {
-    char *filespec1, *filespec2;  /* specs of two files to compare */
-    FILE *file1, *file2;
+main (int argc, const char **argv) {
+    FILE * if1P;
+    FILE * if2P;
     struct pam pam1, pam2;
-    bool color;
-        /* It's a color image */
-    double ySumSqDiff, crSumSqDiff, cbSumSqDiff;
-    tuple *tuplerow1, *tuplerow2;  /* malloc'ed */
-    int row;
+    ColorSpace colorSpace;
     
-    pnm_init(&argc, argv);
+    struct CmdlineInfo cmdline;
 
-    if (argc-1 < 2) 
-        pm_error("Takes two arguments:  specifications of the two files.");
-    else {
-        filespec1 = argv[1];
-        filespec2 = argv[2];
-    }
-    
-    file1 = pm_openr(filespec1);
-    file2 = pm_openr(filespec2);
+    pm_proginit(&argc, argv);
 
-    pnm_readpaminit(file1, &pam1, PAM_STRUCT_SIZE(tuple_type));
-    pnm_readpaminit(file2, &pam2, PAM_STRUCT_SIZE(tuple_type));
+    parseCommandLine(argc, argv, &cmdline);
 
-    validate_input(pam1, pam2);
+    if1P = pm_openr(cmdline.inputFile1Name);
+    if2P = pm_openr(cmdline.inputFile2Name);
 
-    if (strcmp(pam1.tuple_type, PAM_PPM_TUPLETYPE) == 0) 
-        color = TRUE;
-    else
-        color = FALSE;
+    pnm_readpaminit(if1P, &pam1, PAM_STRUCT_SIZE(tuple_type));
+    pnm_readpaminit(if2P, &pam2, PAM_STRUCT_SIZE(tuple_type));
 
-    tuplerow1 = pnm_allocpamrow(&pam1);
-    tuplerow2 = pnm_allocpamrow(&pam2);
-    
-    ySumSqDiff  = 0.0;
-    cbSumSqDiff = 0.0;
-    crSumSqDiff = 0.0;
+    validateInput(pam1, pam2);
 
-    for (row = 0; row < pam1.height; ++row) {
-        int col;
-        
-        pnm_readpamrow(&pam1, tuplerow1);
-        pnm_readpamrow(&pam2, tuplerow2);
-
-        for (col = 0; col < pam1.width; ++col) {
-            if (color) {
-                double ySqDiff, cbSqDiff, crSqDiff;
-                psnr_color(tuplerow1[col], tuplerow2[col], 
-                           &ySqDiff, &cbSqDiff, &crSqDiff);
-                ySumSqDiff  += ySqDiff;
-                cbSumSqDiff += cbSqDiff;
-                crSumSqDiff += crSqDiff;
-                
-            } else {
-                unsigned int const yDiffSq =
-                    square(udiff(tuplerow1[col][0], tuplerow2[col][0]));
-                ySumSqDiff += yDiffSq;
-            }
-        }
-    }
+    if (streq(pam1.tuple_type, PAM_PPM_TUPLETYPE)) {
+        if (cmdline.rgb)
+            colorSpace = rgbColorSpace();
+        else
+            colorSpace = yCbCrColorSpace();
+    } else
+        colorSpace = grayscaleColorSpace();
 
-    reportPsnr(pam1, ySumSqDiff, crSumSqDiff, cbSumSqDiff,
-               filespec1, filespec2);
+    {
+        struct SqDiff const sumSqDiff =
+            sumSqDiffFromRaster(&pam1, &pam2, colorSpace);
 
-    pnm_freepamrow(tuplerow1);
-    pnm_freepamrow(tuplerow2);
+        reportPsnr(pam1, sumSqDiff, colorSpace,
+                   cmdline.inputFile1Name, cmdline.inputFile2Name);
+    }
+    pm_close(if2P);
+    pm_close(if1P);
 
     return 0;
 }
+
+
+
diff --git a/analyzer/ppmhist.c b/analyzer/ppmhist.c
index f42adab5..9b526606 100644
--- a/analyzer/ppmhist.c
+++ b/analyzer/ppmhist.c
@@ -20,17 +20,18 @@
 
 enum sort {SORT_BY_FREQUENCY, SORT_BY_RGB};
 
-enum colorFmt {FMT_DECIMAL, FMT_HEX, FMT_FLOAT, FMT_PPMPLAIN};
+enum ColorFmt {FMT_DECIMAL, FMT_HEX, FMT_FLOAT, FMT_PPMPLAIN};
 
 struct cmdline_info {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
     const char * inputFileName;  /* Name of input file */
-    unsigned int noheader;    /* -noheader option */
-    enum colorFmt colorFmt;
-    unsigned int colorname;   /* -colorname option */
-    enum sort sort;           /* -sort option */
+    unsigned int noheader;
+    enum ColorFmt colorFmt;
+    unsigned int colorname;
+    enum sort sort;
+    unsigned int forensic;
 };
 
 
@@ -45,7 +46,7 @@ parseCommandLine(int argc, const char ** argv,
     optStruct3 opt;  /* set by OPTENT3 */
     optEntry * option_def;
     unsigned int option_def_index;
-    
+
     unsigned int hexcolorOpt, floatOpt, mapOpt, nomapOpt;
     const char * sort_type;
 
@@ -59,6 +60,7 @@ parseCommandLine(int argc, const char ** argv,
     OPTENT3(0,   "float",     OPT_FLAG, NULL,  &floatOpt,              0);
     OPTENT3(0,   "colorname", OPT_FLAG, NULL,  &cmdlineP->colorname,   0);
     OPTENT3(0,   "sort",      OPT_STRING, &sort_type, NULL,            0);
+    OPTENT3(0,   "forensic",  OPT_FLAG, NULL,  &cmdlineP->forensic,    0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -67,10 +69,11 @@ parseCommandLine(int argc, const char ** argv,
     /* Set defaults */
     sort_type = "frequency";
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    free(option_def);
 
-    if (argc-1 == 0) 
+    if (argc-1 == 0)
         cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
@@ -84,9 +87,12 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->colorFmt = FMT_HEX;
     else if (floatOpt)
         cmdlineP->colorFmt = FMT_FLOAT;
-    else if (mapOpt)
+    else if (mapOpt) {
+        if (cmdlineP->forensic)
+            pm_error("You cannot specify -map and -forensic together");
+
         cmdlineP->colorFmt = FMT_PPMPLAIN;
-    else 
+    } else
         cmdlineP->colorFmt = FMT_DECIMAL;
 
     if (strcmp(sort_type, "frequency") == 0)
@@ -101,32 +107,197 @@ parseCommandLine(int argc, const char ** argv,
 
 
 static int
-countcompare(const void *ch1, const void *ch2) {
-    return ((colorhist_vector)ch2)->value - ((colorhist_vector)ch1)->value;
+cmpUint(unsigned int const a,
+        unsigned int const b) {
+/*----------------------------------------------------------------------------
+   Return 1 if 'a' > 'b'; -1 if 'a' is < 'b'; 0 if 'a' == 'b'.
+
+   I.e. what a libc qsort() comparison function returns.
+-----------------------------------------------------------------------------*/
+    return a > b ? 1 : a < b ? -1 : 0;
 }
 
 
+
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn countcompare;
+#endif
+
+
 static int
-rgbcompare(const void * arg1, const void * arg2) {
+countcompare(const void * const a,
+             const void * const b) {
+/*----------------------------------------------------------------------------
+   This is a 'qsort' collation function.
+-----------------------------------------------------------------------------*/
+    const struct colorhist_item * const histItem1P = a;
+    const struct colorhist_item * const histItem2P = b;
+
+    return cmpUint(histItem2P->value, histItem1P->value);
+}
+
+
+
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn rgbcompare;
+#endif
 
-    colorhist_vector const ch1 = (colorhist_vector) arg1;
-    colorhist_vector const ch2 = (colorhist_vector) arg2;
+static int
+rgbcompare(const void * const a,
+           const void * const b) {
+/*----------------------------------------------------------------------------
+   This is a 'qsort' collation function.
+-----------------------------------------------------------------------------*/
+    const struct colorhist_item * const histItem1P = a;
+    const struct colorhist_item * const histItem2P = b;
 
     int retval;
 
-    retval = (PPM_GETR(ch1->color) - PPM_GETR(ch2->color));
+    retval = cmpUint(PPM_GETR(histItem1P->color),
+                     PPM_GETR(histItem2P->color));
     if (retval == 0) {
-        retval = (PPM_GETG(ch1->color) - PPM_GETG(ch2->color));
+        retval = cmpUint(PPM_GETG(histItem1P->color),
+                         PPM_GETG(histItem2P->color));
         if (retval == 0)
-            retval = (PPM_GETB(ch1->color) - PPM_GETB(ch2->color));
+            retval = cmpUint(PPM_GETB(histItem1P->color),
+                             PPM_GETB(histItem2P->color));
     }
     return retval;
 }
 
 
 
+static pixval
+universalMaxval(pixval const maxval,
+                int    const format) {
+/*----------------------------------------------------------------------------
+  A maxval that makes it impossible for a pixel to be invalid in an image that
+  states it maxval as 'maxval' and has format 'format'.
+
+  E.g. in a one-byte-per-sample image, it's not possible to read a sample
+  value greater than 255, so a maxval of 255 makes it impossible for a sample
+  to be invalid.
+
+  But: we never go above 65535, which means our maxval isn't entirely
+  universal.  If the image is plain PPM, it could contain a pixel that
+  exceeds even that.
+-----------------------------------------------------------------------------*/
+    assert(0 < maxval && maxval < 65536);
+
+    if (format == RPPM_FORMAT || format == RPGM_FORMAT) {
+        /*  Raw PPM stream has either one or two bytes per pixel, depending
+            upon its stated maxval.
+        */
+        if (maxval > 255)
+            return 65535;
+        else
+            return 255;
+    } else if (format == RPBM_FORMAT) {
+        /* A Raw PBM stream has one bit per pixel, which libnetpbm renders
+           as 0 or 255 when we read it.
+        */
+        assert(maxval == 255);
+        return 255;
+    } else {
+        /* A plain PPM stream has essentially unlimited range in the
+           tokens that are supposed to be sample values.  We arbitrarily draw
+           the line at 65535.
+        */
+        return 65535;
+    }
+}
+
+
+
+static bool
+colorIsValid(pixel  const color,
+             pixval const maxval) {
+
+    pixval const r = PPM_GETR(color);
+    pixval const g = PPM_GETG(color);
+    pixval const b = PPM_GETB(color);
+
+    return r <= maxval && g <= maxval && b <= maxval;
+}
+
+
+
+static void
+separateInvalidItems(colorhist_vector const chv,
+                     colorhist_vector const chvInvalid,
+                     pixval           const maxval,
+                     unsigned int     const colorCt,
+                     unsigned int  *  const validColorCtP) {
+/*----------------------------------------------------------------------------
+  Move invalid color entries from chv to chvInvalid.
+  Count how many color entries are valid. 
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    unsigned int validCt;
+    unsigned int invalidCt;
+ 
+    for (i = 0, validCt = 0, invalidCt = 0; i < colorCt; ++i) {
+        if (!colorIsValid(chv[i].color, maxval))
+            chvInvalid[invalidCt++] = chv[i];
+        else
+            chv[validCt++] = chv[i];
+    } 
+    *validColorCtP = validCt;
+}
+  
+
+
+static void
+sortHistogramForensic(enum sort        const sortFn,
+                      colorhist_vector const chv,
+                      colorhist_vector const chvInvalid,
+                      pixval           const maxval,
+                      unsigned int     const colorCt,
+                      unsigned int *   const validColorCtP) {
+
+    unsigned int validColorCt;
+
+    separateInvalidItems(chv, chvInvalid, maxval, colorCt, &validColorCt);
+
+    {
+        int (*compare_function)(const void *, const void *);
+    
+        switch (sortFn) {
+        case SORT_BY_FREQUENCY: compare_function = countcompare; break;
+        case SORT_BY_RGB:       compare_function = rgbcompare;   break;
+        }
+
+        qsort((void*) chv, validColorCt,
+              sizeof(struct colorhist_item), compare_function);
+        
+        qsort((void*) chvInvalid, colorCt - validColorCt,
+              sizeof(struct colorhist_item), compare_function);
+    }
+    *validColorCtP = validColorCt;
+}
+
+
+
+static void
+sortHistogramNormal(enum sort        const sortFn,
+                    colorhist_vector const chv,
+                    unsigned int     const colorCt) {
+
+    int (*compare_function)(const void *, const void *);
+
+    switch (sortFn) {
+    case SORT_BY_FREQUENCY: compare_function = countcompare; break;
+    case SORT_BY_RGB:       compare_function = rgbcompare;   break;
+    }
+
+    qsort((void*) chv, colorCt, sizeof(struct colorhist_item),
+          compare_function);
+}
+
+
+
 static const char *
-colornameLabel(pixel        const color, 
+colornameLabel(pixel        const color,
                pixval       const maxval,
                unsigned int const nDictColor,
                pixel        const dictColors[],
@@ -136,15 +307,15 @@ colornameLabel(pixel        const color,
    dictionary to it.  If the name returned is not the exact color,
    prefix it with "*".  Otherwise, prefix it with " ".
 
-   'nDictColor', dictColors[], and dictColorNames[] are the color 
+   'nDictColor', dictColors[], and dictColorNames[] are the color
    dictionary.
 
    Return the name in static storage within this subroutine.
 -----------------------------------------------------------------------------*/
     static char retval[32];
     int colorIndex;
-    
-    pixel color255;  
+
+    pixel color255;
         /* The color, normalized to a maxval of 255: the maxval of a color
            dictionary.
         */
@@ -154,31 +325,31 @@ colornameLabel(pixel        const color,
     colorIndex = ppm_findclosestcolor(dictColors, nDictColor, &color255);
 
     assert(colorIndex >= 0 && colorIndex < nDictColor);
-    
+
     if (PPM_EQUAL(dictColors[colorIndex], color))
         STRSCPY(retval, " ");
     else
         STRSCPY(retval, "*");
-    
+
     STRSCAT(retval, dictColornames[colorIndex]);
-    
+
     return retval;
 }
-                
+
 
 
 static void
-printColors(colorhist_vector const chv, 
-            int              const nColors,
+printColors(colorhist_vector const chv,
+            int              const colorCt,
             pixval           const maxval,
-            enum colorFmt    const colorFmt,
+            enum ColorFmt    const colorFmt,
             unsigned int     const nKnown,
             pixel            const knownColors[],
             const char *     const colornames[]) {
 
     int i;
 
-    for (i = 0; i < nColors; i++) {
+    for (i = 0; i < colorCt; i++) {
         pixval       const r          = PPM_GETR(chv[i].color);
         pixval       const g          = PPM_GETG(chv[i].color);
         pixval       const b          = PPM_GETB(chv[i].color);
@@ -190,7 +361,7 @@ printColors(colorhist_vector const chv,
         const char * colornameValue;
 
         if (colornames)
-            colornameValue = colornameLabel(chv[i].color, maxval, 
+            colornameValue = colornameLabel(chv[i].color, maxval,
                                             nKnown, knownColors, colornames);
         else
             colornameValue = "";
@@ -221,23 +392,97 @@ printColors(colorhist_vector const chv,
 
 
 
+static void
+summarizeInvalidPixels(unsigned long int const validPixelCt,
+                       unsigned long int const invalidPixelCt,
+                       pixval            const maxval) {
+/*----------------------------------------------------------------------------
+  Print total count of valid and invalid pixels, if there are any
+  invalid ones.
+-----------------------------------------------------------------------------*/
+    if (invalidPixelCt > 0) {
+        unsigned long int const totalPixelCt = validPixelCt + invalidPixelCt;
+
+        printf("\n");
+        printf("** Image stream contains invalid sample values "
+               "(above maxval %u)\n", maxval);
+        printf("** Valid sample values : %lu (%5.4g%%)\n",
+               validPixelCt,   (float) validPixelCt   / totalPixelCt * 100.0);
+        printf("** Invalid sample values : %lu (%5.4g%%)\n",
+               invalidPixelCt, (float) invalidPixelCt / totalPixelCt * 100.0);
+    }
+}
+
+
+
+static void
+printInvalidSamples(colorhist_vector const chv,
+                    colorhist_vector const chvInvalid,
+                    int              const totalColorCt,
+                    unsigned int     const validColorCt,
+                    pixval           const maxval,
+                    enum ColorFmt    const colorFmt) {
+
+    unsigned int const invalidColorCt = totalColorCt - validColorCt;
+
+    unsigned int i;
+    unsigned long int validPixelCt;
+    unsigned long int invalidPixelCt;
+
+    for (i = 0, validPixelCt = 0; i < validColorCt; ++i)
+        validPixelCt += chv[i].value; 
+
+    for (i = 0, invalidPixelCt = 0; i < invalidColorCt; ++i) {
+        pixval       const r     = PPM_GETR(chvInvalid[i].color);
+        pixval       const g     = PPM_GETG(chvInvalid[i].color);
+        pixval       const b     = PPM_GETB(chvInvalid[i].color);
+        unsigned int const count = chvInvalid[i].value;
+
+        invalidPixelCt += chvInvalid[i].value; 
+
+        switch(colorFmt) {
+        case FMT_FLOAT:
+            printf(" %1.3f %1.3f %1.3f\t\t%7d\n",
+                   (double)r / maxval,
+                   (double)g / maxval,
+                   (double)b / maxval,
+                   count);
+            break;
+        case FMT_HEX:
+            printf("  %04x  %04x  %04x\t\t%7d\n",
+                   r, g, b, count);
+            break;
+        case FMT_DECIMAL:
+            printf(" %5d %5d %5d\t\t%7d\n",
+                   r, g, b, count);
+            break;
+        case FMT_PPMPLAIN:
+            assert(false);
+            break;
+        }
+    }
+
+    summarizeInvalidPixels(validPixelCt, invalidPixelCt, maxval);
+}
+
+
+
 int
 main(int argc, const char *argv[]) {
 
     struct cmdline_info cmdline;
     FILE * ifP;
     colorhist_vector chv;
+    colorhist_vector chvInvalid;
     int rows, cols;
     pixval maxval;
+    pixval mmaxval;
     int format;
-    int nColors;
-    int (*compare_function)(const void *, const void *);
-        /* The compare function to be used with qsort() to sort the
-           histogram for output
-        */
-    unsigned int nDictColor;
+    int colorCt;
+    unsigned int dictColorCt;
     const char ** dictColornames;
     pixel * dictColors;
+    unsigned int validColorCt;
 
     pm_proginit(&argc, argv);
 
@@ -247,23 +492,29 @@ main(int argc, const char *argv[]) {
 
     ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
 
-    chv = ppm_computecolorhist2(ifP, cols, rows, maxval, format, 0, &nColors);
+    mmaxval = cmdline.forensic ? universalMaxval(maxval, format) : maxval;
+
+    chv = ppm_computecolorhist2(ifP, cols, rows, mmaxval, format, 0, &colorCt);
 
     pm_close(ifP);
 
-    switch (cmdline.sort) {
-    case SORT_BY_FREQUENCY:
-        compare_function = countcompare; break;
-    case SORT_BY_RGB:
-        compare_function = rgbcompare; break;
-    }
+    /* Sort and produce histogram. */
+    if (cmdline.forensic) {
+        MALLOCARRAY(chvInvalid, colorCt);
+        if (chvInvalid == NULL)
+            pm_error("out of memory generating histogram");
 
-    qsort((char*) chv, nColors, sizeof(struct colorhist_item), 
-          compare_function);
+        sortHistogramForensic(cmdline.sort, chv, chvInvalid,
+                              maxval, colorCt, &validColorCt);
+    } else {
+        chvInvalid = NULL;
+        sortHistogramNormal(cmdline.sort, chv, colorCt);
+        validColorCt = colorCt;
+    }
 
     /* And print the histogram. */
-    if (cmdline.colorFmt == FMT_PPMPLAIN) 
-        printf("P3\n# color map\n%d 1\n%d\n", nColors, maxval);
+    if (cmdline.colorFmt == FMT_PPMPLAIN)
+        printf("P3\n# color map\n%d 1\n%d\n", colorCt, maxval);
 
     if (!cmdline.noheader) {
         const char commentDelim = cmdline.colorFmt == FMT_PPMPLAIN ? '#' : ' ';
@@ -273,16 +524,20 @@ main(int argc, const char *argv[]) {
                commentDelim, cmdline.colorname ? "----" : "");
     }
     if (cmdline.colorname) {
-        bool mustOpenTrue = TRUE;
-        ppm_readcolordict(NULL, mustOpenTrue, 
-                          &nDictColor, &dictColornames, &dictColors, NULL);
+        bool const mustOpenTrue = TRUE;
+        ppm_readcolordict(NULL, mustOpenTrue,
+                          &dictColorCt, &dictColornames, &dictColors, NULL);
     } else {
         dictColors = NULL;
         dictColornames = NULL;
     }
-        
-    printColors(chv, nColors, maxval,
-                cmdline.colorFmt, nDictColor, dictColors, dictColornames);
+
+    printColors(chv, validColorCt, maxval,
+                cmdline.colorFmt, dictColorCt, dictColors, dictColornames);
+
+    if (colorCt > validColorCt)
+        printInvalidSamples(chv, chvInvalid, colorCt, validColorCt,
+                            maxval, cmdline.colorFmt);
 
     if (dictColors)
         free(dictColors);
@@ -291,5 +546,11 @@ main(int argc, const char *argv[]) {
 
     ppm_freecolorhist(chv);
 
+    if (chvInvalid)
+        ppm_freecolorhist(chvInvalid);
+
     return 0;
 }
+
+
+
diff --git a/buildtools/Makefile b/buildtools/Makefile
index 9c0ee778..8671c066 100644
--- a/buildtools/Makefile
+++ b/buildtools/Makefile
@@ -8,13 +8,9 @@ include $(BUILDDIR)/config.mk
 
 MERGE_OBJECTS =
 
-# These are programs that are used by the make files:
-PROGS = libopt typegen endiangen
+BUILDPROGS = libopt typegen endiangen
 
-all: $(PROGS)
-
-BINARIES =
-SCRIPTS =
+all: $(BUILDPROGS)
 
 OMIT_BUILDTOOL_RULE = 1
 include $(SRCDIR)/common.mk
@@ -30,16 +26,17 @@ endif
 libopt.o: libopt.c
 	$(CC_FOR_BUILD) -c -o $@ $(CFLAGS_FOR_BUILD) \
 	  -DSHLIBPREFIXLIST="\"$(SHLIBPREFIXLIST)\"" \
-	  $(STRIP_DLL_VERSION) $(EXPLICIT) $(CFLAGS_PERSONAL) $(CADD) \
+	  $(STRIP_DLL_VERSION) $(EXPLICIT) \
+	  $(CFLAGS_PERSONAL) $(CFLAGS) $(CADD) \
 	  $<
 
 typegen.o endiangen.o:%.o:%.c
 	$(CC_FOR_BUILD) -c -o $@ $(CFLAGS_FOR_BUILD) $<
 
-$(PROGS):%:%.o
+$(BUILDPROGS):%:%.o
 	$(LD_FOR_BUILD) -o $@ $(LDFLAGS_FOR_BUILD) $<
 
 distclean clean: cleanlocal
 .PHONY: cleanlocal
 cleanlocal:
-	rm -f $(PROGS)
+	rm -f $(BUILDPROGS)
diff --git a/buildtools/README.pkg b/buildtools/README.pkg
index e544cbb7..d642dee8 100644
--- a/buildtools/README.pkg
+++ b/buildtools/README.pkg
@@ -111,15 +111,26 @@ The parts to be installed are:
 
 
     One of the Netpbm programs is Manweb, which is designed to be a
-    replacement for the classic Man program that can access both
-    traditional man pages and worldwide web documentation in the
-    Netpbm style with the familiar 'man jpegtopnm' kind of command.
-    To set up your system for this, you will have to be sure to create
-    the /usr/man/web directory, with 'netpbm.url' in it.  Also, If you
-    install Manweb as 'man', there is no point to installing the
+    replacement for the classic Man program that can access both traditional
+    man pages and worldwide web documentation in the Netpbm style with the
+    familiar 'man jpegtopnm' kind of command.  The package contains the files
+    necessary to use Manweb to access Netpbm documention on the web (on
+    netpbm.sourceforge.net).  These files are the contents of the man/web
+    directory and the file 'bin/doc.url'.  You should install the
+    'bin/doc.url' file if you are installing the Netpbm executables in a
+    directory all their own.  You should install the 'man/web' files if you
+    are installing the Netpbm executables in some global directory such as
+    'usr/bin'.  You should install neither of these if you don't intend to use
+    Manweb.
+
+    If you install Manweb as 'man', there is no point to installing the
     pointer man pages -- they will never be used.
-    
 
+    If you want to use Manweb to view the Netpbm manual but use a local
+    copy of the manual instead of accessing it on the web, you can get the
+    HTML files from the Sourceforge Subversion repository and install those
+    instead of the .url files that appear in the 'man/web' directory of the
+    package.
 
 The instructions above suggest putting the Netpbm parts in common
 directories such as /usr/bin, mingled with other packages.  This is
@@ -134,21 +145,34 @@ entire installation when you don't want it anymore, and to keep
 multiple versions around.
 
 
-netpbm.config
+netpbm.pc
+---------
+
+You should create a file named 'netpbm.pc' out of the template file
+'pkgconfig_template' in the package directory, and install netpbm.pc in
+your pkg-config directory (typically /usr/lib/pkgconfig).  Programs that want
+to find out where you installed some part of Netpbm can use a 'pkg-config
+netpbm ...' command to find out.  For example, a make file for a program that
+uses the Netpbm programming library might use this facility to generate the
+necessary compiler and linker options to access that library.
+
+The pkg-config facility is fairly widely used, especially with things related
+to the X Window System.
+
+
+netpbm-config
 -------------
 
-You should create a shell script named 'netpbm.config' out of the
-template file 'config_template' in the package directory, and install
-netpbm.config in your executable search path.  Programs that want to
-find out where you installed some part of Netpbm can invoke
-netpbm.config and it will tell them.  For example, a make file for a
-program that uses the Netpbm programming library might use
-netpbm.config to generate the necessary compiler and linker options to
-access that library.
-
-Using netpbm.config, it's possible to have a viable Netpbm
-installation where netpbm.config is the only file in any default
-search path.
-
-The xxx.config file concept is a relatively new but growing convention,
-seen mostly in software related to the X Window System.
+You should create a shell script named 'netpbm-config' out of the template
+file 'config_template' in the package directory, and install netpbm-config in
+your executable search path.  Programs that want to find out where you
+installed some part of Netpbm can invoke netpbm-config and it will tell them.
+For example, a make file for a program that uses the Netpbm programming
+library might use netpbm-config to generate the necessary compiler and linker
+options to access that library.
+
+Using netpbm-config, it's possible to have a viable Netpbm installation where
+netpbm-config is the only file in any default search path.
+
+The xxx-config concept (in general, not just Netpbm) has largely been replaced
+by the pkg-config concept (see netpbm.pc above).
diff --git a/buildtools/configure.pl b/buildtools/configure.pl
index 21f11bf3..9cafd019 100755
--- a/buildtools/configure.pl
+++ b/buildtools/configure.pl
@@ -42,6 +42,11 @@ my ($TRUE, $FALSE) = (1,0);
 
 my $testCc;
 
+# $warned is logical.  It means we have issued a warning to Standard Output.
+
+my $warned;
+
+
 ##############################################################################
 #
 #  Implementation note:
@@ -93,6 +98,97 @@ sub prompt($$) {
 
 
 
+sub promptYesNo($) {
+    my ($default) = @_;
+
+    my $retval;
+
+    while (!defined($retval)) {
+        my $response = prompt("(y)es or (n)o", $default);
+        
+        if (uc($response) =~ m{ ^ (Y|YES) $ }x)  {
+            $retval = $TRUE;
+        } elsif (uc($response) =~ m{ ^ (N|NO) $ }x)  {
+            $retval = $FALSE;
+        } else {
+            print("'$response' isn't one of the choices.  \n" .
+                  "You must choose 'yes' or 'no' (or 'y' or 'n').\n");
+        }
+    }
+
+    return $retval;
+}
+
+
+
+sub flow($) {
+    my ($unflowed) = @_;
+#-----------------------------------------------------------------------------
+#  Return the text $unflowed, split with newlines into 72 character lines.
+#  We assum $unflowed is pure text, without any kind of formatting characters
+#  such as newlines.
+#-----------------------------------------------------------------------------
+    my $retval;
+
+    my @words = split(m{\s+}, $unflowed);
+    
+    my $currentLine;
+    
+    $currentLine = "";
+    
+    foreach my $word (@words) {
+        my $mustSpill;
+        if (length($currentLine) == 0) {
+            $currentLine = $word;
+            $mustSpill = $FALSE;
+        } else {
+            my $separator;
+            if (substr($currentLine, -1) eq '.') {
+                $separator = "  ";
+            } else {
+                $separator = " ";
+            }
+                
+            if (length($currentLine) + length($separator) + length($word)
+                <= 72) {
+                $currentLine .= $separator;
+                $currentLine .= $word;
+                $mustSpill = $FALSE;
+            } else {
+                $mustSpill = $TRUE;
+            }
+        }
+        if ($mustSpill) {
+            $retval .= $currentLine;
+            $retval .= "\n";
+            $currentLine = $word;
+        }
+    }
+
+    $retval .= $currentLine;
+    $retval .= "\n";
+
+    return $retval;
+}
+
+
+
+sub warnUser($) {
+    my ($warning) = @_;
+#-----------------------------------------------------------------------------
+#  Warn the user the build might fail, with explanation "$warning".
+#-----------------------------------------------------------------------------
+    print("************************************" .
+          "************************************\n");
+    print flow("WARNING: $warning");
+    print("************************************" .
+          "************************************\n");
+
+    $warned = $TRUE;
+}
+
+
+
 sub tmpdir() {
 # This is our approximation of File::Spec->tmpdir(), which became part of
 # basic Perl some time after Perl 5.005_03.
@@ -257,7 +353,7 @@ sub testCompile($$$) {
 sub testCompileLink($$$) {
     my ($flags, $cSourceCodeR, $successR) = @_;
 #-----------------------------------------------------------------------------
-#  Do a test compile of the program in @{$cSourceCodeR}.
+#  Do a test compile and link of the program in @{$cSourceCodeR}.
 #  
 #  Return $$successR == $TRUE iff the compile succeeds (exit code 0).
 #-----------------------------------------------------------------------------
@@ -322,20 +418,8 @@ sub askAboutCygwin() {
         $default = "n";
     }
     
-    my $retval;
+    my $retval = promptYesNo($default);
 
-    while (!defined($retval)) {
-        my $response = prompt("(y)es or (n)o", $default);
-    
-        if (uc($response) =~ /^(Y|YES)$/)  {
-            $retval = $TRUE;
-        } elsif (uc($response) =~ /^(N|NO)$/)  {
-            $retval = $FALSE;
-        } else {
-            print("'$response' isn't one of the choices.  \n" .
-                  "You must choose 'yes' or 'no' (or 'y' or 'n').\n");
-        }
-    }
     return $retval;
 }
 
@@ -353,20 +437,7 @@ sub askAboutDjgpp() {
         $default = "n";
     }
     
-    my $retval;
-
-    while (!defined($retval)) {
-        my $response = prompt("(y)es or (n)o", $default);
-    
-        if (uc($response) =~ /^(Y|YES)$/)  {
-            $retval = $TRUE;
-        } elsif (uc($response) =~ /^(N|NO)$/)  {
-            $retval = $FALSE;
-        } else {
-            print("'$response' isn't one of the choices.  \n" .
-                  "You must choose 'yes' or 'no' (or 'y' or 'n').\n");
-        }
-    }
+    my $retval = promptYesNo($default);
 }
 
 
@@ -407,10 +478,10 @@ sub getPlatform() {
     print("Which of the following best describes your platform?\n");
  
     print("gnu      GNU/Linux\n");
+    print("win      Windows/DOS (Cygwin, DJGPP, Mingw32)\n");
     print("sun      Solaris or SunOS\n");
     print("hp       HP-UX\n");
     print("aix      AIX\n");
-    print("win      Windows/DOS (Cygwin, DJGPP, Mingw32)\n");
     print("tru64    Tru64\n");
     print("irix     Irix\n");
     print("bsd      NetBSD, BSD/OS\n");
@@ -485,6 +556,49 @@ sub getPlatform() {
 
 
 
+sub getGccChoiceFromUser($) {
+    my ($platform) = @_;
+
+    my $retval;
+
+    print("GNU compiler or native operating system compiler (cc)?\n");
+    print("\n");
+
+    my $default;
+
+    if ($platform eq "SOLARIS" || $platform eq "SCO" ) {
+        $default = "gcc";
+    } else {
+        $default = "cc";
+    }
+
+    while (!defined($retval)) {
+        my $response = prompt("gcc or cc", $default);
+
+        if ($response eq "gcc") {
+            $retval = "gcc";
+        } elsif ($response eq "cc") {
+            $retval = "cc";
+        } else {
+            print("'$response' isn't one of the choices.  \n" .
+                  "You must choose 'gcc' or 'cc'.\n");
+        }
+    }
+    if ($retval eq 'gcc' && !commandExists('gcc')) {
+        warnUser("WARNING: You selected the GNU compiler, " .
+                 "but do not appear to have a program " .
+                 "named 'gcc' in your PATH.  This may " .
+                 "cause trouble later.  You may need to " .
+                 "set the CC environment variable or CC " .
+                 "makefile variable or install 'gcc'");
+    }
+    print("\n");
+
+    return $retval;
+}
+
+
+
 sub getCompiler($$$) {
     my ($platform, $subplatform, $compilerR) = @_;
 #-----------------------------------------------------------------------------
@@ -529,45 +643,23 @@ sub getCompiler($$$) {
                             "DARWIN"  => 1,
                             );
 
-    if ($gccUsualPlatform{$platform}) {
-        $$compilerR = "gcc";
-    } elsif ($platform eq "WINDOWS" && $subplatform eq "cygwin") {
-        $$compilerR = "gcc";
-    } elsif ($gccOptionalPlatform{$platform}) {
-        print("GNU compiler or native operating system compiler (cc)?\n");
-        print("\n");
-
-        my $default;
+    if (commandExists('x86_64-w64-mingw32-gcc')) {
+        printf("Do you want to use the Mingw-w64 Cross-Compiler?\n");
 
-        if ($platform eq "SOLARIS" || $platform eq "SCO" ) {
-            $default = "gcc";
-        } else {
-            $default = "cc";
+        if (promptYesNo('y') eq "y") {
+            $$compilerR = 'x86_64-w64-mingw32-gcc';
         }
-
-        my $response = prompt("gcc or cc", $default);
-
-        if ($response eq "gcc") {
+    }
+    if (!defined($$compilerR)) {
+        if ($gccUsualPlatform{$platform}) {
             $$compilerR = "gcc";
-        } elsif ($response eq "cc") {
-            $$compilerR = "cc";
+        } elsif ($platform eq "WINDOWS" && $subplatform eq "cygwin") {
+            $$compilerR = "gcc";
+        } elsif ($gccOptionalPlatform{$platform}) {
+            $$compilerR = getGccChoiceFromUser($platform);
         } else {
-            print("'$response' isn't one of the choices.  \n" .
-                  "You must choose 'gcc' or 'cc'.\n");
-            exit 12;
+            $$compilerR = 'cc';
         }
-
-        if ($$compilerR eq 'gcc' && !commandExists('gcc')) {
-            print("WARNING: You selected the GNU compiler, " .
-                  "but do not appear to have a program " .
-                  "named 'gcc' in your PATH.  This may " .
-                  "cause trouble later.  You may need to " .
-                  "set the CC environment variable or CC " .
-                  "makefile variable or install 'gcc'\n");
-        }
-        print("\n");
-    } else {
-        $$compilerR = 'cc';
     }
 }
 
@@ -743,16 +835,16 @@ sub getLibTypes($$$$$$$$) {
         my $response = prompt("(y)es or (n)o", $default);
         
         if (uc($response) =~ /^(Y|YES)$/)  {
-            $staticlib_too = "y";
+            $staticlib_too = "Y";
         } elsif (uc($response) =~ /^(N|NO)$/)  {
-            $staticlib_too = "n";
+            $staticlib_too = "N";
         } else {
             print("'$response' isn't one of the choices.  \n" .
               "You must choose 'yes' or 'no' (or 'y' or 'n').\n");
             exit 12;
         }
     } else {
-        $staticlib_too = "n";
+        $staticlib_too = "N";
     }
     print("\n");
 
@@ -855,6 +947,7 @@ sub getInttypes($) {
 }
 
 
+
 sub getInt64($$) {
 
     my ($inttypes_h, $haveInt64R) = @_;
@@ -890,6 +983,85 @@ sub getInt64($$) {
 
 
 
+sub determineSseCapability($) {
+
+    my ($haveEmmintrinR) = @_;
+
+    if (defined($testCc)) {
+
+        print("(Doing test compiles to determine if your compiler has SSE " .
+              "intrinsics -- ignore errors)\n");
+
+        my $cflags = testCflags();
+
+        my $works;
+
+        my @cSourceCode = (
+                           "#include <emmintrin.h>\n",
+                           );
+            
+        testCompile($cflags, \@cSourceCode, \my $success);
+            
+        if ($success) {
+            print("It does.\n");
+            $$haveEmmintrinR = $TRUE;
+        } else {
+            print("It does not.  Programs will not exploit fast SSE " .
+                  "instructions.\n");
+            $$haveEmmintrinR = $FALSE;
+        }
+        print("\n");
+    } else {
+        # We conservatively estimate the facility isn't there
+        $$haveEmmintrinR = $FALSE;
+    }
+}
+
+
+
+sub getSse($) {
+
+    my ($wantSseR) = @_;
+
+    determineSseCapability(\my $haveEmmintrin);
+
+    my $gotit;
+
+    print("Use SSE instructions?\n");
+    print("\n");
+
+    my $default = $haveEmmintrin ? "y" : "n";
+
+    $$wantSseR = promptYesNo($default);
+
+    # Another complication in the SSE world is that GNU compiler options
+    # -msse, -msse2, and -march=xxx affect whether the compiler can or will
+    # generate the instructions.  When compiling for older processors, the
+    # default for these options is negative ; for newer processors, it is
+    # affirmative.  -[no]msse2 determines whether macro __SSE2__ macro is
+    # defined.  If it is not, #include <emmintrins.h> fails (<emmintrins.h>
+    # checks __SSE2__.
+
+    # The Netpbm build does not mess with these compiler options.  If the
+    # user wants something other than the default, he can put it in CFLAGS
+    # in config.mk manually or on the make command line on in CFLAGS_PERSONAL.
+}
+
+
+sub getIcon($$) {
+
+    my ($platform, $wantIconR) = @_;
+
+    if ($platform eq 'WINDOWS') {
+        print("Include an icon in each executable?\n");
+        $$wantIconR = promptYesNo("y");
+    } else {
+        $$wantIconR = $FALSE;
+    }
+}
+
+
+
 # TODO: These should do test compiles to see if the headers are in the
 # default search path, both to create a default to offer and to issue a
 # warning after user has chosen.  Also test links to test the link library.
@@ -915,7 +1087,7 @@ sub getTiffLibrary($@) {
             $tifflib = $response;
         }
         if (defined($tifflib) and $tifflib =~ m{/} and !-f($tifflib)) {
-            printf("WARNING: No regular file named '$tifflib' exists.\n");
+            warnUser("No regular file named '$tifflib' exists.");
         }
     }
     my $tiffhdr_dir;
@@ -937,7 +1109,7 @@ sub getTiffLibrary($@) {
             $tiffhdr_dir = $response;
         }
         if (defined($tiffhdr_dir) and !-d($tiffhdr_dir)) {
-            printf("WARNING: No directory named '$tiffhdr_dir' exists.\n");
+            warnUser("No directory named '$tiffhdr_dir' exists.");
         }
     }
     return($tifflib, $tiffhdr_dir);
@@ -978,7 +1150,7 @@ sub getJpegLibrary($@) {
             $jpeghdr_dir = $response;
         }
         if (defined($jpeghdr_dir) and !-d($jpeghdr_dir)) {
-            printf("WARNING: No directory named '$jpeghdr_dir' exists.\n");
+            warnUser("No directory named '$jpeghdr_dir' exists.");
         }
     }
     return($jpeglib, $jpeghdr_dir);
@@ -1075,43 +1247,49 @@ sub getX11Library($@) {
     my ($platform, @suggestedHdrDir) = @_;
 
     my $x11lib;
-    {
-        my $default;
-
-        if (-d('/usr/link/X11')) {
-            $default = '/usr/link/X11/libX11' . libSuffix($platform);
-        } elsif (-d('/usr/X11R6/lib')) {
-            $default = '/usr/X11R6/lib/libX11' . libSuffix($platform);
-        } else {
-            $default = "libX11" . libSuffix($platform);
-        }
-        print("What is your X11 (X client) library?\n");
-        
-        my $response = prompt("library filename or 'none'", $default);
-        
-        if ($response ne "none") {
-            $x11lib = $response;
-        }
-    }
     my $x11hdr_dir;
-    if (defined($x11lib)) {
-        my $default;
 
-        $default = "default";
+    if (system('pkg-config x11 --exists') == 0) {
+        # We don't need to ask where X libraries are; pkg-config knows and the
+        # make files will use that.
+    } else {
+        {
+            my $default;
 
-        print("Where are the interface headers for it?\n");
-        
-        my $response = prompt("X11 header directory", $default);
-        
-        if ($response ne "default") {
-            $x11hdr_dir = $response;
+            if (-d('/usr/link/X11')) {
+                $default = '/usr/link/X11/libX11' . libSuffix($platform);
+            } elsif (-d('/usr/X11R6/lib')) {
+                $default = '/usr/X11R6/lib/libX11' . libSuffix($platform);
+            } else {
+                $default = "libX11" . libSuffix($platform);
+            }
+            print("What is your X11 (X client) library?\n");
+            
+            my $response = prompt("library filename or 'none'", $default);
+            
+            if ($response ne "none") {
+                $x11lib = $response;
+            }
         }
-        if (defined($x11hdr_dir)) {
-            if (!-d($x11hdr_dir)) {
-                printf("WARNING: No directory named '$x11hdr_dir' exists.\n");
-            } elsif (!-d("$x11hdr_dir/X11")) {
-                printf("WARNING: Directory '$x11hdr_dir' does not contain " .
-                       "the requisite 'X11' subdirectory\n");
+        if (defined($x11lib)) {
+            my $default;
+
+            $default = "default";
+
+            print("Where are the interface headers for it?\n");
+            
+            my $response = prompt("X11 header directory", $default);
+            
+            if ($response ne "default") {
+                $x11hdr_dir = $response;
+            }
+            if (defined($x11hdr_dir)) {
+                if (!-d($x11hdr_dir)) {
+                    warnUser("No directory named '$x11hdr_dir' exists.");
+                } elsif (!-d("$x11hdr_dir/X11")) {
+                    warnUser("Directory '$x11hdr_dir' does not contain " .
+                             "the requisite 'X11' subdirectory");
+                }
             }
         }
     }
@@ -1137,6 +1315,8 @@ sub getLinuxsvgaLibrary($@) {
             # &>/dev/null should work above, but on 12.03.26, it caused the
             # return value of system() always to be zero!
             $default = 'libvga.so';
+        } elsif (-f('/usr/lib/libvga.a')) {
+            $default = '/usr/lib/libvga.a';
         } else {
             $default = 'none';
         }
@@ -1166,8 +1346,7 @@ sub getLinuxsvgaLibrary($@) {
         }
         if (defined($svgalibhdr_dir)) {
             if (!-d($svgalibhdr_dir)) {
-                printf("WARNING: No directory named " .
-                       "'$svgalibhdr_dir' exists.\n");
+                warnUser("No directory named '$svgalibhdr_dir' exists.");
             }
         }
     }
@@ -1283,7 +1462,8 @@ sub gnuCflags($) {
     return("CFLAGS = " . gnuOptimizeOpt($gccCommandName) . " -ffast-math " .
            " -pedantic -fno-common " . 
            "-Wall -Wno-uninitialized -Wmissing-declarations -Wimplicit " .
-           "-Wwrite-strings -Wmissing-prototypes -Wundef\n");
+           "-Wwrite-strings -Wmissing-prototypes -Wundef " .
+           "-Wno-unknown-pragmas\n");
 }
 
 
@@ -1332,10 +1512,10 @@ sub validateLibraries(@) {
     foreach my $libname (@libList) {
         if (defined($libname)) {
             if ($libname =~ m{/} and !-f($libname)) {
-                print("WARNING: No regular file named '$libname' exists.\n");
+                warnUser("No regular file named '$libname' exists.");
             } elsif (!($libname =~ m{ .* \. (so|a|sa|sl|dll|dylib) $ }x)) {
-                print("WARNING: The library name '$libname' does not have " .
-                      "a conventional suffix (.so, .a, .dll, etc.)\n");
+                warnUser("The library name '$libname' does not have " .
+                         "a conventional suffix (.so, .a, .dll, etc.)");
             }
         }
     }
@@ -1347,16 +1527,12 @@ sub warnJpegTiffDependency($$) {
     my ($jpeglib, $tifflib) = @_;
 
     if (defined($tifflib) && !defined($jpeglib)) {
-        print("WARNING: You say you have a Tiff library, " .
-              "but no Jpeg library.\n");
-        print("Sometimes the Tiff library prerequires the " .
-              "Jpeg library.  If \n");
-        print("that is the case on your system, you will " .
-              "have some links fail with\n");
-        print("missing 'jpeg...' symbols.  If so, rerun " .
-              "Configure and say you\n");
-        print("have no Tiff library either.\n");
-        print("\n");
+        warnUser("You say you have a Tiff library, but no Jpeg library.  " .
+                 "Sometimes the Tiff library prerequires the " .
+                 "Jpeg library.  If that is the case on your system, " .
+                 "you will have some links fail with " .
+                 "missing 'jpeg...' symbols.  If so, rerun " .
+                 "Configure and say you have no Tiff library either.");
     }
 }
 
@@ -1401,41 +1577,37 @@ sub printMissingHdrWarning($$) {
 
     my ($name, $hdr_dir) = @_;
 
-    print("WARNING: You said the compile-time part of the $name library " .
-          "(the header\n");
-    print("files) is in ");
+    warnUser("You said the compile-time part of the $name library " .
+             "(the header files) is in " .
+             
+             (defined($hdr_dir) ?
+              "directory '$hdr_dir', " :
+              "the compiler's default search path, ") .
 
-    if (defined($hdr_dir)) {
-        print("directory '$hdr_dir', ");
-    } else {
-        print("the compiler's default search path, ");
-    }
-    print("but a test compile failed\n");
-    print("to confirm that.  If your configuration is exotic, the test " .
-          "compile might\n");
-    print("just be wrong, but otherwise the Netpbm build will fail.\n");
-    print("\n");
-    print("To fix this, either install the $name library there \n");
-    print("or re-run Configure and answer the question about the $name " .
-          "library\n");
-    print("differently.\n");
-    print("\n");
+             "but a test compile failed to confirm that.  " .
+             "If your configuration is exotic, the test compile might" .
+             "just be wrong, but otherwise the Netpbm build will fail.  " .
+             "To fix this, either install the $name library there " .
+             "or re-run Configure and answer the question about the $name " .
+             "library differently."
+        );
 }
 
 
 
 sub printOldJpegWarning() {
-    print("WARNING: Your JPEG library appears to be too old for Netpbm.\n");
-    print("We base this conclusion on the fact that jpeglib.h apparently\n");
-    print("does not define struct jpeg_marker_struct.\n");
-    print("If the JPEG library is not Independent Jpeg Group's Version 6b\n");
-    print("or better, the Netpbm build will fail when it attempts to build\n");
-    print("the parts that use the JPEG library.\n");
-    print("\n");
-    print("If your configuration is exotic, this test may just be wrong.\n");
-    print("Otherwise, either upgrade your JPEG library or re-run Configure\n");
-    print("and say you don't have a JPEG library.\n");
-    print("\n");
+    warnUser("Your JPEG library appears to be too old for Netpbm.  " .
+             "We base this conclusion on the fact that jpeglib.h apparently" .
+             "does not define struct jpeg_marker_struct.  " .
+             "If the JPEG library is not " .
+             "Independent Jpeg Group's Version 6b" .
+             "or better, the Netpbm build will fail when it attempts " .
+             "to build the parts that use the JPEG library.  " .
+             "If your configuration is exotic, " .
+             "this test may just be wrong.  " .
+             "Otherwise, either upgrade your JPEG library " .
+             "or re-run Configure and say you don't have a JPEG library."
+        );
 }
 
 
@@ -1470,6 +1642,56 @@ sub testJpegHdr($) {
 
 
 
+sub warnJpegNotInDefaultPath($) {
+    my ($jpegLib) = @_;
+
+    print("You said your JPEG library is '$jpegLib', which says it is "
+          . "in the linker's default search path, but a test link we did "
+          . "failed as if it is not.  If it isn't, the build will fail\n");
+}
+
+
+
+sub testJpegLink($) {
+    my ($jpegLib) = @_;
+#-----------------------------------------------------------------------------
+#  See if we can link the JPEG library with the information user gave us.
+#  $jpegLib is the answer to the "what is your JPEG library" prompt, so
+#  it is either a library file name such as "libjpeg.so", in the linker's
+#  default search path, or it is an absolute path name such as
+#  "/usr/jpeg/lib/libjpeg.so".
+#
+#  We actually test only the default search path case, since users often
+#  incorrectly specify that by taking the default.
+#-----------------------------------------------------------------------------
+    if ($jpegLib =~ m{( lib | cyg ) (.+) \. ( so | a )$}x) {
+        my $libName = $2;
+
+        # It's like "libjpeg.so", so is a library in the default search path.
+        # $libName is "jpeg" in this example.
+
+        # First we test our test tool.  We can do this only with GCC.
+
+        my @emptySource;
+
+        testCompileLink('-nostartfiles', \@emptySource, \my $controlWorked);
+
+        if ($controlWorked) {
+            # The "control" case worked.  Now see if it still works when we add
+            # the JPEG library.
+
+            testCompileLink("-nostartfiles -l$libName", \@emptySource,
+                            \my $workedWithJpeg);
+
+            if (!$workedWithJpeg) {
+                warnJpegNotInDefaultPath($jpegLib);
+            }
+        }
+    }
+}
+
+
+
 sub testCompileZlibH($$) {
     my ($cflags, $successR) = @_;
 #-----------------------------------------------------------------------------
@@ -1531,38 +1753,47 @@ sub testPngHdr($$) {
 
 
 
-sub printBadPngConfigLdflagsWarning($) {
-    my ($pngLdFlags) = @_;
-
-    print << 'EOF';
-WARNING: 'libpng-config' in this environment (a program in your PATH)
-gives instructions that don't work for linking with the PNG library.
-Our test link failed.
-
-This indicates Libpng is installed incorrectly on this system.  If so,
-your Netpbm build, which uses 'libpng-config', will fail.  But it
-might also just be our test that is broken.
-
-EOF
+sub printBadPngConfigCflagsWarning($) {
+    my ($pngCFlags) = @_;
 
+    warnUser("'libpng-config' in this environment (a program in your PATH) " .
+             "gives instructions that don't work for compiling for " .
+             "(not linking with) the PNG library.  " .
+             "Our test compile failed.  " .
+             "This indicates Libpng is installed incorrectly " .
+             "on this system.  If so, your Netpbm build, " .
+             "which uses 'libpng-config', will fail.  " .
+             "But it might also just be our test that is broken.");
 }
 
 
 
-sub printBadPngConfigCflagsWarning($) {
-    my ($pngCFlags) = @_;
+sub pngLinkWorksWithLzLmMsg() {
 
-    print << 'EOF';
-WARNING: 'libpng-config' in this environment (a program in your PATH)
-gives instructions that don't work for compiling for the PNG library.
-Our test compile failed.
+    return
+        "When we added \"-lz -lm\" to the linker flags, the link worked.  " .
+        "That means the fix for this may be to modify 'libpng-config' " .
+        "so that 'libpng-config --ldflags' includes \"-lz -lm\" " .
+        "in its output.  But the right fix may actually " .
+        "be to build libpng differently so that it specifies its dependency " .
+        "on those libraries, or to put those libraries in a different " .
+        "place, or to create missing symbolic links.";
+}
 
-This indicates Libpng is installed incorrectly on this system.  If so,
-your Netpbm build, which uses 'libpng-config', will fail.  But it
-might also just be our test that is broken.
 
-EOF
 
+sub printBadPngConfigLdflagsWarning($$) {
+    my ($pngLdFlags, $lzLmSuccess) = @_;
+
+    warnUser(
+        "'libpng-config' in this environment (a program in your PATH) " .
+        "gives instructions that don't work for linking " .
+        "with the PNG library.  Our test link failed.  " .
+        "This indicates Libpng is installed incorrectly on this system.  " .
+        "If so, your Netpbm build, which uses 'libpng-config', will fail.  " .
+        "But it might also just be our test that is broken.  " .
+        ($lzLmSuccess ? pngLinkWorksWithLzLmMsg : "")
+        );
 }
 
 
@@ -1588,7 +1819,10 @@ sub testLinkPnglib($$) {
                         \@cSourceCode, \my $success);
         
         if (!$success) {
-            printBadPngConfigLdflagsWarning($pngLdflags);
+            testCompileLink("$generalCflags $pngCflags $pngLdflags -lz -lm",
+                        \@cSourceCode, \my $lzLmSuccess);
+
+            printBadPngConfigLdflagsWarning($pngLdflags, $lzLmSuccess);
         }
     }
 }
@@ -1633,20 +1867,15 @@ sub testCompileXmlreaderH($$) {
 sub printBadXml2CFlagsWarning($) {
     my ($xml2CFlags) = @_;
 
-    print << 'EOF';
-
-WARNING: 'xml2-config' in this environment (a program in your PATH)
-gives instructions that don't work for compiling for the Libxml2 library.
-Our test compile failed.
-
-This indicates Libxml2 is installed incorrectly on this system.  If so,
-your Netpbm build, which uses 'xml2-config', will fail.  But it
-might also just be our test that is broken.
-
-'xml2-config' says to use compiler options '$xml2CFlags'.
-
-EOF
-
+    warnUser(
+      "'xml2-config' in this environment (a program in your PATH) " .
+        "gives instructions that don't work for compiling for the " .
+        "Libxml2 library.  Our test compile failed.  " .
+        "This indicates Libxml2 is installed incorrectly on this system.  " .
+        "If so, your Netpbm build, which uses 'xml2-config', will fail.  " .
+        "But it might also just be our test that is broken.  ".
+        "'xml2-config' says to use compiler options '$xml2CFlags'.  "
+        );
 }
 
 
@@ -1669,26 +1898,24 @@ sub testCompileXmlReaderTypes($$) {
 
 sub printMissingXmlReaderTypesWarning() {
 
-    print << 'EOF';
-
-WARNING: Your libxml2 interface header file does not define the type
-'xmlReaderTypes', which Netpbm needs.  In order to build Netpbm successfully,
-you must install a more recent version of Libxml2.
-
-EOF
+    warnUser(
+        "Your libxml2 interface header file does not define the type " .
+        "'xmlReaderTypes', which Netpbm needs.  " .
+        "In order to build Netpbm successfully, " .
+        "you must install a more recent version of Libxml2.  "
+        );
 }
 
 
 
 sub printNoLibxml2Warning() {
 
-    print << 'EOF';
-
-WARNING: You appear not to have Libxml2 installed ('xml2-config' does not
-exist in your program search PATH).  If this is the case at build time,
-the build will skip building 'svgtopam'.
-
-EOF
+    warnUser(
+        "You appear not to have Libxml2 installed ('xml2-config' does not " .
+        "exist in your program search PATH).  " .
+        "If this is the case at build time, " .
+        "the build will skip building 'svgtopam'."
+        );
 }
 
 
@@ -1731,6 +1958,8 @@ sub testConfiguration($$$$$$) {
 
     if (defined($jpeglib)) {
         testJpegHdr($jpeghdr_dir);
+
+        testJpegLink($jpeglib);
     }
     if (defined($pnglib) && defined($zlib)) {
         testPngHdr($pnghdr_dir, $zhdr_dir);
@@ -1862,41 +2091,37 @@ getInttypes(\my $inttypesHeaderFile);
 
 getInt64($inttypesHeaderFile, \my $haveInt64);
 
+getSse(\my $wantSse);
+
 findProcessManagement(\my $dontHaveProcessMgmt);
 
+getIcon($platform, \my $wantIcon);
+
 #******************************************************************************
 #
 #  FIND THE PREREQUISITE LIBRARIES
 #
 #*****************************************************************************
 
-print("\n\n");
-print("The following questions concern the subroutine libraries that are " .
-      "Netpbm\n");
-print("prerequisites.  Every library has a compile-time part (header " .
-      "files)\n");
-print("and a link-time part.  In the case of a shared library, these are " .
-      "both\n");
-print("part of the \"development\" component of the library, which may be " .
-      "separately\n");
-print("installable from the runtime shared library.  For each library, you " .
-      "must\n");
-print("give the filename of the link library.  If it is not in your " .
-      "linker's\n");
-print("default search path, give the absolute pathname of the file.  In " .
-      "addition,\n");
-print("you will be asked for the directory in which the library's interface " .
-      "headers\n");
-print("reside, and you can respond 'default' if they are in your compiler's " .
-      "default\n");
-print("search path.\n");
-print("\n");
-print("If you don't have the library on your system, you can enter 'none' " .
-      "as the\n");
-print("library filename and the builder will skip any part that requires " .
-      "that ");
-print("library.\n");
-print("\n");
+print << 'EOF';
+
+
+The following questions concern the subroutine libraries that are Netpbm
+prerequisites.  Every library has a compile-time part (header files)
+and a link-time part.  In the case of a shared library, these are both
+part of the "development" component of the library, which may be separately
+installable from the runtime shared library.  For each library, you must
+give the filename of the link library.  If it is not in your linker's
+default search path, give the absolute pathname of the file.  In addition,
+you will be asked for the directory in which the library's interface headers
+reside, and you can respond 'default' if they are in your compiler's default
+search path.
+
+If you don't have the library on your system, you can enter 'none' as the
+library filename and the builder will skip any part that requires that
+library.
+
+EOF
 
 my ($jpeglib, $jpeghdr_dir) = getJpegLibrary($platform);
 print("\n");
@@ -2131,7 +2356,7 @@ if ($platform eq "GNU") {
     # only Ppmtompeg and it isn't clear that using long instead of int is
     # ever right anyway.
 
-    push(@config_mk, "OMIT_NETWORK = y\n");
+    push(@config_mk, "OMIT_NETWORK = Y\n");
     push(@config_mk, "LINKER_CAN_DO_EXPLICIT_LIBRARY=Y\n");
 } elsif ($platform eq "IRIX") {
 #    push(@config_mk, "INSTALL = install\n");
@@ -2145,7 +2370,7 @@ if ($platform eq "GNU") {
         makeCompilerGcc(\@config_mk);
     }
     push(@config_mk, "EXE = .exe\n");
-    push(@config_mk, "OMIT_NETWORK = y\n");
+    push(@config_mk, "OMIT_NETWORK = Y\n");
 #    # Though it may not have the link as "ginstall", "install" in a Windows
 #    # Unix environment is usually GNU install.
 #    my $ginstall_result = `ginstall --version 2>/dev/null`;
@@ -2160,6 +2385,9 @@ if ($platform eq "GNU") {
     if ($subplatform ne "cygwin") {
         push(@config_mk, "MSVCRT = Y\n");
     }
+    if ($wantIcon) {
+        push(@config_mk, 'WINICON_OBJECT = $(BUILDDIR)/icon/netpbm.o', "\n");
+    }
 } elsif ($platform eq "BEOS") {
     push(@config_mk, "LDSHLIB = -nostart\n");
 } elsif ($platform eq "OPENBSD") {
@@ -2296,6 +2524,10 @@ if ($haveInt64 ne 'Y') {
     push(@config_mk, "HAVE_INT64 = $haveInt64\n");
 }
 
+if ($wantSse) {
+    push(@config_mk, "WANT_SSE = Y\n");
+}
+
 if ($dontHaveProcessMgmt) {
     push(@config_mk, "DONT_HAVE_PROCESS_MGMT = Y\n");
 }
@@ -2322,6 +2554,9 @@ print("make.\n");
 print("\n");
 
 print("Now you may proceed with 'make'\n");
+if ($warned) {
+    print("BUT: per the previous WARNINGs, don't be surprised if it fails\n");
+}
 print("\n");
 
 
diff --git a/buildtools/empty_depend b/buildtools/empty_depend
deleted file mode 100755
index a4ba372c..00000000
--- a/buildtools/empty_depend
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/perl -w
-
-use strict;
-
-# This program turns every Makefile.depend file into an empty file,
-# so that dependencies in there can't make trouble for users trying to
-# build Netpbm.  They don't need dependencies anyway, since they're 
-# building everything and not modifying anything.
-
-# Developers should do 'make dep' to create real Makefile.depend files
-# before building
-
-my $mf_list = `find . -name Makefile.depend`;
-my @mf_list = split(/\s/, $mf_list);
-foreach (@mf_list) {
-    print "Emptying $_\n";
-    open(MF, ">$_");
-    close(MF);
-}
diff --git a/buildtools/installnetpbm.pl b/buildtools/installnetpbm.pl
index 25376ec3..61900335 100755
--- a/buildtools/installnetpbm.pl
+++ b/buildtools/installnetpbm.pl
@@ -47,7 +47,10 @@ sub prompt($$) {
 
 
 sub getPkgdir() {
-
+#-----------------------------------------------------------------------------
+#  Find out from the user where the Netpbm package is (i.e. where
+#  'make package' put it).
+#-----------------------------------------------------------------------------
     my $pkgdir;
 
     while (!$pkgdir) {
@@ -141,7 +144,7 @@ sub getPrefix() {
 
 sub getCpCommand() {
 #-----------------------------------------------------------------------------
-# compute the command + options need to do a recursive copy, preserving
+# Compute the command + options need to do a recursive copy, preserving
 # symbolic links and file attributes.
 #-----------------------------------------------------------------------------
     my $cpCommand;
@@ -168,7 +171,10 @@ sub getCpCommand() {
 
 
 sub getBinDir($) {
-
+#-----------------------------------------------------------------------------
+#  Find out from the user where he wants the programs installed, and return
+#  that.
+#-----------------------------------------------------------------------------
     my ($prefix) = @_;
 
     print("Where do you want the programs installed?\n");
@@ -223,7 +229,10 @@ sub installProgram($$$) {
 
 
 sub getLibDir($) {
-
+#-----------------------------------------------------------------------------
+#  Find out from the user where he wants the runtime libraries installed and
+#  return that.
+#-----------------------------------------------------------------------------
     my ($prefix) = @_;
 
     print("Where do you want the shared library installed?\n");
@@ -256,8 +265,174 @@ sub getLibDir($) {
 
 
 
-sub 
-execLdconfig() {
+sub ldconfigExists() {
+
+    return (system("ldconfig -? >/dev/null 2>/dev/null") >> 8) != 127;
+}
+
+
+
+sub crleExists() {
+
+    return (system("crle -? 2>/dev/null") >> 8) != 127;
+}
+
+
+
+sub dirName($) {
+    my ($fileName) = @_;
+#-----------------------------------------------------------------------------
+#  The directory component of file name $fileName.
+#-----------------------------------------------------------------------------
+
+    my @components = split(m{/}, $fileName);
+
+    pop(@components);
+
+    if (@components == 1 && $components[0] eq '') {
+        return '/';
+    } else {
+        return join('/', @components);
+    }
+}
+
+
+
+sub ldConfigKnowsDir($) {
+    my ($shlibDir) = @_;
+#-----------------------------------------------------------------------------
+#  Ldconfig appears to search $shlibDir for shared libraries.
+#
+#  Our determination is approximate.  We just look at whether 'ldconfig'
+#  found anything in $shlibDir the last time it searched.  If it searched
+#  $shlibDir and just didn't find anything or $shlibDir has been added to
+#  its search path since then, we'll wrongly conclue that it doesn't search
+#  $shlibDir now.
+#-----------------------------------------------------------------------------
+    my @ldconfigOutput = split(m{\n}, qx{ldconfig -p});
+
+    my $found;
+
+    foreach (@ldconfigOutput) {
+
+        if (m{ => \s (.*) $ }x) {
+            my ($fileName) = ($1);
+
+            if (dirName($fileName) eq $shlibDir) {
+                $found = $TRUE;
+            }
+        }
+    }
+    return $found;
+}
+
+
+
+
+sub warnNonstandardShlibDirLdconfig($) {
+    my ($shlibDir) = @_;
+#-----------------------------------------------------------------------------
+#  Assuming this is a system that has an 'ldconfig' program, warn the user
+#  if $shlibDir appears not to be in the system shared library search path.
+#-----------------------------------------------------------------------------
+
+    # This appears to be a system that uses the GNU libc dynamic linker.
+    # The list of system shared library directories is in /etc/ld.so.conf.
+    # The program Ldconfig searches the directories in that list and
+    # remembers all the shared libraries it found (and some informtaion
+    # about them) in its cache /etc/ld.so.cache, which is what the 
+    # dynamic linker uses at run time to find the shared libraries.
+
+    if (!ldConfigKnowsDir($shlibDir)) {
+        print("You have installed shared libraries in " .
+              "'$shlibDir',\n" .
+              "which does not appear to be a system shared " .
+              "library directory ('ldconfig -p' \n" .
+              "doesn't show any other libraries in there).  " .
+              "Therefore, the system may not be\n" .
+              "able to find the Netpbm shared libraries " .
+              "when you run Netpbm programs.\n" .
+              "\n" .
+              "To fix this, you may need to update /etc/ld.so.conf\n" .
+              "\n" .
+              "You may need to use an LD_LIBRARY_PATH " .
+              "environment variable when running Netpbm programs\n" .
+              "\n");
+    }
+}
+
+
+
+
+sub warnNonstandardShlibDirCrle($) {
+    my ($shlibDir) = @_;
+#-----------------------------------------------------------------------------
+#  Assuming this is a system that has a 'crle' program, warn the user
+#  if $shlibDir appears not to be in the system shared library search path.
+#-----------------------------------------------------------------------------
+    # We should use 'crle' here to determine whether $shlibDir is a
+    # system directory.  But I don't have a Solaris system to reverse
+    # engineer/test with.
+
+    if ($shlibDir ne "/lib" && $shlibDir ne "/usr/lib") {
+        print("You have installed shared libraries in " .
+              "'$shlibDir',\n" .
+              "which is not a conventional system shared " .
+              "library directory.\n" .
+              "Therefore, the system may not be able to " .
+              "find the Netpbm\n" .
+              "shared libraries when you run Netpbm programs.\n" .
+              "\n" .
+              "To fix this, you may need to run 'crle -l'.\n" .
+              "\n" .
+              "You may need to use an LD_LIBRARY_PATH " .
+              "environment variable when running Netpbm programs\n" .
+              "\n");
+    }
+}
+        
+
+
+sub warnNonstandardShlibDirGeneric($) {
+    my ($shlibDir) = @_;
+#-----------------------------------------------------------------------------
+#  Without assuming any particular shared library search scheme on this
+#  system, warn if $shlibDir appears not to be in the system shared library
+#  search path.
+#-----------------------------------------------------------------------------
+
+    if ($shlibDir ne "/lib" && $shlibDir ne "/usr/lib") {
+        print("You have installed shared libraries in " .
+              "'$shlibDir',\n" .
+              "which is not a conventional system shared " .
+              "library directory.\n" .
+              "Therefore, the system may not be able to " .
+              "find the Netpbm\n" .
+              "shared libraries when you run Netpbm programs.\n" .
+              "\n" .
+              "You may need to use an LD_LIBRARY_PATH " .
+              "environment variable when running Netpbm programs\n" .
+              "\n");
+    }
+}
+
+
+
+sub warnNonstandardShlibDir($) {
+    my ($shlibDir) = @_;
+
+    if (ldconfigExists()) {
+        warnNonstandardShlibDirLdconfig($shlibDir);
+    } elsif (crleExists()) {
+        warnNonstandardShlibDirCrle($shlibDir);
+    } else {
+        warnNonstandardShlibDirGeneric($shlibDir);
+    }
+}
+
+
+
+sub execLdconfig() {
 #-----------------------------------------------------------------------------
 #  Run Ldconfig.  Try with the -X option first, and if that is an invalid
 #  option (which we have seen on an openBSD system), try it without -X.
@@ -311,15 +486,7 @@ execLdconfig() {
 
 
 
-sub ldconfigExists() {
-
-    return (system("ldconfig -? 2>/dev/null") >> 8) != 127;
-}
-
-
-
-sub
-doLdconfig() {
+sub doLdconfig() {
 #-----------------------------------------------------------------------------
 #  Run Ldconfig where appropriate.
 #-----------------------------------------------------------------------------
@@ -336,7 +503,7 @@ doLdconfig() {
               "to put the \n");
         print("Netpbm shared library in the cache?  This works only if " .
               "you have\n");
-        print("installed the library in a standard location.\n");
+        print("installed the library in a directory Ldconfig knows about.\n");
         print("\n");
         
         my $done;
@@ -377,6 +544,9 @@ sub installSharedLib($$$) {
         } else {
             print("done.\n");
             print("\n");
+
+            warnNonstandardShlibDir($libDir);
+
             doLdconfig();
         }
         $$libdirR = $libDir;
@@ -390,7 +560,10 @@ sub installSharedLib($$$) {
 
 
 sub getLinkDir($) {
-
+#-----------------------------------------------------------------------------
+#  Find out from the user where he wants the link-edit libraries installed and
+#  return that.
+#-----------------------------------------------------------------------------
     my ($prefix) = @_;
 
     print("Where do you want the static link library installed?\n");
@@ -450,7 +623,10 @@ sub installStaticLib($$$) {
 
 
 sub getDataDir($) {
-
+#-----------------------------------------------------------------------------
+#  Find out from the user where he wants the runtime data files installed and
+#  return that.
+#-----------------------------------------------------------------------------
     my ($prefix) = @_;
 
     print("Where do you want the data files installed?\n");
@@ -484,7 +660,10 @@ sub getDataDir($) {
 
 
 sub getHdrDir($) {
-
+#-----------------------------------------------------------------------------
+#  Find out from the user where he wants the compile-time header files
+#  installed and return that.
+#-----------------------------------------------------------------------------
     my ($prefix) = @_;
 
     print("Where do you want the library interface header files installed?\n");
@@ -574,7 +753,10 @@ sub installHeader($$$) {
 
 
 sub getManDir($) {
-
+#-----------------------------------------------------------------------------
+#  Find out from the user where he wants the pointer man pages
+#  installed and return that.
+#-----------------------------------------------------------------------------
     my ($prefix) = @_;
 
     print("Where do you want the man pages installed?\n");
@@ -792,10 +974,37 @@ sub installManPage($$$) {
 
 
 
+sub netpbmVersion($) {
+    my ($pkgdir) = @_;
+
+    my $versionOpened = open(VERSION, "<$pkgdir/VERSION");
+
+    my $version;
+    my $error;
+
+    if (!$versionOpened) {
+        $error = "Unable to open $pkgdir/VERSION for reading.  " .
+            "Errno=$ERRNO\n";
+    } else {
+        $version = <VERSION>;
+        chomp($version);
+        close(VERSION);
+    }
+
+    if ($error) {
+        print("Failed to determine the version of Netpbm from the package, "
+              . "so that will not be correct in netpbm.config and netpbm.pc.  "
+              . $error . "\n");
+        $version = "???";
+    }
+    return $version;
+}
+
+
+
 sub 
-processTemplate($$$$$$$$$) {
-    my ($templateR, $version, $bindir, $libdir, $linkdir, $datadir, 
-        $includedir, $mandir, $outputR) = @_;
+processTemplate($$$) {
+    my ($templateR, $infoR, $outputR) = @_;
 
     my @output;
 
@@ -803,26 +1012,26 @@ processTemplate($$$$$$$$$) {
         if (m{^@}) {
             # Comment -- ignore it.
         } else {
-            if (defined($version)) {
-                s/\@VERSION\@/$version/;
+            if (defined($infoR->{VERSION})) {
+                s/\@VERSION\@/$infoR->{VERSION}/;
             }
-            if (defined($bindir)) {
-                s/\@BINDIR@/$bindir/;
+            if (defined($infoR->{BINDIR})) {
+                s/\@BINDIR@/$infoR->{BINDIR}/;
             }
-            if (defined($libdir)) {
-                s/\@LIBDIR@/$libdir/;
+            if (defined($infoR->{LIBDIR})) {
+                s/\@LIBDIR@/$infoR-.{LIBDIR}/;
             }
-            if (defined($linkdir)) {
-                s/\@LINKDIR@/$linkdir/;
+            if (defined($infoR->{LINKDIR})) {
+                s/\@LINKDIR@/$infoR->{LINKDIR}/;
             }
-            if (defined($datadir)) {
-                s/\@DATADIR@/$datadir/;
+            if (defined($infoR->{DATADIR})) {
+                s/\@DATADIR@/$infoR->{DATADIR}/;
             }
-            if (defined($includedir)) {
-                s/\@INCLUDEDIR@/$includedir/;
+            if (defined($infoR->{INCLUDEDIR})) {
+                s/\@INCLUDEDIR@/$infoR->{INCLUDEDIR}/;
             }
-            if (defined($mandir)) {
-                s/\@MANDIR@/$mandir/;
+            if (defined($infoR->{MANDIR})) {
+                s/\@MANDIR@/$infoR->{MANDIR}/;
             }
             push(@output, $_);
         }
@@ -832,11 +1041,12 @@ processTemplate($$$$$$$$$) {
 
 
 
-sub
-installConfig($$$$$$$) {
-    my ($pkgdir, 
-        $bindir, $libdir, $linkdir, $datadir, $includedir, $mandir) = @_;
-
+sub installConfig($$) {
+    my ($installdir, $templateSubsR) = @_;
+#-----------------------------------------------------------------------------
+# Install 'netpbm-config' -- a program you run to tell you things about
+# how Netpbm is installed.
+#-----------------------------------------------------------------------------
     my $error;
 
     my $configTemplateFilename = dirname($0) . "/config_template";
@@ -849,41 +1059,108 @@ installConfig($$$$$$$) {
 
         close(TEMPLATE);
 
-        my $versionOpened = open(VERSION, "<$pkgdir/VERSION");
+        processTemplate(\@template, $templateSubsR, \my $fileContentsR);
 
-        my $version;
-        if (!$versionOpened) {
-            $error = "Unable to open $pkgdir/VERSION for reading.  " .
-                "Errno=$ERRNO\n";
+        # TODO: Really, this ought to go in an independent directory,
+        # because you might want to have the Netpbm executables in
+        # some place not in the PATH and use this program, via the
+        # PATH, to find them.
+        
+        my $filename = "$installdir/netpbm-config";
+        
+        my $success = open(NETPBM_CONFIG, ">$filename");
+        if ($success) {
+            chmod(0755, $filename);
+            foreach (@{$fileContentsR}) { print NETPBM_CONFIG; }
+            close(NETPBM_CONFIG);
         } else {
-            $version = <VERSION>;
-            chomp($version);
-            close(VERSION);
-
-            processTemplate(\@template, $version, $bindir, $libdir,
-                            $linkdir, $datadir, $includedir, $mandir,
-                            \my $fileContentsR);
-
-            # TODO: Really, this ought to go in an independent directory,
-            # because you might want to have the Netpbm executables in
-            # some place not in the PATH and use this program, via the
-            # PATH, to find them.
-            
-            my $filename = "$bindir/netpbm-config";
+            $error = "Unable to open the file " .
+                "'$filename' for writing.  Errno=$ERRNO\n";
+        }
+    }
+    if ($error) {
+        print(STDERR "Failed to create the Netpbm configuration program.  " .
+              "$error\n");
+    }
+}
+
+
+
+
+sub getPkgconfigDir($) {
+#-----------------------------------------------------------------------------
+#  Find out from the user where he wants the Pkg-config file for the
+#  installation (netpbm.pc) and return that.
+#-----------------------------------------------------------------------------
+    my ($prefix) = @_;
+
+    print("Where do you want the Pkg-config file netpbm.pc installed?\n");
+    print("\n");
+
+    my $pkgconfigDir;
+
+    while (!$pkgconfigDir) {
+        my $default = "$prefix/lib/pkgconfig";
+
+        my $response = prompt("Pkg-config directory", $default);
+        
+        if (-d($response)) {
+            $pkgconfigDir = $response;
+        } else {
+            my $succeeded = mkdir($response, 0777);
             
-            my $success = open(NETPBM_CONFIG, ">$filename");
-            if ($success) {
-                chmod(0755, $filename);
-                foreach (@{$fileContentsR}) { print NETPBM_CONFIG; }
-                close(NETPBM_CONFIG);
+            if (!$succeeded) {
+                print("Unable to create directory '$response'.  " .
+                      "Error=$ERRNO\n");
             } else {
-                $error = "Unable to open the file " .
-                    "'$filename' for writing.  Errno=$ERRNO\n";
+                $pkgconfigDir = $response;
             }
         }
     }
+    print("\n");
+
+    return $pkgconfigDir;
+}
+
+
+
+sub installPkgConfig($$) {
+    my ($prefix, $templateSubsR) = @_;
+#-----------------------------------------------------------------------------
+# Install a pkg-config file (netpbm.pc) - used by the 'pkg-config' program to
+# find out various things about how Netpbm is installed.
+#-----------------------------------------------------------------------------
+    my $pkgconfigDir = getPkgconfigDir($prefix);
+
+    my $error;
+
+    my $pcTemplateFilename = dirname($0) . "/pkgconfig_template";
+
+    my $templateOpened = open(TEMPLATE, "<$pcTemplateFilename");
+    if (!$templateOpened) {
+        $error = "Can't open template file '$pcTemplateFilename'.\n";
+    } else {
+        my @template = <TEMPLATE>;
+
+        close(TEMPLATE);
+
+        processTemplate(\@template, $templateSubsR,
+                        \my $fileContentsR);
+
+        my $filename = "$pkgconfigDir/netpbm.pc";
+        
+        my $success = open(NETPBM_PC, ">$filename");
+        if ($success) {
+            chmod(0755, $filename);
+            foreach (@{$fileContentsR}) { print NETPBM_PC; }
+            close(NETPBM_PC);
+        } else {
+            $error = "Unable to open the file " .
+                "'$filename' for writing.  Errno=$ERRNO\n";
+        }
+    }
     if ($error) {
-        print(STDERR "Failed to create the Netpbm configuration program.  " .
+        print(STDERR "Failed to create the Netpbm Pkg-config file.  " .
               "$error\n");
     }
 }
@@ -929,8 +1206,18 @@ print("\n");
 installManPage($pkgdir, $prefix, \my $mandir);
 print("\n");
 
-installConfig($pkgdir, 
-              $bindir, $libdir, $linkdir, $datadir, $includedir, $mandir);
+my $templateSubsR =
+    {VERSION    => netpbmVersion($pkgdir),
+     BINDIR     => $bindir,
+     LIBDIR     => $libdir,
+     LINKDIR    => $linkdir,
+     DATADIR    => $datadir,
+     INCLUDEDIR => $includedir,
+     MANDIR     => $mandir};
+
+installConfig($bindir, $templateSubsR);
+
+installPkgConfig($prefix, $templateSubsR);
 
 print("Installation is complete (except where previous error messages have\n");
 print("indicated otherwise).\n");
diff --git a/buildtools/installosf b/buildtools/installosf
index 1d1d61a7..77934411 100755
--- a/buildtools/installosf
+++ b/buildtools/installosf
@@ -17,7 +17,7 @@ while [ $# -gt 0 ]; do
     elif [ "$1" = "-m" ]; then 
         shift
         PERMISSIONS=$1
-    elif [ ${SOURCE_FILE}x == x ]; then
+    elif [ ${SOURCE_FILE}x = x ]; then
         SOURCE_FILE=$1
     else
         TARGET_DIR=$1
diff --git a/buildtools/makeman b/buildtools/makeman
index 2e122779..94ee2172 100755
--- a/buildtools/makeman
+++ b/buildtools/makeman
@@ -1,4 +1,4 @@
-#!/bin/env python
+#!/usr/bin/python
 #
 # makeman -- compile netpbm's stereotyped HTML to troff markup
 #
@@ -51,9 +51,7 @@ def makeman(name, file, indoc):
     # Protect escapes before we try generating font changes.
     indoc = indoc.replace("\\", r"\e")
     # Header-bashing
-    indoc = indoc.replace('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "DTD/xhtml11.dtd">', "")
-    indoc = indoc.replace('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "")
-    indoc = indoc.replace('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">\n',"")
+    indoc = re.sub('(?i)<!DOCTYPE html[^>]*>', "", indoc)
     indoc = indoc.replace('<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">', "")
     indoc = indoc.replace('<meta http-equiv="Content-Type" content="text/html; charset=us-ascii"/>', "")
     indoc = indoc.replace('<?xml version="1.1" encoding="iso-8859-1" ?>\n',"")
@@ -121,7 +119,10 @@ def makeman(name, file, indoc):
     indoc = re.sub("(?i)</SUP>", r"\\d", indoc)
     # Paragraph handling
     indoc = re.sub("(?i)\n*<P>\n*", r"\n.PP\n", indoc)
+    indoc = re.sub("(?i)<br */>", r"\n.PP\n", indoc)
     indoc = re.sub("(?i)</P>", "", indoc)
+    indoc = re.sub("(?i)<!--[^>]*-->", "", indoc)
+    indoc = re.sub("(?i)<meta[^>]*>", "", indoc)
     lines = indoc.split("\n")
     listdepth = 0
     for i in range(len(lines)):
@@ -138,13 +139,14 @@ def makeman(name, file, indoc):
     indoc = re.sub('(?i)<A[ \n]+HREF="mailto:[^>]+">([^<]+)</A>', r'\\fI\1\\fP', indoc)    
     # Format manual crossreferences
     def xrefmatch(match):
-        xrefto = match.group(1)
-        xrefsection = sectmap.get(xrefto, 1)
+        xrefto = match.group(2)
+        xrefurl = match.group(1)
+        xrefsection = sectmap.get(xrefurl, 1)
         if xrefsection == 0:
             return "\n.I " + xrefto
         else:
-            return "\n.BR %s (%d)" % (xrefto, xrefsection)
-    indoc = re.sub(r'(?i)\n* *(?:\\fB)?<A[ \n]+HREF="[^>]+.html">([^<]+)</A>(?:\\fP)?',
+            return "\n.BR %s (%d)\n" % (xrefto, xrefsection)
+    indoc = re.sub(r'(?i)\n* *(?:\\fB)?<A[ \n]+HREF="?([^>]+.html)"?>([^<]+)</A>(?:\\fP)?',
                    xrefmatch, indoc)
     # Format URLs
     def urlmatch(match):
@@ -159,7 +161,7 @@ def makeman(name, file, indoc):
     indoc = indoc.replace("&#174;", r"\*R")
     indoc = indoc.replace("&copy;", r"\(co")
     # Turn anchors into .UN tags
-    indoc = re.sub('(?i)<A NAME *= *"#?([a-zA-Z][a-zA-Z0-9.-]+)">(?:&nbsp;)*</A>\s*', ".UN \\1\n", indoc)
+    indoc = re.sub('(?i)<A NAME *= *"#?([a-zA-Z_][a-zA-Z_0-9.-]+)">(?:&nbsp;)*</A>\s*', ".UN \\1\n", indoc)
     # Strip off the index trailer
     trailer = re.compile('<HR */*>.*', re.DOTALL | re.IGNORECASE)
     indoc = re.sub(trailer, "", indoc)
diff --git a/buildtools/manpage.mk b/buildtools/manpage.mk
index e1c0bce2..76116ffe 100644
--- a/buildtools/manpage.mk
+++ b/buildtools/manpage.mk
@@ -48,43 +48,62 @@ MAN1 = \
 	mtvtoppm.1 \
 	neotoppm.1 \
 	palmtopnm.1 \
+	pamaddnoise.1 \
 	pamarith.1 \
+	pambackground.1 \
+	pambayer.1 \
 	pamchannel.1 \
 	pamcomp.1 \
 	pamcut.1 \
 	pamdeinterlace.1 \
+	pamdepth.1 \
 	pamdice.1 \
 	pamditherbw.1 \
 	pamedge.1 \
 	pamendian.1 \
+	pamenlarge.1 \
 	pamfile.1 \
 	pamfixtrunc.1 \
 	pamflip.1 \
 	pamfunc.1 \
 	pamgauss.1 \
+	pamgradient.1 \
 	pamlookup.1 \
+	pammasksharpen.1 \
+	pammixinterlace.1 \
 	pamoil.1 \
 	pamperspective.1 \
+	pampick.1 \
 	pampop9.1 \
+	pamrgbatopng.1 \
 	pamscale.1 \
 	pamseq.1 \
 	pamsharpmap.1 \
 	pamsharpness.1 \
 	pamslice.1 \
+	pamsplit.1 \
 	pamstack.1 \
 	pamstereogram.1 \
 	pamstretch-gen.1 \
 	pamstretch.1 \
 	pamsumm.1 \
 	pamsummcol.1 \
+	pamthreshold.1 \
+	pamtilt.1 \
 	pamtodjvurle.1 \
+	pamtofits.1 \
+	pamtogif.1 \
 	pamtohdiff.1 \
 	pamtohtmltbl.1 \
 	pamtojpeg2k.1 \
 	pamtopfm.1 \
 	pamtopnm.1 \
+	pamtosvg.1 \
 	pamtotga.1 \
+	pamtotiff.1 \
 	pamtouil.1 \
+	pamtoxvmini.1 \
+	pamx.1 \
 	pbmclean.1 \
 	pbmlife.1 \
 	pbmmake.1 \
@@ -107,11 +126,13 @@ MAN1 = \
 	pbmtog3.1 \
 	pbmtogem.1 \
 	pbmtogo.1 \
+	pbmtoibm23xx.1 \
 	pbmtoicon.1 \
 	pbmtolj.1 \
 	pbmtoln03.1 \
 	pbmtolps.1 \
 	pbmtomacp.1 \
+	pbmtomatrixorbital.1 \
 	pbmtomda.1 \
 	pbmtomgr.1 \
 	pbmtomrf.1 \
@@ -136,10 +157,13 @@ MAN1 = \
 	pgmabel.1 \
 	pgmbentley.1 \
 	pgmcrater.1 \
+	pgmdeshadow.1 \
 	pgmedge.1 \
 	pgmenhance.1 \
 	pgmhist.1 \
 	pgmkernel.1 \
+	pgmmake.1 \
+	pgmmedian.1 \
 	pgmminkowski.1 \
 	pgmmorphconv.1 \
 	pgmnoise.1 \
@@ -217,9 +241,13 @@ MAN1 = \
 	ppmchange.1 \
 	ppmcie.1 \
 	ppmcolormask.1 \
+	ppmdcfont.1 \
+	ppmddumpfont.1 \
 	ppmdim.1 \
 	ppmdist.1 \
 	ppmdither.1 \
+	ppmdmkfont.1 \
+	ppmdraw.1 \
 	ppmfade.1 \
 	ppmflash.1 \
 	ppmforge.1 \
@@ -279,6 +307,7 @@ MAN1 = \
 	rawtopgm.1 \
 	rawtoppm.1 \
 	rgb3toppm.1 \
+	rlatopam.1 \
 	rletopnm.1 \
 	sbigtopgm.1 \
 	sgitopnm.1 \
@@ -302,40 +331,11 @@ MAN1 = \
 	yuvsplittoppm.1 \
 	yuvtoppm.1 \
 	zeisstopnm.1 \
-        pamaddnoise.1 \
-        pambackground.1 \
-        pambayer.1 \
-        pamdepth.1 \
-        pamenlarge.1 \
-        pamgradient.1 \
-        pammasksharpen.1 \
-        pammixinterlace.1 \
-        pampick.1 \
-        pamrgbatopng.1 \
-        pamsplit.1 \
-        pamthreshold.1 \
-        pamtilt.1 \
-        pamtofits.1 \
-        pamtogif.1 \
-        pamtosvg.1 \
-        pamtotiff.1 \
-        pamtoxvmini.1 \
-        pamx.1 \
-        pbmtoibm23xx.1 \
-        pbmtomatrixorbital.1 \
-        pgmdeshadow.1 \
-        pgmmake.1 \
-        pgmmedian.1 \
-        ppmdcfont.1 \
-        ppmddumpfont.1 \
-        ppmdmkfont.1 \
-        ppmdraw.1 \
-        rlatopam.1 \
 
 MAN3 = \
-	libnetpbm.3 \
 	libnetpbm_image.3 \
 	libnetpbm_ug.3 \
+	libnetpbm.3 \
 	libpbm.3 \
 	libpgm.3 \
 	libpm.3 \
@@ -348,13 +348,13 @@ MAN5 = \
 	extendedopacity.5 \
 	pam.5 \
 	pbm.5 \
-        pfm.5 \
+	pfm.5 \
 	pgm.5 \
 	pnm.5 \
 	ppm.5 \
 
 # These things do get converted to man pages and installed.
-MANPAGES = $(MAN1) netpbm.1 $(MAN3) $(MAN5)
+MANPAGES = netpbm.1 $(MAN1) $(MAN3) $(MAN5)
 HTMLMANUALS = $(MAN1:.1=.html) $(MAN3:.3=.html) $(MAN5:.5=.html)
 
 # These things don't get converted to manual pages.
@@ -388,13 +388,13 @@ xmlpages:
 # This will install the generated man pages
 installman:
 	set -x
-	for f in $(MAN1); do if [ -f $$f ]; then gzip <$$f >$(MANDIR)/man1/$$f.gz; fi; done
+	for f in netpbm.1 $(MAN1); do if [ -f $$f ]; then gzip <$$f >$(MANDIR)/man1/$$f.gz; fi; done
 	for f in $(MAN3); do if [ -f $$f ]; then gzip <$$f >$(MANDIR)/man3/$$f.gz; fi; done
 	for f in $(MAN5); do if [ -f $$f ]; then gzip <$$f >$(MANDIR)/man5/$$f.gz; fi; done
 
 # This will uninstall them
 uninstallman:
-	for f in $(MAN1); do rm -f $(MANDIR)/man1/$$f.gz; fi; done
+	for f in netpbm.1 $(MAN1); do rm -f $(MANDIR)/man1/$$f.gz; fi; done
 	for f in $(MAN3); do rm -f $(MANDIR)/man3/$$f.gz; fi; done
 	for f in $(MAN5); do rm -f $(MANDIR)/man5/$$f.gz; fi; done
 
diff --git a/buildtools/stamp-date b/buildtools/stamp-date
index 5fb6c13a..32839e94 100755
--- a/buildtools/stamp-date
+++ b/buildtools/stamp-date
@@ -9,7 +9,7 @@
 # documentation.  This software is provided "as is" without express or
 # implied warranty.
 #
-DATE=`date`
+DATE=$(date)
 LOGNAME_OR_UNKNOWN=${LOGNAME:-UNKNOWN}
 USER=${USER:-$LOGNAME_OR_UNKNOWN}
 if [ "$USER" = "UNKNOWN" ]; then
diff --git a/common.mk b/common.mk
index ed4f68bb..dd7f4ed3 100644
--- a/common.mk
+++ b/common.mk
@@ -43,12 +43,11 @@
 # MERGEBINARIES: list of the programs that, in a merge build, are invoked
 #   via the merged Netpbm program
 # CC: C compiler command 
-# CPPFLAGS: C preprocessor options
-# CFLAGS: C compiler general options
+# CFLAGS_CONFIG: C compiler options from config.mk.
+# CFLAGS_TARGET: C compiler options for a particular target
 # LD: linker command
 # LINKERISCOMPILER: 'Y' if the linker invoked by LD is actually a compiler
 #   front end, so takes linker options in a different format
-# LDFLAGS: linker options 
 # LIBS or LOADLIBES: names of libraries to be added to all links
 # COMP_INCLUDES: Compiler option string to establish the search path for
 #   component-specific include files when compiling things or computing
@@ -63,6 +62,10 @@
 # is intended to be set on a make command line (e.g. 'make CADD=-g')
 # for options that apply just to a particular build.
 
+# In addition, there is CFLAGS, which is extra C compilation options and is
+# expected to be set via the make command line for a particular build.
+# Likewise, LDFLAGS for link-edit options.
+
 # In addition, there is CFLAGS_PERSONAL, which is extra C
 # compilation options and is expected to be set via environment variable
 # for options that are particular to the person doing the build and not
@@ -76,7 +79,20 @@ include $(SRCDIR)/version.mk
 # fully made.
 .DELETE_ON_ERROR:
 
-NETPBM_INCLUDES := -Iimportinc -I$(SRCDIR)/$(SUBDIR)
+# -I importinc/netpbm is a backward compatibility thing.  Really, the source
+# file should refer to e.g. "netpbm/pam.h" but for historical reasons, most
+# refer to "pam.h" and we'll probably never have the energy to convert them
+# all.  The reason the file exists as importinc/netpbm/pam.h rather than just
+# importinc/pam.h (as it did originally) is that it lives on a user's system
+# as <netpbm/pam.h>, and therefore all _exported_ header files do say
+# "<netpbm/pam.h>.
+ifneq ($(ALL_INTERNAL_HEADER_FILES_ARE_QUALIFIED),Y)
+  LEGACY_NETPBM_INCLUDE = -Iimportinc/netpbm
+else
+  LEGACY_NETPBM_INCLUDE =
+endif
+
+NETPBM_INCLUDES := -Iimportinc $(LEGACY_NETPBM_INCLUDE) -I$(SRCDIR)/$(SUBDIR)
 
 # -I. is needed when builddir != srcdir
 INCLUDES = -I. $(COMP_INCLUDES) $(NETPBM_INCLUDES) $(EXTERN_INCLUDES)
@@ -101,11 +117,20 @@ LDLIBS = $(LOADLIBES) $(LIBS)
 # way to detect that case and fail, so I just add a '/' to the front
 # if it isn't already there.
 ifneq ($(pkgdir)x,x)
-  PKGDIR = $(patsubst //%, /%, /$(pkgdir))
+  PKGDIR = $(patsubst //%,/%, /$(pkgdir))
 else
   PKGDIR = $(PKGDIR_DEFAULT)
 endif
 
+
+# 'resultdir', like 'pkgdir' is meant to be supplied from the make
+# command line.  Unlike 'pkgdir' we allow relative paths.
+ifneq ($(resultdir)x,x)
+  RESULTDIR = $(resultdir)
+else
+  RESULTDIR = $(RESULTDIR_DEFAULT)
+endif
+
 #===========================================================================
 # We build a directory full of symbolic links to the intra-Netpbm public
 # header files just so the compile commands don't have to be littered
@@ -118,14 +143,16 @@ endif
 IMPORTINC_ROOT_HEADERS := pm_config.h inttypes_netpbm.h version.h
 
 IMPORTINC_LIB_HEADERS := \
-  pm.h pbm.h pgm.h ppm.h pnm.h pam.h bitio.h pbmfont.h ppmcmap.h \
+  pm.h pbm.h pgm.h ppm.h pnm.h pam.h pbmfont.h ppmcmap.h \
   pammap.h colorname.h ppmfloyd.h ppmdraw.h pm_system.h ppmdfont.h \
-  pm_gamma.h lum.h dithers.h
+  pm_gamma.h lum.h dithers.h pamdraw.h
 
 IMPORTINC_LIB_UTIL_HEADERS := \
-  bitarith.h bitreverse.h filename.h intcode.h floatcode.h mallocvar.h\
-  nsleep.h nstring.h pm_c_util.h shhopt.h \
-  wordaccess.h wordaccess_64_le.h wordaccess_gcc3_be.h wordaccess_generic.h \
+  bitarith.h bitio.h bitreverse.h filename.h intcode.h floatcode.h io.h \
+  matrix.h mallocvar.h \
+  nsleep.h nstring.h pm_c_util.h runlength.h shhopt.h token.h \
+  wordaccess.h  wordaccess_generic.h wordaccess_64_le.h \
+  wordaccess_be_aligned.h wordaccess_be_unaligned.h \
   wordintclz.h
 
 IMPORTINC_HEADERS := \
@@ -133,33 +160,32 @@ IMPORTINC_HEADERS := \
   $(IMPORTINC_LIB_HEADERS) \
   $(IMPORTINC_LIB_UTIL_HEADERS)
 
-IMPORTINC_ROOT_FILES := $(IMPORTINC_ROOT_HEADERS:%=importinc/%)
-IMPORTINC_LIB_FILES := $(IMPORTINC_LIB_HEADERS:%=importinc/%)
-IMPORTINC_LIB_UTIL_FILES := $(IMPORTINC_LIB_UTIL_HEADERS:%=importinc/%)
+IMPORTINC_ROOT_FILES := $(IMPORTINC_ROOT_HEADERS:%=importinc/netpbm/%)
+IMPORTINC_LIB_FILES := $(IMPORTINC_LIB_HEADERS:%=importinc/netpbm/%)
+IMPORTINC_LIB_UTIL_FILES := $(IMPORTINC_LIB_UTIL_HEADERS:%=importinc/netpbm/%)
 
 importinc: \
   $(IMPORTINC_ROOT_FILES) \
   $(IMPORTINC_LIB_FILES) \
   $(IMPORTINC_LIB_UTIL_FILES) \
-  importinc/netpbm \
 
-importinc/netpbm:
-	mkdir -p importinc
-	rm -f $@
-	$(SYMLINK) . $@
+# The reason we mkdir importinc/netpbm every time instead of just having
+# importinc depend on it and a rule to make it is that as a dependency, it
+# would force importinc to rebuild when importinc/netpbm has a more recent
+# modification date, which it sometimes would.
 
-$(IMPORTINC_ROOT_FILES):importinc/%:$(BUILDDIR)/%
-	mkdir -p importinc
+$(IMPORTINC_ROOT_FILES):importinc/netpbm/%:$(BUILDDIR)/%
+	mkdir -p importinc/netpbm
 	rm -f $@
 	$(SYMLINK) $< $@
 
-$(IMPORTINC_LIB_FILES):importinc/%:$(SRCDIR)/lib/%
-	mkdir -p importinc
+$(IMPORTINC_LIB_FILES):importinc/netpbm/%:$(SRCDIR)/lib/%
+	mkdir -p importinc/netpbm
 	rm -f $@
 	$(SYMLINK) $< $@
 
-$(IMPORTINC_LIB_UTIL_FILES):importinc/%:$(SRCDIR)/lib/util/%
-	mkdir -p importinc
+$(IMPORTINC_LIB_UTIL_FILES):importinc/netpbm/%:$(SRCDIR)/lib/util/%
+	mkdir -p importinc/netpbm
 	rm -f $@
 	$(SYMLINK) $< $@
 
@@ -209,6 +235,27 @@ config:
 
 # Rule to make regular object files, e.g. pnmtojpeg.o.
 
+# The NDEBUG macro says to build code that assumes there are no bugs.
+# This makes the code go faster.  The main thing it does is tell the C library
+# to make assert() a no-op as opposed to generating code to check the
+# assertion and crash the program if it isn't really true.  You can add
+# -UNDEBUG (in any of various ways) to override this.
+#
+CFLAGS_ALL = \
+  -DNDEBUG $(CPPFLAGS) $(CFLAGS_CONFIG) $(CFLAGS_TARGET) $(CFLAGS_PERSONAL) $(CFLAGS) $(CADD)
+
+ifeq ($(WANT_SSE),Y)
+  # The only two compilers we've seen that have the SSE capabilities that
+  # WANT_SSE requests are GCC and Clang, and they both have these options and
+  # require them in order for <emmintrin.h> to compile.  On some systems
+  # (x86_64, in our experience), these options are default, but on more
+  # traditional systems, they are not.  Note: __SSE2__ macro tells whether
+  # -msse2 is in effect.
+  CFLAGS_SSE = -msse -msse2
+else
+  CFLAGS_SSE =
+endif
+
 $(OBJECTS): %.o: %.c importinc
 #############################################################################
 # Note that the user may have configured -I options into CFLAGS or CPPFLAGS.
@@ -221,14 +268,8 @@ $(OBJECTS): %.o: %.c importinc
 # the space is no longer a problem for anyone.
 #############################################################################
 #
-# The NDEBUG macro says not to build code that assumes there are no bugs.
-# This makes the code go faster.  The main thing it does is tell the C library
-# to make assert() a no-op as opposed to generating code to check the
-# assertion and crash the program if it isn't really true.  You can add
-# -UNDEBUG (in any of various ways) to override this.
-#
-	$(CC) -c $(INCLUDES) -DNDEBUG \
-	    $(CPPFLAGS) $(CFLAGS) $(CFLAGS_PERSONAL) $(CADD) -o $@ $<
+# We have to get this all on one line to make make messages neat
+	$(CC) -c $(INCLUDES) $(CFLAGS_ALL) -o $@ $<
 
 # libopt is a utility program used in the make file below.
 LIBOPT = $(BUILDDIR)/buildtools/libopt
@@ -251,6 +292,10 @@ $(BUNDLED_URTLIB): $(BUILDDIR)/urt
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 endif
 
+$(BUILDDIR)/icon/netpbm.o: $(BUILDDIR)/icon
+	$(MAKE) -C $(dir $@) -f $(SRCDIR)/icon/Makefile \
+	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
+
 # Here are some notes from Nelson H. F. Beebe on April 16, 2002:
 #
 #   There are at least three incompatible kinds of command-line options
@@ -332,11 +377,19 @@ endif
 # Earlier ones do it regardless of __FAST_MATH__.
 
 MATHLIB ?= -lm
-$(PORTBINARIES) $(MATHBINARIES): %: %.o $(NETPBMLIB) $(LIBOPT)
+
 # Note that LDFLAGS might contain -L options, so order is important.
 # LDFLAGS is commonly set as an environment variable.
-	$(LD) -o $@ $@.o $(MATHLIB) $(shell $(LIBOPT) $(NETPBMLIB)) \
-	  $(LDFLAGS) $(LDLIBS) $(MATHLIB) $(RPATH) $(LADD)
+# Some of the target-specific libraries are internal Netpbm libraries
+# (such as libfiasco), which use Libnetpbm.  So we put $(NETPBMLIB)
+# after LDFLAGS_TARGET.
+LDFLAGS_ALL = $(WINICON_OBJECT) \
+ $(LDFLAGS_TARGET) $(shell $(LIBOPT) $(NETPBMLIB)) \
+ $(LDFLAGS) $(LDLIBS) $(MATHLIB) $(RPATH) $(LADD)
+
+$(PORTBINARIES) $(MATHBINARIES): %: %.o \
+  $(NETPBMLIB) $(LIBOPT) $(WINICON_OBJECT)
+	$(LD) -o $@$(EXE) $@.o $(ADDL_OBJECTS) $(LDFLAGS_ALL)
 
 
 # MERGE STUFF
@@ -410,7 +463,17 @@ ifeq ($(SYMLINKEXE)x,x)
   SYMLINKEXE := $(SYMLINK)
 endif
 
-$(PKGDIR)/%:
+# An implicit rule for $(PKGDIR)/% does not work because it causes Make
+# sometimes to believe the directory it creates from this rule is an unneeded
+# intermediate file and try to delete it later.  So we explicitly list the
+# possible directories under $(PKGDIR):
+
+PKGMANSUBDIRS = man1 man3 man5 web
+
+PKGSUBDIRS = bin include include/netpbm lib link misc \
+  $(PKGMANSUBDIRS:%=$(PKGMANDIR)/%)
+
+$(PKGSUBDIRS:%=$(PKGDIR)/%):
 	$(SRCDIR)/buildtools/mkinstalldirs $@
 
 .PHONY: install.merge
@@ -452,11 +515,13 @@ install.man: install.man1 install.man3 install.man5 \
 
 MANUALS1 = $(BINARIES) $(SCRIPTS)
 
-install.man1: $(PKGDIR)/$(PKGMANDIR)/man1 $(MANUALS1:%=%_installman1)
+install.man1: $(MANUALS1:%=%_installman1)
 
-install.man3: $(PKGDIR)/$(PKGMANDIR)/man3 $(MANUALS3:%=%_installman3)
+install.man3: $(MANUALS3:%=%_installman3)
 
-install.man5: $(PKGDIR)/$(PKGMANDIR)/man5 $(MANUALS5:%=%_installman5)
+install.man5: $(MANUALS5:%=%_installman5)
+
+install.manweb: $(MANUALS1:%=%_installmanweb) $(SUBDIRS:%=%/install.manweb)
 
 %_installman1: $(PKGDIR)/$(PKGMANDIR)/man1
 	perl -w $(SRCDIR)/buildtools/makepointerman $(@:%_installman1=%) \
@@ -470,6 +535,10 @@ install.man5: $(PKGDIR)/$(PKGMANDIR)/man5 $(MANUALS5:%=%_installman5)
 	perl -w $(SRCDIR)/buildtools/makepointerman $(@:%_installman5=%) \
           $(NETPBM_DOCURL) $< 5 $(MANPAGE_FORMAT) $(INSTALL_PERM_MAN)
 
+%_installmanweb: $(PKGDIR)/$(PKGMANDIR)/web
+	echo $(NETPBM_DOCURL)$(@:%_installmanweb=%).html \
+	  >$</$(@:%_installmanweb=%).url
+
 .PHONY: clean
 
 ifneq ($(EXE)x,x)
@@ -515,6 +584,9 @@ endif
 %/install.man:
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
+%/install.manweb:
+	$(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \
+	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 %/install.data:
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
diff --git a/config.mk.in b/config.mk.in
index 0e061701..0f2ccb6c 100644
--- a/config.mk.in
+++ b/config.mk.in
@@ -89,6 +89,17 @@ INTTYPES_H = <inttypes.h>
 HAVE_INT64 = Y
 #HAVE_INT64 = N
 
+# WANT_SSE tells whether the build should use SSE instructions, via the the
+# standard SSE intrinsics (operators such as '_mm_movemask_epi8').  SSE
+# instructions are faster than traditional instructions, but aren't available
+# on all CPUs.  Also, the standard intrinsics are not available in all
+# compilers.  Even if you say N here, Netpbm may still be built with some
+# SSE exploitation (e.g. SSE floating point) because the compiler will 
+# do it automatically.  You can add a -nomsse or -nomsse2 option to
+# CFLAGS or CFLAGS_PERSONAL to stop that.
+WANT_SSE = N
+#WANT_SSE = Y
+
 # CC and LD are for building the Netpbm programs, which are not necessarily
 # intended to run on the same system on which Make is running.  But when we 
 # build a build tool such as Libopt, it is meant to run only on the same 
@@ -96,9 +107,13 @@ HAVE_INT64 = Y
 # to use to compile and link build tools.
 CC_FOR_BUILD = $(CC)
 LD_FOR_BUILD = $(LD)
-CFLAGS_FOR_BUILD = $(CFLAGS)
+CFLAGS_FOR_BUILD = $(CFLAGS_CONFIG)
 LDFLAGS_FOR_BUILD = $(LDFLAGS)
 
+# WINDRES is the program that creates a linkable object file from 
+# a Windows Icon (.ico) file.
+WINDRES = windres
+
 # MAKE is set automatically by Make to what was used to invoke Make.
 
 INSTALL = $(SRCDIR)/buildtools/install.sh
@@ -154,7 +169,7 @@ LEX = flex
 # -pedantic isn't a problem because it causes at worst a warning.
 #CFLAGS = -O3 -ffast-math -pedantic -fno-common \
 #          -Wall -Wno-uninitialized -Wmissing-declarations -Wimplicit \
-#          -Wwrite-strings -Wmissing-prototypes -Wundef
+#          -Wwrite-strings -Wmissing-prototypes -Wundef -Wno-unknown-pragmas
 # The merged programs have a main_XXX subroutine instead of main(),
 # which would cause a warning with -Wmissing-declarations or 
 # -Wmissing-prototypes.
@@ -375,12 +390,11 @@ TIFFHDR_DIR =
 #TIFFHDR_DIR = /usr/local1/DEC/include
 
 # Some TIFF libraries do Jpeg and/or Z (flate) compression and thus any
-# program linked with the TIFF library needs a Jpeg and/or Z library.
-# Some TIFF libraries have such library statically linked in, but others
-# need it to be dynamically linked at program load time.
-# Make this 'N' if youf TIFF library doesn't need such dynamic linking.
-# As of 2005.01, the most usual build of the TIFF library appears to require
-# both.
+# program linked with the TIFF library needs a Jpeg and/or Z library.  Some
+# TIFF libraries have such library statically linked in, but others need it to
+# be linked with the program at link-edit time or dynamically at program load
+# time.  Make this 'N' if your TIFF library doesn't need such linking.  As of
+# 2005.01, the most usual build of the TIFF library appears to require both.
 TIFFLIB_NEEDS_JPEG = Y
 TIFFLIB_NEEDS_Z = Y
 
@@ -424,6 +438,14 @@ JPEGHDR_DIR =
 # files will use that program if it exists (must be in the PATH).  In that
 # case, PNGLIB and PNGHDR_DIR are irrelevant, but PNGVER is still meaningful,
 # because the make file runs 'libpng$(PNGVER)-config'.
+#
+# Even more recent versions have the more modern Pkgconfig database entry
+# to tell how to link it.  The make files will try to use that first.
+#
+# The normal way to choose the libpng the Netpbm build uses from among multiple
+# versions on your system is not to mess with the variables below, but rather
+# to mess with PKG_CONFIG_PATH or PATH environment variable so that the version
+# you want to use appears first in the search path.
 
 PNGLIB = NONE
 PNGHDR_DIR =
@@ -450,8 +472,8 @@ ZHDR_DIR =
 
 # The JBIG lossless image compression library (aka JBIG-KIT):
 
-JBIGLIB = $(BUILDDIR)/converter/other/jbig/libjbig.a
-JBIGHDR_DIR = $(SRCDIR)/converter/other/jbig
+JBIGLIB = $(INTERNAL_JBIGLIB)
+JBIGHDR_DIR = $(INTERNAL_JBIGHDR_DIR)
 
 # The Jasper JPEG-2000 image compression library (aka JasPer):
 JASPERLIB = $(INTERNAL_JASPERLIB)
@@ -485,7 +507,12 @@ LINUXSVGAHDR_DIR =
 #LINUXSVGALIB = /usr/lib/libvga.so
 #LINUXSVGAHDR_DIR = /usr/include/vgalib
 
-# If you don't want any network functions, set OMIT_NETWORK to "y".
+# WINICON_OBJECT is the object file to bind into all Netpbm executables
+# to provide the icon for Windows to use for it.  Null for none.
+WINICON_OBJECT =
+#WINICON_OBJECT = $(BUILDDIR)/icon/netpbm.o
+
+# If you don't want any network functions, set OMIT_NETWORK to "Y".
 # The only thing that requires network functions is the option in
 # ppmtompeg to run it on multiple computers simultaneously.  On some
 # systems network functions don't work or we haven't figured out how to 
@@ -494,11 +521,11 @@ OMIT_NETWORK =
 #DJGPP/Windows, Tru64:
 #   (there's some minor header problem that prevents network functions from 
 #   building on Tru64 2000.10.06)
-#OMIT_NETWORK = y
+#OMIT_NETWORK = Y
 
 # These are -l options to link in the network libraries.  Often, these are
 # built into the standard C library, so this can be null.  This is irrelevant
-# if OMIT_NETWORK is "y".
+# if OMIT_NETWORK is "Y".
 
 NETWORKLD = 
 # Solaris, SunOS:
@@ -506,10 +533,6 @@ NETWORKLD =
 # SCO:
 #NETWORKLD = -lsocket, -lresolv
 
-VMS = 
-#VMS:
-#VMS = yes
-
 # DONT_HAVE_PROCESS_MGMT is Y if this system doesn't have the usual
 # Unix process management stuff - fork, wait, etc.  N for a regular Unix
 # system.
@@ -523,6 +546,10 @@ DONT_HAVE_PROCESS_MGMT = N
 # override it by setting 'pkgdir' on the Make command line.
 PKGDIR_DEFAULT = /tmp/netpbm
 
+# This is where test results are written when you do 'make check', unless
+# you override it by setting 'resultdir' on the Make command line.
+RESULTDIR_DEFAULT = /tmp/netpbm-test
+
 # Subdirectory of the package directory ($(pkgdir)) in which man pages
 # go.
 PKGMANDIR = man
@@ -584,12 +611,12 @@ NETPBMLIBSUFFIX = so
 # Windows shared library:
 #NETPBMLIBSUFFIX = dll
 
-#STATICLIB_TOO is "y" to signify that you want a static library built
+#STATICLIB_TOO is "Y" to signify that you want a static library built
 #and installed in addition to whatever library type you specified by
 #NETPBMLIBTYPE.  If NETPBMLIBTYPE specified a static library,
 #STATICLIB_TOO simply has no effect.
-STATICLIB_TOO = y
-#STATICLIB_TOO = n
+STATICLIB_TOO = Y
+#STATICLIB_TOO = N
 
 #STATICLIBSUFFIX is the suffix that static libraries have.  It's
 #meaningless if you aren't building static libraries.
@@ -629,8 +656,11 @@ DLLVER =
 #library, and file type.  E.g. The documentation for jpegtopnm might be in
 #http://netpbm.sourceforge.net/doc/jpegtopnm.html .  This value gets
 #installed in the man pages (which say no more than to read the webpage)
-#and in the Webman netpbm.url file.
+#and in the Manweb netpbm.url file.
 NETPBM_DOCURL = http://netpbm.sourceforge.net/doc/
 #For a system with no web access, but a local copy of the doc:
 #NETPBM_DOCURL = file:/usr/doc/netpbm/
 
+# RGB_DB_PATH is where Netpbm looks for the color database when the RGBDEF
+# environment variable is not set.  See pm_config_in.h for details.
+RGB_DB_PATH = /usr/share/netpbm/rgb.txt:/usr/lib/X11/rgb.txt:/usr/share/X11/rgb.txt:/usr/X11R6/lib/X11/rgb.txt
diff --git a/converter/bmp.h b/converter/bmp.h
index 8b2aa302..524bbf7e 100644
--- a/converter/bmp.h
+++ b/converter/bmp.h
@@ -88,12 +88,30 @@ enum bmpClass {C_WIN=1, C_OS2=2};
 static char const er_internal[] = "%s: internal error!";
 
 /* Values of the "compression" field of the BMP info header */
-#define COMP_RGB       0
-#define COMP_RLE8      1
-#define COMP_RLE4      2
-#define COMP_BITFIELDS 3
-#define COMP_JPEG      4
-#define COMP_PNG       5
+typedef enum BMPCompType {
+    BMPCOMP_RGB       = 0,
+    BMPCOMP_RLE8      = 1,
+    BMPCOMP_RLE4      = 2,
+    BMPCOMP_BITFIELDS = 3,
+    BMPCOMP_JPEG      = 4,
+    BMPCOMP_PNG       = 5
+} BMPCompType;
+
+static __inline__ const char *
+BMPCompTypeName(BMPCompType const compression) {
+
+    switch (compression) {
+    case BMPCOMP_RGB:       return "none (RBG)";
+    case BMPCOMP_RLE4:      return "4 bit run-length coding";
+    case BMPCOMP_RLE8:      return "8 bit run-length coding";
+    case BMPCOMP_BITFIELDS: return "none (bitfields)";
+    case BMPCOMP_JPEG:      return "JPEG";
+    case BMPCOMP_PNG:       return "PNG";   
+    }
+    return 0;  /* Default compiler warning */
+}
+
+
 
 static __inline__ unsigned int
 BMPlenfileheader(enum bmpClass const class) {
@@ -127,7 +145,13 @@ static __inline__ unsigned int
 BMPlencolormap(enum bmpClass const class,
                unsigned int  const bitcount, 
                unsigned int  const cmapsize) {
+/*----------------------------------------------------------------------------
+   The number of bytes of the BMP stream occupied by the colormap in a
+   BMP of class 'class' with 'bitcount' bits per pixel and 'cmapsize'
+   entries in the palette.
 
+   'cmapsize' == 0 means there is no palette.
+-----------------------------------------------------------------------------*/
     unsigned int lenrgb;
     unsigned int lencolormap;
 
@@ -214,21 +238,21 @@ BMPoffbits(enum bmpClass const class,
 
 
 static __inline__ unsigned int
-BMPlenfileGen(enum bmpClass     const class,
-              unsigned int      const bitcount, 
-              unsigned int      const cmapsize,
-              unsigned int      const x,
-              unsigned int      const y,
-              unsigned int      const imageSize,
-              unsigned long int const compression) {
+BMPlenfileGen(enum bmpClass const class,
+              unsigned int  const bitcount, 
+              unsigned int  const cmapsize,
+              unsigned int  const x,
+              unsigned int  const y,
+              unsigned int  const imageSize,
+              BMPCompType   const compression) {
 /*----------------------------------------------------------------------------
   Return the size of the BMP file in bytes.
 -----------------------------------------------------------------------------*/
     unsigned int retval;
 
     switch (compression) {
-    case COMP_RGB:
-    case COMP_BITFIELDS:
+    case BMPCOMP_RGB:
+    case BMPCOMP_BITFIELDS:
         retval =
             BMPoffbits(class, bitcount, cmapsize) +
             BMPlenbits(class, bitcount, x, y);
@@ -250,7 +274,7 @@ BMPlenfile(enum bmpClass const class,
 /*----------------------------------------------------------------------------
   return the size of the BMP file in bytes; no compression
 -----------------------------------------------------------------------------*/
-    return BMPlenfileGen(class, bitcount, cmapsize, x, y, 0, COMP_RGB);
+    return BMPlenfileGen(class, bitcount, cmapsize, x, y, 0, BMPCOMP_RGB);
 }
 
 #endif
diff --git a/converter/other/Makefile b/converter/other/Makefile
index 077db195..6a3d14ed 100644
--- a/converter/other/Makefile
+++ b/converter/other/Makefile
@@ -7,12 +7,20 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 include $(BUILDDIR)/config.mk
 
-ifeq ($(shell xml2-config --version),)
-  XML2_LIBS=NONE
-  XML2_CFLAGS=NONE
+TEST_PKGCONFIG_LIBXML2 = if pkg-config libxml-2.0; then echo exists; fi
+
+ifneq ($(shell $(TEST_PKGCONFIG_LIBXML2)),)
+  # pkg-config libxml2 works on this system
+  XML2_LIBS = $(shell pkg-config libxml-2.0 --libs)
+  XML2_CFLAGS = $(shell pkg-config libxml-2.0 --cflags)
 else
-  XML2_LIBS=$(shell xml2-config --libs)
-  XML2_CFLAGS=$(shell xml2-config --cflags)
+  ifeq ($(shell xml2-config --version),)
+    XML2_LIBS=NONE
+    XML2_CFLAGS=NONE
+  else
+    XML2_LIBS=$(shell xml2-config --libs)
+    XML2_CFLAGS=$(shell xml2-config --cflags)
+  endif
 endif
 
 SUBDIRS = jbig pnmtopalm jpeg2000 cameratopam pamtosvg
@@ -26,19 +34,29 @@ ifneq ($(TIFFLIB),NONE)
   endif
 endif
 
-ifeq ($(shell libpng$(PNGVER)-config --version),)
-  ifneq ($(PNGLIB),NONE)
+TEST_PKGCONFIG_LIBPNG = if pkg-config libpng$(PNGVER); then echo exists; fi
+
+ifneq ($(shell $(TEST_PKGCONFIG_LIBPNG)),)
+  # pkg-config libpng works on this system
+  HAVE_PNGLIB = Y
+  EXTERN_INCLUDES += $(shell pkg-config libpng$(PNGVER) --cflags)
+else
+  ifneq ($(shell libpng$(PNGVER)-config --version),)
+    # No pkg-config, but we have libpng-config on this system
     HAVE_PNGLIB = Y
-    ifneq ($(PNGHDR_DIR)x,x)
-      EXTERN_INCLUDES += -I$(PNGHDR_DIR)
-    endif
-    ifneq ($(ZHDR_DIR)x,x)
-      EXTERN_INCLUDES += -I$(ZHDR_DIR)
+    EXTERN_INCLUDES += $(shell libpng$(PNGVER)-config --cflags)
+  else
+    # System can't tell us where libpng is; use stuff from config.mk
+    ifneq ($(PNGLIB),NONE)
+      HAVE_PNGLIB = Y
+      ifneq ($(PNGHDR_DIR)x,x)
+        EXTERN_INCLUDES += -I$(PNGHDR_DIR)
+      endif
+      ifneq ($(ZHDR_DIR)x,x)
+        EXTERN_INCLUDES += -I$(ZHDR_DIR)
+      endif
     endif
   endif
-else
-  HAVE_PNGLIB = Y
-  EXTERN_INCLUDES += $(shell libpng$(PNGVER)-config --cflags)
 endif
 
 ifneq ($(JPEGLIB),NONE)
@@ -77,49 +95,66 @@ ifeq ($(TIFFLIB_NEEDS_Z),Y)
   endif
 endif
 
-PORTBINARIES =  bmptopnm fitstopnm \
+# TIFFLIB_USERLIBS is for the user to set manually, on the make command line
+# or manually added to config.mk.  There are arcane situations where the TIFF
+# library refers to additional libraries not handled above.
+
+TIFFLIB_EXTRALIBS += $(TIFFLIB_USERLIBS)
+
+# Pnmtops's "flate" compression function requires libz.  But if we don't
+# have libz, we still build Pnmtops; we just omit the flate compression
+# capability.
+ifeq ($(ZLIB),NONE)
+  PNMTOPS_ZLIB_OPT =
+  PNMTOPS_NOFLATE_OPT = -DNOFLATE
+else
+  PNMTOPS_ZLIB_OPT = $(ZLIB)
+  PNMTOPS_NOFLATE_OPT =
+endif
+
+
+PORTBINARIES =  avstopam bmptopnm fitstopnm \
 		gemtopnm giftopnm hdifftopam infotopam \
-		pamtodjvurle pamtofits pamtogif \
+		pamtoavs pamtodjvurle pamtofits pamtogif \
 		pamtohdiff pamtohtmltbl pamtompfont pamtooctaveimg \
-		pamtopam pamtopfm pamtopnm pamtouil \
-		pamtoxvmini \
-		pbmtopgm pfmtopam \
+		pamtopam pamtopdbimg pamtopfm pamtopnm pamtosrf pamtouil \
+		pamtowinicon pamtoxvmini \
+		pbmtopgm pdbimgtopam pfmtopam \
 	        pgmtopbm pgmtoppm ppmtopgm pnmtoddif \
-		pnmtopclxl \
+		pnmtopclxl pnmtorast \
 		pnmtosgi pnmtosir pamtotga pnmtoxwd \
-		rlatopam sgitopnm sirtopnm xwdtopnm zeisstopnm
+		rasttopnm rlatopam sgitopnm sirtopnm srftopam sunicontopnm \
+		winicontopam xwdtopnm yuy2topam zeisstopnm
 
 ifneq ($(DONT_HAVE_PROCESS_MGMT),Y)
-  PORTBINARIES += pstopnm
+  PORTBINARIES += pstopnm pnmtops
 endif
 
-BINARIES = $(PORTBINARIES) pnmtorast rasttopnm
-
 ifeq ($(HAVE_PNGLIB),Y)
-  BINARIES += pnmtopng pngtopnm pngtopam pamrgbatopng
+  PORTBINARIES += pamtopng pnmtopng pngtopam
 endif
 ifneq ($(JPEGLIB),NONE)
-  BINARIES += jpegtopnm pnmtojpeg
+  PORTBINARIES += jpegtopnm pnmtojpeg
 endif
 ifneq ($(TIFF_PREREQ_MISSING),Y)
-  BINARIES += tifftopnm pamtotiff pnmtotiffcmyk
+  PORTBINARIES += tifftopnm pamtotiff pnmtotiffcmyk
 endif
 ifneq ($(URTLIB),NONE)
-  BINARIES += rletopnm pnmtorle
-endif
-ifneq ($(ZLIB),NONE)
-  BINARIES += pnmtops
+  PORTBINARIES += rletopnm pnmtorle
 endif
 
 ifneq ($(XML2_LIBS),NONE)
-  BINARIES += svgtopam
+  PORTBINARIES += svgtopam
 endif 
 
+BINARIES = $(PORTBINARIES)
+
 MERGEBINARIES = $(BINARIES)
 
-EXTRA_OBJECTS = exif.o rast.o bmepsoe.o
+EXTRA_OBJECTS = exif.o rast.o ipdb.o srf.o
 ifeq ($(HAVE_PNGLIB),Y)
   EXTRA_OBJECTS += pngtxt.o
+  EXTRA_OBJECTS += pngx.o
 endif
 ifneq ($(JPEGLIB),NONE)
   EXTRA_OBJECTS += jpegdatasource.o
@@ -131,6 +166,7 @@ endif
 OBJECTS = $(BINARIES:%=%.o) $(EXTRA_OBJECTS)
 MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2) $(EXTRA_OBJECTS)
 
+pnmtops.o pnmtops.o2: CFLAGS_TARGET=$(PNMTOPS_NOFLATE_OPT)
 
 SCRIPTS = anytopnm pnmtoplainpnm
 
@@ -145,69 +181,68 @@ else
   LIBOPTR =
 endif
 
-LIBOPTS_TIFF = $(shell $(LIBOPT) $(NETPBMLIB) \
+LIBOPTS_TIFF = $(shell $(LIBOPT) \
   $(LIBOPTR) $(TIFFLIB) $(TIFFLIB_EXTRALIBS))
 
-tifftopnm pamtotiff pnmtotiffcmyk: %: %.o tiff.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $@.o tiff.o \
-	  $(LIBOPTS_TIFF) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
+tifftopnm pamtotiff pnmtotiffcmyk: tiff.o
+tifftopnm pamtotiff pnmtotiffcmyk: ADDL_OBJECTS = tiff.o
+tifftopnm pamtotiff pnmtotiffcmyk: \
+  LDFLAGS_TARGET = \
+ $(shell $(LIBOPT)  $(LIBOPTR) $(TIFFLIB) $(TIFFLIB_EXTRALIBS))
 
-ifeq ($(shell libpng$(PNGVER)-config --version),)
-  PNGLIB_LIBOPTS = $(shell $(LIBOPT) $(LIBOPTR) $(PNGLIB) $(ZLIB))
+ifneq ($(shell $(TEST_PKGCONFIG_LIBPNG)),)
+  # pkg-config libpng works on this system
+  PNGLIB_LIBOPTS = $(shell pkg-config libpng$(PNGVER) --libs)
 else
-  PNGLIB_LIBOPTS = $(shell libpng$(PNGVER)-config --ldflags)
+  ifneq ($(shell libpng$(PNGVER)-config --version),)
+    # No pkg-config, but we have libpng-config on this system
+    PNGLIB_LIBOPTS = $(shell libpng$(PNGVER)-config --ldflags)
+  else
+    # System can't tell us where libpng is; use stuff from config.mk
+    PNGLIB_LIBOPTS = $(shell $(LIBOPT) $(LIBOPTR) $(PNGLIB) $(ZLIB))
+  endif
 endif
 
-pngtopnm pngtopam: %: %.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $@.o \
-	  $(shell $(LIBOPT) $(NETPBMLIB)) \
-	  $(PNGLIB_LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
+pngtopam: pngx.o
+pngtopam: ADDL_OBJECTS = pngx.o
+pngtopam: LDFLAGS_TARGET = $(PNGLIB_LIBOPTS)
+
+pamtopng: pngx.o pngtxt.o
+pamtopng: ADDL_OBJECTS = pngx.o pngtxt.o
+pamtopng: LDFLAGS_TARGET = $(PNGLIB_LIBOPTS)
 
-pnmtopng: %: %.o pngtxt.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $@.o pngtxt.o \
-	  $(shell $(LIBOPT) $(NETPBMLIB)) \
-	  $(PNGLIB_LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
+pnmtopng: pngx.o pngtxt.o
+pnmtopng: ADDL_OBJECTS = pngx.o pngtxt.o
+pnmtopng: LDFLAGS_TARGET = $(PNGLIB_LIBOPTS)
 
-pamrgbatopng: %: %.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $@.o \
-	  $(shell $(LIBOPT) $(NETPBMLIB)) $(PNGLIB_LIBOPTS) \
-	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
+jpegtopnm: jpegdatasource.o exif.o
+jpegtopnm: ADDL_OBJECTS = jpegdatasource.o exif.o
+jpegtopnm: LDFLAGS_TARGET = $(shell $(LIBOPT) $(LIBOPTR) $(JPEGLIB))
 
-jpegtopnm: %: %.o jpegdatasource.o exif.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $< jpegdatasource.o exif.o \
-	  $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR) $(JPEGLIB)) \
-	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD) 
+srftopam pamtosrf: srf.o
+srftopam pamtosrf: ADDL_OBJECTS = srf.o
 
-pnmtojpeg: %: %.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $@.o \
-	  $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR) $(JPEGLIB)) \
-	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
+pnmtojpeg: LDFLAGS_TARGET = $(shell $(LIBOPT) $(LIBOPTR) $(JPEGLIB))
 
-svgtopam: %: %.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $@.o \
-	  $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR)) \
-	  $(XML2_LIBS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
+svgtopam: LDFLAGS_TARGET = $(XML2_LIBS)
 
 # If URTLIB is BUNDLED_URTLIB, then we're responsible for building it, which
 # means it needs to be a dependency:
 ifeq ($(URTLIB), $(BUNDLED_URTLIB))
-  URTLIBDEP = $(URTLIB)
+rletopnm pnmtorle: $(URTLIB)
 endif
 
-rletopnm pnmtorle: %: %.o $(NETPBMLIB) $(URTLIBDEP) $(LIBOPT)
-	$(LD) -o $@ $@.o \
-	  $(shell $(LIBOPT) $(URTLIB) $(NETPBMLIB)) \
-	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
+rletopnm pnmtorle: LDFLAGS_TARGET = $(shell $(LIBOPT) $(URTLIB))
+
+pnmtops: LDFLAGS_TARGET = $(shell $(LIBOPT) $(PNMTOPS_ZLIB_OPT))
+
+pnmtorast rasttopnm: rast.o
+pnmtorast rasttopnm: ADDL_OBJECTS = rast.o
 
-pnmtops: %: %.o bmepsoe.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $@.o bmepsoe.o \
-	  $(shell $(LIBOPT) $(NETPBMLIB) $(ZLIB)) \
-	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
+pdbimgtopam pamtopdbimg: ipdb.o
+pdbimgtopam pamtopdbimg: ADDL_OBJECTS = ipdb.o
 
-pnmtorast rasttopnm: %: %.o rast.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $@.o rast.o \
-	  $(shell $(LIBOPT) $(NETPBMLIB)) \
-	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
+# Declare dependencies on created header files (symbolic links, actually).
 
 bmptopnm.o bmptopnm.o2: bmp.h
 
@@ -248,3 +283,16 @@ ifneq ($(TIFF_PREREQ_MISSING),Y)
 	cd $(PKGDIR)/bin ; \
 	$(SYMLINK) pamtotiff$(EXE) pnmtotiff$(EXE)
 endif
+ifeq ($(HAVE_PNGLIB),Y)
+# In September 2009, pngtopam replaced pngtopnm
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) pngtopam$(EXE) pngtopnm$(EXE)
+endif
+# In December 2010, sunicontopnm replaced icontopbm
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) sunicontopnm$(EXE) icontopbm$(EXE)
+ifeq ($(HAVE_PNGLIB),Y)
+# In June 2015, pamtopng replaced pamrgbapng
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) pamtopng$(EXE) pamrgbatopng$(EXE)
+endif
diff --git a/converter/other/anytopnm b/converter/other/anytopnm
index 06f48b4f..397faae5 100755
--- a/converter/other/anytopnm
+++ b/converter/other/anytopnm
@@ -150,7 +150,8 @@ computeTypeFromTypeDescription () {
             filetype=tiff
             ;;
     
-        *PC*bitmap*data* )
+        # We have seen "PC bitmap" and "PC bitmap data"
+        *PC*bitmap* )
             filetype=bmp
             ;;
         
@@ -399,7 +400,7 @@ case "$2" in
         ;;
 
     gif )
-        giftopnm "$file"
+        giftopnm -image=all "$file"
         ;;
 
     tiff )
@@ -437,7 +438,7 @@ case "$2" in
         ;;
 
     png )
-        pngtopnm "$file"
+        pngtopam "$file"
         ;;
 
     mda )
diff --git a/converter/other/avstopam.c b/converter/other/avstopam.c
new file mode 100644
index 00000000..322f387d
--- /dev/null
+++ b/converter/other/avstopam.c
@@ -0,0 +1,103 @@
+/* ----------------------------------------------------------------------
+ *
+ * Convert an AVS X image to a PAM image
+ *
+ * By Scott Pakin <scott+pbm@pakin.org>
+ *
+ * ----------------------------------------------------------------------
+ *
+ * Copyright (C) 2010 Scott Pakin <scott+pbm@pakin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+
+#include "pm.h"
+#include "pam.h"
+
+
+
+static void
+producePam(FILE *       const avsFileP,
+           struct pam * const pamP) {
+
+    tuple *      tuplerow;
+    unsigned int row;
+
+    tuplerow = pnm_allocpamrow(pamP);
+    for (row = 0; row < pamP->height; ++row) {
+        unsigned int col;
+        for (col = 0; col < pamP->width; ++col) {
+            tuple const thisTuple = tuplerow[col];
+            unsigned char c;
+            pm_readcharu(avsFileP, &c); thisTuple[3] = c;
+            pm_readcharu(avsFileP, &c); thisTuple[0] = c;
+            pm_readcharu(avsFileP, &c); thisTuple[1] = c;
+            pm_readcharu(avsFileP, &c); thisTuple[2] = c;
+        }
+        pnm_writepamrow(pamP, tuplerow);
+    }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    const char * comment = "Produced by avstopam";  /* constant */
+
+    struct pam   outPam;
+    const char * inputFilename;
+    FILE       * inFileP;
+    long         width;
+    long         height;
+
+    pm_proginit(&argc, argv);
+
+    inputFilename = (argc > 1) ? argv[1] : "-";
+
+    inFileP = pm_openr(inputFilename);
+
+    pm_readbiglong(inFileP, &width);
+    pm_readbiglong(inFileP, &height);
+
+    outPam.size             = sizeof(struct pam);
+    outPam.len              = PAM_STRUCT_SIZE(comment_p);
+    outPam.file             = stdout;
+    outPam.format           = PAM_FORMAT;
+    outPam.plainformat      = 0;
+    outPam.width            = width;
+    outPam.height           = height;
+    outPam.depth            = 4;
+    outPam.maxval           = 255;
+    outPam.bytes_per_sample = 1;
+    sprintf(outPam.tuple_type, "RGB_ALPHA");
+    outPam.allocation_depth = 4;
+    outPam.comment_p        = &comment;
+
+    /* Produce a PAM output header.  Note that AVS files *always*
+       contain four channels with one byte per channel.
+    */
+    pnm_writepaminit(&outPam);
+
+    producePam(inFileP, &outPam);
+
+    pm_closer(inFileP);
+
+    return 0;
+}
diff --git a/converter/other/bmepsoe.c b/converter/other/bmepsoe.c
deleted file mode 100644
index 02bf39aa..00000000
--- a/converter/other/bmepsoe.c
+++ /dev/null
@@ -1,560 +0,0 @@
-/* 
- * This was adapted for Netpbm from bmpesoe.c in Dirk Krause's Bmeps package
- * by Bryan Henderson on 2005.01.05.
- *
- * Differences:
- *   - doesn't require Bmeps configuration stuff (bmepsco.h)
- *   - doesn't include pngeps.h
- *   - doesn't have test scaffold code
- *   - a few compiler warnings fixed
- *
- * Copyright (C) 2000 - Dirk Krause
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- * In this package the copy of the GNU Library General Public License
- * is placed in file COPYING.
- */
-
-#include "bmepsoe.h"
-
-#define RL_RUNLENGTH(i) (257 - (i))
-#define RL_STRINGLENGTH(i) ((i) - 1)
-#define RL_MAXLENGTH (127)
-
-#define FINALOUTPUT(c) fputc((c),o->out)
-
-void oe_init(Output_Encoder *o, FILE *out, int mode, int rate, int *buf,
-  Bytef *flib, size_t flis, Bytef *flob, size_t flos
-)
-{
-  
-  o->out = out;
-  o->mode = mode;
-  o->textpos = 0;
-  o->a85_value = 0UL; o->a85_consumed = 0;
-  o->a85_0 = 1UL;
-  o->a85_1 = 85UL;
-  o->a85_2 = 85UL * 85UL;
-  o->a85_3 = 85UL * o->a85_2;
-  o->a85_4 = 85UL * o->a85_3;
-  o->rl_lastbyte = 0;
-  o->rl_buffer = buf;
-  o->rl_bufused = 0;
-  o->rl_state = 0;
-  o->bit_value = 0;
-  o->bit_consumed = 0;
-  o->flate_rate = rate;
-  if(o->flate_rate < 0) {
-    o->mode &= (~OE_FLATE);
-  }
-  if(o->flate_rate > 9) {
-    o->flate_rate = 9;
-  }
-  if((o->mode & OE_FLATE) && flib && flis && flob && flos) {
-    (o->flate_stream).zfree = (free_func)0;
-    (o->flate_stream).zalloc = (alloc_func)0;
-    (o->flate_stream).opaque = (voidpf)0;
-    if(deflateInit((&(o->flate_stream)),o->flate_rate) != Z_OK) {
-      o->mode &= (~OE_FLATE);
-    }
-  }
-  o->fl_i_buffer = flib;
-  o->fl_o_buffer = flob;
-  o->fl_i_size   = flis;
-  o->fl_o_size   = flos;
-  o->fl_i_used   = 0;
-  
-}
-
-static char hexdigits[] = {
-  "0123456789ABCDEF"
-};
-
-static void asciihex_add(Output_Encoder *o, int b)
-{
-  
-  FINALOUTPUT(hexdigits[(b/16)]) ;
-  FINALOUTPUT(hexdigits[(b%16)]) ;
-  o->textpos = o->textpos + 2;
-  if(o->textpos >= 64) {
-    FINALOUTPUT('\n') ;
-    o->textpos = 0;
-  }
-  
-}
-
-static void asciihex_flush(Output_Encoder *o)
-{
-  
-  if(o->textpos > 0) {
-    FINALOUTPUT('\n') ;
-    o->textpos = 0;
-  }
-  
-}
-
-static char   ascii85_char(unsigned long x)
-{
-  unsigned u;
-  int      i;
-  char back;
-  back = (char)0;
-  u = (unsigned)x;
-  i = (int)u;
-  i += 33;
-  back = (char)i;
-  return back;
-}
-
-
-
-static void
-ascii85_output(Output_Encoder * const o) {
-    unsigned long value;
-
-    value = o->a85_value;  /* initial value */
-
-    if (value == 0 && o->a85_consumed == 4) {
-        FINALOUTPUT('z');
-        ++o->textpos;
-    } else {
-        unsigned int i;
-        unsigned int j;
-        char buffer[6];
-
-        buffer[0] = ascii85_char(value / (o->a85_4));
-        value = value % (o->a85_4);
-        buffer[1] = ascii85_char(value / (o->a85_3));
-        value = value % (o->a85_3);
-        buffer[2] = ascii85_char(value / (o->a85_2));
-        value = value % (o->a85_2);
-        buffer[3] = ascii85_char(value / (o->a85_1));
-        value = value % (o->a85_1);
-        buffer[4] = ascii85_char(value);
-        buffer[5] = '\0';
-
-        i = o->a85_consumed + 1;
-        o->textpos += i;
-
-        for (j = 0; j < i; ++j)
-            FINALOUTPUT(buffer[j]);
-    }
-    if (o->textpos >= 64) {
-        FINALOUTPUT('\n');
-        o->textpos = 0;
-    }
-}
-
-
-
-static void ascii85_add(Output_Encoder *o, int b)
-{
-  unsigned u;
-  unsigned long ul;
-  
-  u = (unsigned)b;
-  ul = (unsigned long)u;
-  o->a85_value = 256UL * (o->a85_value) + ul;
-  o->a85_consumed = o->a85_consumed + 1;
-  if(o->a85_consumed >= 4) {
-    ascii85_output(o);
-    o->a85_value = 0UL;
-    o->a85_consumed = 0;
-  }
-  
-}
-
-static void ascii85_flush(Output_Encoder *o)
-{
-  int i;
-  
-  if(o->a85_consumed > 0) {
-    i = o->a85_consumed;
-    while(i < 4) {
-      o->a85_value = 256UL * o->a85_value;
-      i++;
-    }
-    ascii85_output(o);
-    o->a85_value = 0UL;
-    o->a85_consumed = 0;
-  }
-  if(o->textpos > 0) {
-    FINALOUTPUT('\n') ;
-    o->textpos = 0;
-  }
-  
-}
-
-static void after_flate_add(Output_Encoder *o, int b)
-{
-  
-  if(o->mode & OE_ASC85) {
-    ascii85_add(o,b);
-  } else {
-    asciihex_add(o,b);
-  }
-  
-}
-
-
-
-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 - (oP->flate_stream.avail_out);
-                        while (xs--) {
-                            after_flate_add(oP, *(xptr++));
-                        }
-                    } break;
-                    case Z_OK : {
-                        mustContinue = 1;
-                        xptr = optr;
-          
-                        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 - (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;
-  Bytef *btptr;
-  
-  btptr = o->fl_i_buffer;
-  if(btptr) { 
-    bt = (Byte)b; 
-    btptr[(o->fl_i_used)] = bt;
-    o->fl_i_used += 1UL;
-    if(o->fl_i_used >= o->fl_i_size) {
-      do_flate_flush(o, 0);
-      o->fl_i_used = 0UL;
-    }
-  }
-  
-}
-
-static void after_flate_flush(Output_Encoder *o)
-{
-  
-  if(o->mode & OE_ASC85) {
-    ascii85_flush(o);
-  } else {
-    asciihex_flush(o);
-  }
-  
-}
-
-static void flate_flush(Output_Encoder *o)
-{
-  do_flate_flush(o,1);
-  deflateEnd(&(o->flate_stream));
-  after_flate_flush(o);
-}
-
-
-static void after_rl_add(Output_Encoder *o, int b)
-{
-  
-  if(o->mode & OE_FLATE) {
-    flate_add(o,b);
-  } else {
-    after_flate_add(o,b);
-  }
-  
-}
-
-static void rl_add(Output_Encoder *o, int b)
-{
-  int lgt, i;
-  int *buffer;
-  /* ##### */
-  
-  buffer = o->rl_buffer;
-  lgt = o->rl_bufused;
-  if(buffer) {
-    
-    if(lgt > 0) {
-      if(o->rl_lastbyte == b) {
-    switch(o->rl_state) {
-      case 2: {
-        buffer[lgt++] = b;
-        o->rl_bufused = lgt;
-        o->rl_state = 2;
-        o->rl_lastbyte = b;
-        if(lgt >= RL_MAXLENGTH) {
-          after_rl_add(o, RL_RUNLENGTH(lgt));
-          after_rl_add(o, b);
-          o->rl_bufused = 0;
-          o->rl_state = 0;
-          o->rl_lastbyte = b;
-        }
-      } break;
-      case 1: {
-        buffer[lgt++] = b;
-        o->rl_bufused = lgt;
-        o->rl_state = 2;
-        o->rl_lastbyte = b;
-        lgt = lgt - 3;
-        if(lgt > 0) {
-          after_rl_add(o, RL_STRINGLENGTH(lgt));
-          for(i = 0; i < lgt; i++) {
-        after_rl_add(o, buffer[i]);
-          }
-          buffer[0] = buffer[1] = buffer[2] = b;
-          o->rl_bufused = 3;
-          o->rl_state = 2;
-          o->rl_lastbyte = b;
-        }
-      } break;
-      default: {
-        buffer[lgt++] = b;
-        o->rl_bufused = lgt;
-        o->rl_state = 1;
-        o->rl_lastbyte = b;
-        if(lgt >= RL_MAXLENGTH) {
-          lgt = lgt - 2;
-          after_rl_add(o, RL_STRINGLENGTH(lgt));
-          for(i = 0; i < lgt; i++) {
-        after_rl_add(o, buffer[i]);
-          }
-          buffer[0] = buffer[1] = b;
-          o->rl_bufused = 2;
-          o->rl_state = 1;
-          o->rl_lastbyte = b;
-        }
-      } break;
-    }
-      } else {
-    if(o->rl_state == 2) {
-      after_rl_add(o, RL_RUNLENGTH(lgt));
-      after_rl_add(o, (o->rl_lastbyte));
-      buffer[0] = b; o->rl_bufused = 1; o->rl_lastbyte = b;
-      o->rl_state = 0;
-    } else {
-      buffer[lgt++] = b;
-      o->rl_bufused = lgt;
-      o->rl_lastbyte = b;
-      if(lgt >= RL_MAXLENGTH) {
-        after_rl_add(o, RL_STRINGLENGTH(lgt));
-        for(i = 0; i < lgt; i++) {
-          after_rl_add(o, buffer[i]);
-        }
-        o->rl_bufused = 0;
-      }
-      o->rl_state = 0;
-    }
-      }
-    } else {
-      buffer[0] = b;
-      o->rl_bufused = 1;
-      o->rl_lastbyte = b;
-    }
-    o->rl_lastbyte = b;
-    
-  } else { 
-    after_rl_add(o,0);
-    after_rl_add(o,b);
-  }
-  
-}
-
-static void after_rl_flush(Output_Encoder *o)
-{
-  
-  if(o->mode & OE_FLATE) {
-    flate_flush(o);
-  } else {
-    after_flate_flush(o);
-  }
-  
-}
-
-static void rl_flush(Output_Encoder *o)
-{
-  int lgt;
-  int *buffer;
-  int i;
-  
-  buffer = o->rl_buffer;
-  lgt = o->rl_bufused;
-  if(lgt > 0) {
-    if(o->rl_state == 2) {
-      i = o->rl_lastbyte;
-      after_rl_add(o,RL_RUNLENGTH(lgt));
-      after_rl_add(o,i);
-    } else {
-      after_rl_add(o,RL_STRINGLENGTH(lgt));
-      for(i = 0; i < lgt; i++) {
-    after_rl_add(o,buffer[i]);
-      }
-    }
-  }
-  after_rl_flush(o);
-  
-}
-
-
-
-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)
-{
-  
-  if((o->mode) & OE_RL) {
-    rl_flush(o);
-  } else {
-    after_rl_flush(o);
-  }
-  
-}
-
-
-
-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)
-{
-  
-  if(o->bit_consumed) {
-    int v, i;
-    v = o->bit_value;
-    i = o->bit_consumed;
-    while(i < 8) {
-      i++;
-      v = v * 2;
-    }
-    internal_byte_add(o,v);
-    o->bit_value = 0;
-    o->bit_consumed = 0;
-  }
-  internal_byte_flush(o);
-  
-}
-
-
-
-void
-oe_byte_add(Output_Encoder * const oP,
-            int              const b) {
-  
-    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);
-    }
-}
-
-
-
-void oe_byte_flush(Output_Encoder *o)
-{
-  
-  oe_bit_flush(o);
-  
-}
diff --git a/converter/other/bmepsoe.h b/converter/other/bmepsoe.h
deleted file mode 100644
index 99c59f77..00000000
--- a/converter/other/bmepsoe.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/* 
- * libbmeps - Bitmap to EPS conversion library
- * Copyright (C) 2000 - Dirk Krause
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- * In this package the copy of the GNU Library General Public License
- * is placed in file COPYING.
- */
-
-#ifndef BMPESOE_H_INCLUDED
-#define BMPESOE_H_INCLUDED
-
-#include <stdio.h>
-
-#include <zlib.h>
-
-typedef struct {
-  int mode;
-  FILE *out;
-  int textpos;
-  unsigned long a85_value;
-  int      a85_consumed;
-  unsigned long a85_4;
-  unsigned long a85_3;
-  unsigned long a85_2;
-  unsigned long a85_1;
-  unsigned long a85_0;
-  int rl_lastbyte;
-  int *rl_buffer;
-  int rl_bufused;
-  int rl_state;
-  int bit_value;
-  int bit_consumed;
-  z_stream flate_stream;
-  Bytef *fl_i_buffer;
-  Bytef *fl_o_buffer;
-  uLong  fl_i_size;
-  uLong  fl_o_size;
-  uLong  fl_i_used;
-  int    flate_rate;
-} Output_Encoder;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void oe_init(Output_Encoder *o, FILE *out, int mode, int rate, int *buf,
-  Bytef *flib, size_t flis, Bytef *flob, size_t flos
-);
-void oe_byte_add(Output_Encoder *o, int b);
-void oe_byte_flush(Output_Encoder *o);
-void oe_bit_add(Output_Encoder *o, int b);
-void oe_bit_flush(Output_Encoder *o);
-
-#ifdef __cplusplus
-}
-#endif
-
-
-
-#define OE_ASC85 1
-#define OE_FLATE 2
-#define OE_RL    4
-
-#endif
-
diff --git a/converter/other/bmptopnm.c b/converter/other/bmptopnm.c
index fb89433c..bf4d10f8 100644
--- a/converter/other/bmptopnm.c
+++ b/converter/other/bmptopnm.c
@@ -28,14 +28,12 @@
 #include <assert.h>
 
 #include "pm_c_util.h"
-#include "pnm.h"
+#include "mallocvar.h"
 #include "shhopt.h"
 #include "nstring.h"
+#include "pnm.h"
 #include "bmp.h"
 
-/* MAXCOLORS is the maximum size of a color map in a BMP image */
-#define MAXCOLORS       256
-
 static xelval const bmpMaxval = 255;
     /* The maxval for intensity values in a BMP image -- either in a
        truecolor raster or in a colormap
@@ -103,59 +101,57 @@ struct bmpInfoHeader {
         /* Size in bytes of the image data.  We only reference this 
            when the image is compressed. */    
     unsigned short cPlanes;
-    unsigned long int compression;
+    BMPCompType compression;
     struct pixelformat pixelformat;
 };
 
 
 
-struct cmdline_info {
+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;  /* Filespecs of input files */
-    int verbose;    /* -verbose option */
+    const char * inputFileName;
+    unsigned int verbose;
 };
 
-static const char *ifname;
+static const char * ifname;
 
 
 
 static void
-parse_command_line(int argc, char ** argv,
-                   struct cmdline_info *cmdline_p) {
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optStruct *option_def = malloc(100*sizeof(optStruct));
+    optEntry * option_def;
         /* Instructions to OptParseOptions2 on how to parse our options.
          */
-    optStruct2 opt;
+    optStruct3 opt;
 
     unsigned int option_def_index;
 
-    option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENTRY(0,   "verbose",     OPT_FLAG,   &cmdline_p->verbose,         0);
- 
-    /* Set the defaults */
-    cmdline_p->verbose = FALSE;
+    MALLOCARRAY_NOFAIL(option_def, 100);
 
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "verbose",     OPT_FLAG,   NULL, &cmdlineP->verbose,   0);
+ 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions2(&argc, argv, opt, 0);
-        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 == 0) 
-        cmdline_p->input_filespec = "-";
+        cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
                  "specified %d", argc-1);
     else
-        cmdline_p->input_filespec = argv[1];
-
+        cmdlineP->inputFileName = argv[1];
 }
 
 
@@ -254,13 +250,13 @@ readOffBytes(FILE * const fp, unsigned int const nbytes) {
 
 
 static void
-BMPreadfileheader(FILE *         const ifP, 
+bmpReadfileheader(FILE *         const ifP, 
                   unsigned int * const bytesReadP, 
                   unsigned int * const offBitsP) {
 
-    unsigned short  xHotSpot;
-    unsigned short  yHotSpot;
-    unsigned long   offBits;
+    unsigned short    xHotSpot;
+    unsigned short    yHotSpot;
+    unsigned long     offBits;
     unsigned long int fileSize;
 
 
@@ -273,9 +269,9 @@ BMPreadfileheader(FILE *         const ifP,
 
 
     fileSize = GetLong(ifP);  /* This is not always reliable. */
-    xHotSpot  = GetShort(ifP);
-    yHotSpot  = GetShort(ifP);
-    offBits   = GetLong(ifP);
+    xHotSpot = GetShort(ifP);
+    yHotSpot = GetShort(ifP);
+    offBits  = GetLong(ifP);
 
     *offBitsP = offBits;
 
@@ -329,7 +325,7 @@ readOs2InfoHeader(FILE *                 const ifP,
                  
     headerP->pixelformat = defaultPixelformat(headerP->cBitCount);
 
-    headerP->compression = COMP_RGB;
+    headerP->compression = BMPCOMP_RGB;
     
     pm_message("OS/2 BMP, %dx%dx%d",
                headerP->cols, headerP->rows, headerP->cBitCount);
@@ -342,26 +338,26 @@ 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 ) 
+    if (compression != BMPCOMP_RGB && compression != BMPCOMP_BITFIELDS &&
+        compression != BMPCOMP_RLE4 && compression != BMPCOMP_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);
+                 compression, BMPCOMP_RGB, BMPCOMP_BITFIELDS,
+                 BMPCOMP_RLE4, BMPCOMP_RLE8);
                      
-    if ((compression == COMP_RLE4 || compression == COMP_RLE8) &&
+    if ((compression == BMPCOMP_RLE4 || compression == BMPCOMP_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) ) 
+    if ((compression == BMPCOMP_RLE4 && cBitCount !=4 ) ||
+        (compression == BMPCOMP_RLE8 && cBitCount !=8 )) 
         pm_error("Invalid BMP header.  " 
                  "Compression type (%s) disagrees with "
                  "number of bits per pixel (%u).",
-                 compression == COMP_RLE4 ? "RLE4" : "RLE8",
+                 compression == BMPCOMP_RLE4 ? "RLE4" : "RLE8",
                  cBitCount);
 }
 
@@ -379,18 +375,22 @@ readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
 -----------------------------------------------------------------------------*/
     int colorsimportant;   /* ColorsImportant value from header */
     int colorsused;        /* ColorsUsed value from header */
-    long colsField;
+    unsigned short planesField, bitCountField;
+    int32_t colsField;
 
     headerP->class = C_WIN;
 
-    colsField = GetLong(ifP);
-
-    if (colsField <= 0)
-        pm_error("Invalid BMP file: says width is %ld", colsField);
+    pm_readlittlelong2(ifP, &colsField);
 
-    headerP->cols = (unsigned long)colsField;
+    if (colsField == 0)
+        pm_error("Invalid BMP file: says width is zero");
+    else if (colsField < 0)
+        pm_error("Invalid BMP file: says width is negative (%d)", colsField);
+    else
+        headerP->cols = (unsigned int)colsField;
     {
         long const cy = GetLong(ifP);
+
         if (cy == 0)
             pm_error("Invalid BMP file: says height is zero");
         if (cy < 0) {
@@ -401,17 +401,18 @@ readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
             headerP->rows = cy;
         }
     }
-    headerP->cPlanes = GetShort(ifP);
-    headerP->cBitCount = GetShort(ifP);
- 
+    pm_readlittleshortu(ifP, &planesField);
+    headerP->cPlanes = planesField;
+    pm_readlittleshortu(ifP, &bitCountField);
+    headerP->cBitCount = bitCountField;
     {
         unsigned long int const compression = GetLong(ifP);
 
-        headerP->bitFields = (compression == COMP_BITFIELDS);
-
         validateCompression(compression, headerP->rowOrder,
                             headerP->cBitCount);
 
+        headerP->bitFields = (compression == BMPCOMP_BITFIELDS);
+
         headerP->compression = compression;             
     }
     /* And read the rest of the junk in the 40 byte header */
@@ -624,7 +625,7 @@ readWindowsInfoHeader(FILE *                 const ifP,
 
 
 static void
-BMPreadinfoheader(FILE *                 const ifP, 
+bmpReadinfoheader(FILE *                 const ifP, 
                   unsigned int *         const bytesReadP,
                   struct bmpInfoHeader * const headerP) {
 
@@ -656,7 +657,7 @@ BMPreadinfoheader(FILE *                 const ifP,
 
 
 static void
-BMPreadcolormap(FILE *         const ifP, 
+bmpReadColormap(FILE *         const ifP, 
                 int            const class, 
                 xel **         const colormapP, 
                 unsigned int   const cmapsize,
@@ -883,8 +884,8 @@ convertRow(unsigned char      const bmprow[],
             unsigned int const index = bmprow[col];
             validateIndex(index, cmapsize);
             xelrow[col] = colormap[index];
-        }
-    } else if (cBitCount < 8) {
+	}
+    } else if (cBitCount == 1 || cBitCount == 2 || cBitCount == 4) {
         /* It's a bit field color index */
         unsigned char const mask = ( 1 << cBitCount ) - 1;
 
@@ -898,18 +899,21 @@ convertRow(unsigned char      const bmprow[],
             validateIndex(index, cmapsize);
             xelrow[col] = colormap[index];
         }
-    } else
-        pm_error("Internal error: invalid cBitCount in convertRow()");
+    } else {
+        /* Every possible BMP bits per pixel is handled above */
+        assert(false);
+    }
 }
 
 
 
 static unsigned char **
-allocBMPraster(unsigned int const rows, unsigned int const bytesPerRow) {
+allocBmpRaster(unsigned int const rows,
+               unsigned int const bytesPerRow) {
 
     unsigned int const storageSize = 
         rows * sizeof(unsigned char *) + rows * bytesPerRow;        
-    unsigned char ** BMPraster;
+    unsigned char ** bmpRaster;
     unsigned int row;
     unsigned char * startOfRows;
 
@@ -920,18 +924,18 @@ allocBMPraster(unsigned int const rows, unsigned int const bytesPerRow) {
     if (UINT_MAX / (bytesPerRow + sizeof(unsigned char *)) < rows)
         pm_error("raster is ridiculously large.");
 
-    BMPraster = (unsigned char **) malloc(storageSize);
+    bmpRaster = (unsigned char **) malloc(storageSize);
 
-    if (BMPraster == NULL)
+    if (bmpRaster == NULL)
         pm_error("Unable to allocate %u bytes for the BMP raster\n",
                  storageSize);
 
-    startOfRows = (unsigned char *)(BMPraster + rows);
+    startOfRows = (unsigned char *)(bmpRaster + rows);
 
     for (row = 0; row < rows; ++row) 
-        BMPraster[row] = startOfRows + row * bytesPerRow;
+        bmpRaster[row] = startOfRows + row * bytesPerRow;
 
-    return BMPraster;
+    return bmpRaster;
 }
 
 
@@ -940,14 +944,14 @@ static void
 readrow(FILE *           const ifP,
         unsigned int     const row,
         unsigned int     const bytesPerRow,
-        unsigned char ** const BMPraster,
+        unsigned char ** const bmpRaster,
         unsigned int *   const bytesReadP) {
 
     size_t bytesRead;
 
     assert(bytesPerRow > 0);
     
-    bytesRead = fread(BMPraster[row], 1, bytesPerRow, ifP);
+    bytesRead = fread(bmpRaster[row], 1, bytesPerRow, ifP);
 
     if (bytesRead < bytesPerRow) {
         if (feof(ifP))
@@ -1023,12 +1027,12 @@ readrowRLE(FILE *           const ifP,
            unsigned int     const row,
            unsigned int     const cols,
            bool             const lastrow,
-           unsigned long    const compression,
-           unsigned char ** const BMPraster,
+           BMPCompType      const compression,
+           unsigned char ** const bmpRaster,
            unsigned int  *  const bytesReadP) {
 
-    bool const RLE4 = (compression == COMP_RLE4);
-    int  const pixelsPerRowMargin = RLE4 ? cols % 2 : 0;
+    bool const rle4 = (compression == BMPCOMP_RLE4);
+    int  const pixelsPerRowMargin = rle4 ? cols % 2 : 0;
 
     char const err_decode[] = 
         "Error while decoding compressed BMP image.  "
@@ -1046,17 +1050,17 @@ readrowRLE(FILE *           const ifP,
     totalBytesRead = 0;  /* Initial value */
     pixelsRead = 0;      /* Initial value */
 
-    while (TRUE) {
+    while (true) {
         unsigned int n;
             /* decompressed bytes already read; current write point */ 
         unsigned int cnt;
         unsigned char code;
 
-        n = RLE4 ? (pixelsRead + 1) / 2 : pixelsRead;
+        n = rle4 ? (pixelsRead + 1) / 2 : pixelsRead;
 
         switch (readRLEcode(ifP, &cnt, &code)) {
         case ENC_MODE: {
-            unsigned int const byteCnt = RLE4 ? (cnt + 1) /2 : cnt;
+            unsigned int const byteCnt = rle4 ? (cnt + 1) /2 : cnt;
             unsigned int i; 
 
             if (pixelsRead + cnt > cols + pixelsPerRowMargin)
@@ -1064,11 +1068,11 @@ readrowRLE(FILE *           const ifP,
                          row, pixelsRead ); 
                  
             for (i = 0; i < byteCnt; ++i)
-                BMPraster[row][n+i] = code;
+                bmpRaster[row][n+i] = code;
                  
-            if (RLE4 && pixelsRead % 2 == 1)
+            if (rle4 && pixelsRead % 2 == 1)
                 /* previous read ended odd */
-                nibbleAlign(&BMPraster[row][n-1], cnt); 
+                nibbleAlign(&bmpRaster[row][n-1], cnt); 
             
             pixelsRead += cnt;
             totalBytesRead += 2;
@@ -1078,13 +1082,13 @@ readrowRLE(FILE *           const ifP,
             unsigned int cmpBytesRead; /* compressed bytes read */
             /* align read-end to 16 bit boundary */
             unsigned int const bytesToRead =
-                RLE4 ? (cnt + 3) / 4 * 2 : (cnt + 1) / 2 * 2;
+                rle4 ? (cnt + 3) / 4 * 2 : (cnt + 1) / 2 * 2;
 
             if (pixelsRead + cnt > cols + pixelsPerRowMargin)
                 pm_error(err_decode,  "Too many pixels in absolute mode",
                          row, pixelsRead); 
 
-            cmpBytesRead = fread(&BMPraster[row][n], 
+            cmpBytesRead = fread(&bmpRaster[row][n], 
                                  sizeof(char), bytesToRead, ifP);
 
             if (cmpBytesRead < bytesToRead) {
@@ -1095,8 +1099,8 @@ readrowRLE(FILE *           const ifP,
                     pm_error("Error reading BMP raster.  Errno=%d (%s)",
                              errno, strerror(errno));
             }
-            if (RLE4 && pixelsRead % 2 == 1) /* previous read ended odd */
-                nibbleAlign(&BMPraster[row][n-1], cnt); 
+            if (rle4 && pixelsRead % 2 == 1) /* previous read ended odd */
+                nibbleAlign(&bmpRaster[row][n-1], cnt); 
     
             pixelsRead += cnt;
             totalBytesRead += cmpBytesRead + 2;
@@ -1146,25 +1150,36 @@ readrowRLE(FILE *           const ifP,
 
 
 static void
-BMPreadraster(FILE *            const ifP, 
+bmpReadraster(FILE *            const ifP, 
               unsigned int      const cols, 
               unsigned int      const rows, 
               enum rowOrder     const rowOrder,
               unsigned int      const cBitCount, 
-              unsigned long int const compression,
-              unsigned char *** const BMPrasterP, 
+              BMPCompType       const compression,
+              unsigned char *** const bmpRasterP, 
               unsigned int *    const bytesReadP) {
+/*----------------------------------------------------------------------------
+   Read the raster from the BMP file on *ifP (which is positioned to the
+   raster).  The raster is 'rows' rows of 'cols' columns, 'cBitCount' bits per
+   pixel, with rows in order 'rowOrder'.
+
+   Return the raster in a newly malloced 2-dimensional array and return
+   a pointer to that array as *bmpRasterP.
 
+   Leave the input file positioned immediately after the raster and return
+   as *bytesReadP the number of bytes we read from the file (i.e. the number
+   of bytes in the raster portion of the file).
+-----------------------------------------------------------------------------*/
     unsigned int const bytesPerRow =
-        (compression == COMP_RLE4) ? cols / 2 + 2 :
-        (compression == COMP_RLE8) ? cols + 1 :
+        (compression == BMPCOMP_RLE4) ? cols / 2 + 2 :
+        (compression == BMPCOMP_RLE8) ? cols + 1 :
         ((cols * cBitCount + 31) / 32) * 4;
         /* A BMP raster row is a multiple of 4 bytes, padded on the right
            with don't cares.
         */
-    unsigned char ** BMPraster;
+    unsigned char ** bmpRaster;
 
-    BMPraster = allocBMPraster(rows, bytesPerRow);
+    bmpRaster = allocBmpRaster(rows, bytesPerRow);
 
     *bytesReadP = 0;
 
@@ -1175,31 +1190,36 @@ BMPreadraster(FILE *            const ifP,
     */
     
     switch(compression){
-    case COMP_RGB:
-    case COMP_BITFIELDS: {
+    case BMPCOMP_RGB:
+    case BMPCOMP_BITFIELDS: {
         unsigned int i;
         for (i = 0; i < rows; ++i)
             readrow(ifP, rowOrder == TOPDOWN ? i : rows - i - 1, 
-                    bytesPerRow, BMPraster, bytesReadP);
+                    bytesPerRow, bmpRaster, bytesReadP);
     } break;
-    case COMP_RLE4: 
-    case COMP_RLE8: {
+    case BMPCOMP_RLE4: 
+    case BMPCOMP_RLE8: {
         unsigned int i;
         /* Read all rows except last */
+        assert(rows >= 1);
         for (i = 0; i < rows - 1; ++i){
             readrowRLE(ifP, rowOrder == TOPDOWN ? i : rows - i - 1, 
-                       cols, FALSE, compression, BMPraster, bytesReadP);
+                       cols, FALSE, compression, bmpRaster, bytesReadP);
         }
         /* Read last row */
         readrowRLE(ifP, rowOrder == TOPDOWN ? i : rows - i - 1, 
-                   cols, TRUE,  compression, BMPraster, bytesReadP);
+                   cols, TRUE,  compression, bmpRaster, bytesReadP);
     } break;             
-    default:       
-        pm_error("The BMP specifies a compression scheme we don't "
-                 "recognize.  Code= %lu", compression);
+    case BMPCOMP_JPEG:
+        pm_error("BMP file uses JPEG compression.  We don't know how to "
+                 "interpret that.");
+        break;
+    case BMPCOMP_PNG:
+        pm_error("BMP file uses PNG compression.  We don't know how to "
+                 "interpret that.");
+        break;
     }
-
-    *BMPrasterP = BMPraster;
+    *bmpRasterP = bmpRaster;
 }
 
 
@@ -1220,14 +1240,7 @@ reportHeader(struct bmpInfoHeader const header,
                header.rowOrder == BOTTOMUP ? "bottom up" : "top down");
     pm_message("  Byte offset of raster within file: %u", offBits);
     pm_message("  Bits per pixel in raster: %u", header.cBitCount);
-    pm_message("  Compression: %s", 
-               header.compression == COMP_RGB ? "none" :
-               header.compression == COMP_RLE4 ? "4 bit run-length coding" :
-               header.compression == COMP_RLE8 ? "8 bit run-length coding" :
-               header.compression == COMP_BITFIELDS ? "none" :
-               header.compression == COMP_JPEG ? "JPEG (not supported)" :
-               header.compression == COMP_PNG ? "PNG (not supported)" :
-               "???");                
+    pm_message("  Compression: %s", BMPCompTypeName(header.compression));
     pm_message("  Colors in color map: %u", header.cmapsize);
 }        
 
@@ -1265,11 +1278,11 @@ analyzeColors(xel          const colormap[],
 
 
 static void
-warnIfOffBitsWrong(struct bmpInfoHeader const BMPheader,
+warnIfOffBitsWrong(struct bmpInfoHeader const bmpHeader,
                    unsigned int         const offBits) {
 
-    if (offBits != BMPoffbits(BMPheader.class, BMPheader.cBitCount, 
-                              BMPheader.cmapsize)) {
+    if (offBits != BMPoffbits(bmpHeader.class, bmpHeader.cBitCount, 
+                              bmpHeader.cmapsize)) {
 
         pm_message("warning: the BMP header says the raster starts "
                    "at offset %u bytes into the file (offbits), "
@@ -1277,8 +1290,8 @@ warnIfOffBitsWrong(struct bmpInfoHeader const BMPheader,
                    "the raster.  This inconsistency probably means the "
                    "input file is not a legal BMP file and is unusable.",
                    offBits,
-                   BMPoffbits(BMPheader.class, BMPheader.cBitCount, 
-                              BMPheader.cmapsize));
+                   BMPoffbits(bmpHeader.class, bmpHeader.cBitCount, 
+                              bmpHeader.cmapsize));
     }
 }
 
@@ -1286,63 +1299,37 @@ warnIfOffBitsWrong(struct bmpInfoHeader const BMPheader,
 
 static void
 readColorMap(FILE *               const ifP,
-             struct bmpInfoHeader const BMPheader,
+             struct bmpInfoHeader const bmpHeader,
              xel **               const colorMapP,
              unsigned int *       const posP) {
 
     unsigned int bytesRead;
 
-    BMPreadcolormap(ifP, BMPheader.class, 
-                    colorMapP, BMPheader.cmapsize, &bytesRead);
+    bmpReadColormap(ifP, bmpHeader.class, 
+                    colorMapP, bmpHeader.cmapsize, &bytesRead);
 
     *posP += bytesRead;
-
-    if (bytesRead != BMPlencolormap(BMPheader.class, BMPheader.cBitCount, 
-                                    BMPheader.cmapsize)) {
-
-        pm_message("warning: %u-byte RGB table, expected %u bytes",
-                   bytesRead,
-                   BMPlencolormap(BMPheader.class, BMPheader.cBitCount, 
-                                  BMPheader.cmapsize));
-    }
 }
 
 
 
 static void
 readRaster(FILE *               const ifP,
-           struct bmpInfoHeader const BMPheader,
-           unsigned char ***    const BMPrasterP, 
+           struct bmpInfoHeader const bmpHeader,
+           unsigned char ***    const bmpRasterP, 
            unsigned int *       const posP) {
 
     unsigned int bytesRead;
 
-    BMPreadraster(ifP, BMPheader.cols, BMPheader.rows, BMPheader.rowOrder,
-                  BMPheader.cBitCount, BMPheader.compression,
-                  BMPrasterP, &bytesRead);
+    bmpReadraster(ifP, bmpHeader.cols, bmpHeader.rows, bmpHeader.rowOrder,
+                  bmpHeader.cBitCount, bmpHeader.compression,
+                  bmpRasterP, &bytesRead);
 
     *posP += bytesRead;
 }
 
 
 
-static void
-warnIfBadFileSize(struct bmpInfoHeader const BMPheader,
-                  unsigned int         const pos) {
-
-    unsigned int const expectedSize =
-        BMPlenfileGen(BMPheader.class, BMPheader.cBitCount, 
-                      BMPheader.cmapsize, BMPheader.cols,
-                      BMPheader.rows, BMPheader.imageSize,
-                      BMPheader.compression);
-
-    if (pos != expectedSize)
-        pm_message("warning: read %u bytes, expected to read %u bytes",
-                   pos, expectedSize);
-}
-
-
-
 static bool
 isValidBmpBpp(unsigned int const cBitCount) {
 
@@ -1364,7 +1351,7 @@ isValidBmpBpp(unsigned int const cBitCount) {
 
 static void
 readBmp(FILE *               const ifP, 
-        unsigned char ***    const BMPrasterP, 
+        unsigned char ***    const bmpRasterP, 
         int *                const colsP, 
         int *                const rowsP,
         bool *               const grayPresentP, 
@@ -1383,60 +1370,59 @@ readBmp(FILE *               const ifP,
     
     unsigned int offBits;
         /* Byte offset into file of raster */
-    struct bmpInfoHeader BMPheader;
+    struct bmpInfoHeader bmpHeader;
 
     pos = 0;  /* Starting at the beginning ... */
     { 
         unsigned int bytesRead;
-        BMPreadfileheader(ifP, &bytesRead, &offBits);
+        bmpReadfileheader(ifP, &bytesRead, &offBits);
         pos += bytesRead;
     }
     {
         unsigned int bytesRead;
-        BMPreadinfoheader(ifP, &bytesRead, &BMPheader);
+        bmpReadinfoheader(ifP, &bytesRead, &bmpHeader);
         if (verbose)
             pm_message("Read %u bytes of header", bytesRead);
         pos += bytesRead;
     }
 
     if (verbose) 
-        reportHeader(BMPheader, offBits);
+        reportHeader(bmpHeader, offBits);
 
-    warnIfOffBitsWrong(BMPheader, offBits);
+    warnIfOffBitsWrong(bmpHeader, offBits);
 
-    readColorMap(ifP, BMPheader, &colormap, &pos);
+    readColorMap(ifP, bmpHeader, &colormap, &pos);
 
-    analyzeColors(colormap, BMPheader.cmapsize, bmpMaxval, 
+    analyzeColors(colormap, bmpHeader.cmapsize, bmpMaxval, 
                   grayPresentP, colorPresentP);
 
     readOffBytes(ifP, offBits - pos);
 
     pos = offBits;
 
-    readRaster(ifP, BMPheader, BMPrasterP, &pos);
+    readRaster(ifP, bmpHeader, bmpRasterP, &pos);
 
-    warnIfBadFileSize(BMPheader, pos);
-    
     if (fgetc(ifP) != EOF)
         pm_message("warning: some image data remains unread.");
     
-    if (!isValidBmpBpp(BMPheader.cBitCount))
+    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);
+                 bmpHeader.cBitCount);
 
-    *colsP        = BMPheader.cols;
-    *rowsP        = BMPheader.rows;
-    *cBitCountP   = BMPheader.cBitCount;
-    *pixelformatP = BMPheader.pixelformat;
+    *cBitCountP   = bmpHeader.cBitCount;
+
+    *colsP        = bmpHeader.cols;
+    *rowsP        = bmpHeader.rows;
+    *pixelformatP = bmpHeader.pixelformat;
     *colormapP    = colormap;
-    *cmapsizeP    = BMPheader.cmapsize;
+    *cmapsizeP    = bmpHeader.cmapsize;
 }
 
 
 
 static void
-writeRasterGen(unsigned char **   const BMPraster,
+writeRasterGen(unsigned char **   const bmpRaster,
                int                const cols, 
                int                const rows, 
                int                const format,
@@ -1446,7 +1432,7 @@ writeRasterGen(unsigned char **   const BMPraster,
                unsigned int       const cmapsize) {
 /*----------------------------------------------------------------------------
   Write the PNM raster to Standard Output, corresponding to the raw BMP
-  raster BMPraster.  Write the raster assuming the PNM image has 
+  raster bmpRaster.  Write the raster assuming the PNM image has 
   dimensions 'cols' by 'rows' and format 'format', with maxval 255.
 
   The BMP image has 'cBitCount' bits per pixel, arranged in format
@@ -1463,7 +1449,7 @@ writeRasterGen(unsigned char **   const BMPraster,
     xelrow = pnm_allocrow(cols);
 
     for (row = 0; row < rows; ++row) {
-        convertRow(BMPraster[row], xelrow, cols, cBitCount, pixelformat,
+        convertRow(bmpRaster[row], xelrow, cols, cBitCount, pixelformat,
                    colormap, cmapsize);
         pnm_writepnmrow(stdout, xelrow, cols, bmpMaxval, format, FALSE);
     }
@@ -1473,13 +1459,13 @@ writeRasterGen(unsigned char **   const BMPraster,
 
 
 static void
-writeRasterPbm(unsigned char ** const BMPraster,
+writeRasterPbm(unsigned char ** const bmpRaster,
                int              const cols, 
                int              const rows, 
                xel              const colormap[]) {
 /*----------------------------------------------------------------------------
   Write the PBM raster to Standard Output corresponding to the raw BMP
-  raster BMPraster.  Write the raster assuming the PBM image has 
+  raster bmpRaster.  Write the raster assuming the PBM image has 
   dimensions 'cols' by 'rows'.
 
   The BMP image has 'cBitCount' bits per pixel, arranged in format
@@ -1490,10 +1476,8 @@ writeRasterPbm(unsigned char ** const BMPraster,
   abnormal case in which colormap[0] and colormap[1] have the same
   value (i.e. both white or both black.)
   
-  We destroy *BMPraster as a side effect.
+  We destroy *bmpRaster as a side effect.
 -----------------------------------------------------------------------------*/
-    unsigned int const charBits = (sizeof(unsigned char) * 8);
-        /* Number of bits in a character */
     unsigned int const colChars = pbm_packed_bytes(cols);
     
     int row;
@@ -1506,20 +1490,15 @@ writeRasterPbm(unsigned char ** const BMPraster,
         colorformat = BlackWhite;
         
     for (row=0; row < rows; ++row){
-        unsigned char * const bitrow = BMPraster[row]; 
+        unsigned char * const bitrow = bmpRaster[row]; 
 
         if (colorformat == BlackWhite) {
             unsigned int i;
             for (i = 0; i < colChars; ++i) 
                 bitrow[i] = ~bitrow[i]; /* flip all pixels */ 
         }   
-            
-        if (cols % 8 > 0) {
-            /* adjust final partial byte */
-            bitrow[colChars-1] >>= charBits - cols % charBits;
-            bitrow[colChars-1] <<= charBits - cols % charBits;
-        }
-        
+
+        pbm_cleanrowend_packed(bitrow, cols);
         pbm_writepbmrow_packed(stdout, bitrow, cols, FALSE);
     }
 }
@@ -1527,9 +1506,9 @@ writeRasterPbm(unsigned char ** const BMPraster,
 
 
 int
-main(int argc, char ** argv) {
+main(int argc, const char ** argv) {
 
-    struct cmdline_info cmdline;
+    struct cmdlineInfo cmdline;
     FILE * ifP;
     int outputType;
 
@@ -1539,10 +1518,10 @@ main(int argc, char ** argv) {
            and gray.
         */
     int cols, rows;
-    unsigned char **BMPraster;
+    unsigned char **bmpRaster;
         /* The raster part of the BMP image, as a row x column array, with
            each element being a raw byte from the BMP raster.  Note that
-           BMPraster[0] is really Row 0 -- the top row of the image, even
+           bmpRaster[0] is really Row 0 -- the top row of the image, even
            though the bottom row comes first in the BMP format.
         */
     unsigned int cBitCount;
@@ -1561,17 +1540,17 @@ main(int argc, char ** argv) {
            allow files with just 1.
 	 */
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
-    parse_command_line(argc, argv, &cmdline);
+    parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.input_filespec);
-    if (streq(cmdline.input_filespec, "-"))
+    ifP = pm_openr(cmdline.inputFileName);
+    if (streq(cmdline.inputFileName, "-"))
         ifname = "Standard Input";
     else 
-        ifname = cmdline.input_filespec;
+        ifname = cmdline.inputFileName;
 
-    readBmp(ifP, &BMPraster, &cols, &rows, &grayPresent, &colorPresent, 
+    readBmp(ifP, &bmpRaster, &cols, &rows, &grayPresent, &colorPresent, 
             &cBitCount, &pixelformat, &colormap, &cmapsize,
             cmdline.verbose);
     pm_close(ifP);
@@ -1589,14 +1568,14 @@ main(int argc, char ** argv) {
     
     if (outputType == PBM_TYPE  && cBitCount == 1){
         pbm_writepbminit(stdout, cols, rows, FALSE);
-        writeRasterPbm(BMPraster, cols, rows, colormap);
+        writeRasterPbm(bmpRaster, cols, rows, colormap);
     } else {
         pnm_writepnminit(stdout, cols, rows, bmpMaxval, outputType, FALSE);
-        writeRasterGen(BMPraster, cols, rows, outputType, cBitCount,
+        writeRasterGen(bmpRaster, cols, rows, outputType, cBitCount,
                        pixelformat, colormap, cmapsize); 
     }
     free(colormap);
-    free(BMPraster);
+    free(bmpRaster);
 
     return 0;
 }
diff --git a/converter/other/cameratopam/Makefile b/converter/other/cameratopam/Makefile
index 20a95aa2..d6207aea 100644
--- a/converter/other/cameratopam/Makefile
+++ b/converter/other/cameratopam/Makefile
@@ -9,7 +9,7 @@ EXTERN_INCLUDES =
 ifneq ($(JPEGLIB),NONE)
   ifneq ($(JPEGHDR_DIR)x,x)
     EXTERN_INCLUDES += -I$(JPEGHDR_DIR)
-    CFLAGS += -DHAVE_JPEG
+    HAVE_JPEG_DEFINE = -DHAVE_JPEG
   endif
 endif
 
@@ -19,20 +19,20 @@ include $(BUILDDIR)/config.mk
 .PHONY: all
 all: cameratopam
 
-OBJECTS = util.o identify.o cameratopam.o camera.o foveon.o decode.o \
+ADDL_OBJECTS = util.o identify.o camera.o foveon.o decode.o \
 	canon.o ljpeg.o dng.o
 
-MERGE_OBJECTS =
+OBJECTS = cameratopam.o $(ADDL_OBJECTS)
 
-BINARIES = cameratopam
-MERGEBINARIES = 
+camera.o camera.o2: CFLAGS_TARGET = $(HAVE_JPEG_DEFINE)
+
+MERGE_OBJECTS = cameratopam.o2 $(ADDL_OBJECTS)
+
+PORTBINARIES = cameratopam
+BINARIES = $(PORTBINARIES)
+MERGEBINARIES = cameratopam
 SCRIPTS = 
 
 include $(SRCDIR)/common.mk
 
-cameratopam: $(OBJECTS) $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ \
-          $(OBJECTS) $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR)) \
-	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) \
-	  $(RPATH) $(LADD)
-
+cameratopam: $(ADDL_OBJECTS)
diff --git a/converter/other/cameratopam/camera.c b/converter/other/cameratopam/camera.c
index 98d6d37a..a1adba95 100644
--- a/converter/other/cameratopam/camera.c
+++ b/converter/other/cameratopam/camera.c
@@ -12,10 +12,13 @@
 #include <jpeglib.h>
 #endif
 
+#include "pm_config.h"
 #include "pm.h"
 #include "mallocvar.h"
+#include "pm_c_util.h"
 
 #include "global_variables.h"
+#include "cameratopam.h"
 #include "util.h"
 #include "decode.h"
 #include "bayer.h"
@@ -54,67 +57,76 @@ merror (const void *ptr, const char *where)
 
 
 static void  
-adobe_copy_pixel (int row, int col, unsigned short **rp, bool use_secondary)
-{
-  unsigned r=row, c=col;
-
-  if (fuji_secondary && use_secondary) (*rp)++;
-  if (filters) {
-    if (fuji_width) {
-      r = row + fuji_width - 1 - (col >> 1);
-      c = row + ((col+1) >> 1);
-    }
-    if (r < height && c < width)
-      BAYER(r,c) = **rp < 0x1000 ? curve[**rp] : **rp;
-    *rp += 1 + fuji_secondary;
-  } else
-    for (c=0; c < tiff_samples; c++) {
-      image[row*width+col][c] = **rp < 0x1000 ? curve[**rp] : **rp;
-      (*rp)++;
+adobeCopyPixel(Image             const image,
+               unsigned int      const row,
+               unsigned int      const col,
+               unsigned short ** const rp,
+               bool              const useSecondary) {
+
+    unsigned r=row, c=col;
+
+    if (fuji_secondary && useSecondary)
+        ++(*rp);
+    if (filters) {
+        if (fuji_width) {
+            r = row + fuji_width - 1 - (col >> 1);
+            c = row + ((col+1) >> 1);
+        }
+        if (r < height && c < width)
+            BAYER(r,c) = **rp < 0x1000 ? curve[**rp] : **rp;
+        *rp += 1 + fuji_secondary;
+    } else {
+        unsigned int c;
+        for (c = 0; c < tiff_samples; ++c) {
+            image[row*width+col][c] = **rp < 0x1000 ? curve[**rp] : **rp;
+            ++(*rp);
+        }
     }
-  if (fuji_secondary && use_secondary) (*rp)--;
+    if (fuji_secondary && useSecondary)
+        --(*rp);
 }
 
 void
-adobe_dng_load_raw_lj()
-{
-  int save, twide, trow=0, tcol=0, jrow, jcol;
-  struct jhead jh;
-  unsigned short *rp;
+adobe_dng_load_raw_lj(Image const image) {
 
-  while (1) {
-    save = ftell(ifp);
-    fseek (ifp, get4(ifp), SEEK_SET);
-    if (!ljpeg_start (ifp, &jh)) break;
-    if (trow >= raw_height) break;
-    if (jh.high > raw_height-trow)
-    jh.high = raw_height-trow;
-    twide = jh.wide;
-    if (filters) twide *= jh.clrs;
-    else         colors = jh.clrs;
-    if (fuji_secondary) twide /= 2;
-    if (twide > raw_width-tcol)
-    twide = raw_width-tcol;
-
-    for (jrow=0; jrow < jh.high; jrow++) {
-      ljpeg_row (&jh);
-      for (rp=jh.row, jcol=0; jcol < twide; jcol++)
-    adobe_copy_pixel (trow+jrow, tcol+jcol, &rp, use_secondary);
-    }
-    fseek (ifp, save+4, SEEK_SET);
-    if ((tcol += twide) >= raw_width) {
-      tcol = 0;
-      trow += jh.high;
+    int save, twide, trow=0, tcol=0, jrow, jcol;
+    struct jhead jh;
+    unsigned short *rp;
+
+    while (1) {
+        save = ftell(ifp);
+        fseek (ifp, get4(ifp), SEEK_SET);
+        if (!ljpeg_start (ifp, &jh)) break;
+        if (trow >= raw_height) break;
+        if (jh.high > raw_height-trow)
+            jh.high = raw_height-trow;
+        twide = jh.wide;
+        if (filters) twide *= jh.clrs;
+        else         colors = jh.clrs;
+        if (fuji_secondary) twide /= 2;
+        if (twide > raw_width-tcol)
+            twide = raw_width-tcol;
+
+        for (jrow=0; jrow < jh.high; jrow++) {
+            ljpeg_row(ifp, &jh);
+            for (rp=jh.row, jcol=0; jcol < twide; jcol++)
+                adobeCopyPixel(image,
+                               trow+jrow, tcol+jcol, &rp, use_secondary);
+        }
+        fseek (ifp, save+4, SEEK_SET);
+        if ((tcol += twide) >= raw_width) {
+            tcol = 0;
+            trow += jh.high;
+        }
+        free (jh.row);
     }
-    free (jh.row);
-  }
 }
 
 
 
 void
-adobe_dng_load_raw_nc()
-{
+adobe_dng_load_raw_nc(Image const image) {
+
     unsigned short *pixel, *rp;
     int row, col;
 
@@ -123,7 +135,7 @@ adobe_dng_load_raw_nc()
     for (row=0; row < raw_height; row++) {
         read_shorts (ifp, pixel, raw_width * tiff_samples);
         for (rp=pixel, col=0; col < raw_width; col++)
-            adobe_copy_pixel (row, col, &rp, use_secondary);
+            adobeCopyPixel(image, row, col, &rp, use_secondary);
     }
     free (pixel);
 }
@@ -133,8 +145,8 @@ adobe_dng_load_raw_nc()
 static int nikon_curve_offset;
 
 void
-nikon_compressed_load_raw(void)
-{
+nikon_compressed_load_raw(Image const image) {
+
     static const unsigned char nikon_tree[] = {
         0,1,5,1,1,1,1,1,1,2,0,0,0,0,0,0,
         5,4,3,6,2,7,1,0,8,9,11,10,12
@@ -158,7 +170,7 @@ nikon_compressed_load_raw(void)
     for (row=0; row < height; row++)
         for (col=0; col < raw_width; col++)
         {
-            diff = ljpeg_diff (first_decode);
+            diff = ljpeg_diff (ifp, first_decode);
             if (col < 2) {
                 i = 2*(row & 1) + (col & 1);
                 vpred[i] += diff;
@@ -175,8 +187,8 @@ nikon_compressed_load_raw(void)
 }
 
 void
-nikon_load_raw()
-{
+nikon_load_raw(Image const image) {
+
   int irow, row, col, i;
 
   getbits(ifp, -1);
@@ -293,8 +305,8 @@ minolta_z2()
 }
 
 void
-nikon_e2100_load_raw()
-{
+nikon_e2100_load_raw(Image const image) {
+        
   unsigned char   data[3432], *dp;
   unsigned short pixel[2288], *pix;
   int row, col;
@@ -321,8 +333,8 @@ nikon_e2100_load_raw()
 }
 
 void
-nikon_e950_load_raw()
-{
+nikon_e950_load_raw(Image const image) {
+
   int irow, row, col;
 
   getbits(ifp, -1);
@@ -340,8 +352,7 @@ nikon_e950_load_raw()
    The Fuji Super CCD is just a Bayer grid rotated 45 degrees.
  */
 void
-fuji_s2_load_raw()
-{
+fuji_s2_load_raw(Image const image) {
   unsigned short pixel[2944];
   int row, col, r, c;
 
@@ -357,8 +368,7 @@ fuji_s2_load_raw()
 }
 
 void
-fuji_s3_load_raw()
-{
+fuji_s3_load_raw(Image const image) {
   unsigned short pixel[4352];
   int row, col, r, c;
 
@@ -373,42 +383,52 @@ fuji_s3_load_raw()
   }
 }
 
-static void  fuji_common_load_raw (int ncol, int icol, int nrow)
-{
-  unsigned short pixel[2048];
-  int row, col, r, c;
-
-  for (row=0; row < nrow; row++) {
-    read_shorts(ifp, pixel, ncol);
-    for (col=0; col <= icol; col++) {
-      r = icol - col + (row >> 1);
-      c = col + ((row+1) >> 1);
-      BAYER(r,c) = pixel[col];
+static void 
+fuji_common_load_raw(Image        const image,
+                     unsigned int const ncol,
+                     unsigned int const icol,
+                     unsigned int const nrow) {
+
+    unsigned short pixel[2048];
+    unsigned int row;
+
+    for (row = 0; row < nrow; ++row) {
+        unsigned int col;
+        read_shorts(ifp, pixel, ncol);
+        for (col = 0; col <= icol; ++col) {
+            int const r = icol - col + (row >> 1);
+            int const c = col + ((row+1) >> 1);
+            BAYER(r,c) = pixel[col];
+        }
     }
-  }
 }
 
+
+
 void
-fuji_s5000_load_raw()
-{
+fuji_s5000_load_raw(Image const image) {
+
   fseek (ifp, (1472*4+24)*2, SEEK_CUR);
-  fuji_common_load_raw (1472, 1423, 2152);
+  fuji_common_load_raw(image, 1472, 1423, 2152);
 }
 
+
+
 void
-fuji_s7000_load_raw()
-{
-  fuji_common_load_raw (2048, 2047, 3080);
+fuji_s7000_load_raw(Image const image) {
+
+    fuji_common_load_raw(image, 2048, 2047, 3080);
 }
 
+
+
 /*
    The Fuji Super CCD SR has two photodiodes for each pixel.
    The secondary has about 1/16 the sensitivity of the primary,
    but this ratio may vary.
  */
 void
-fuji_f700_load_raw()
-{
+fuji_f700_load_raw(Image const image) {
   unsigned short pixel[2944];
   int row, col, r, c, val;
 
@@ -424,8 +444,7 @@ fuji_f700_load_raw()
 }
 
 void
-rollei_load_raw()
-{
+rollei_load_raw(Image const image) {
   unsigned char pixel[10];
   unsigned iten=0, isix, i, buffer=0, row, col, todo[16];
 
@@ -451,8 +470,7 @@ rollei_load_raw()
 }
 
 void
-phase_one_load_raw()
-{
+phase_one_load_raw(Image const image) {
   int row, col, a, b;
   unsigned short *pixel, akey, bkey;
 
@@ -478,8 +496,7 @@ phase_one_load_raw()
 }
 
 void
-ixpress_load_raw()
-{
+ixpress_load_raw(Image const image) {
   unsigned short pixel[4090];
   int row, col;
 
@@ -493,8 +510,7 @@ ixpress_load_raw()
 }
 
 void
-leaf_load_raw()
-{
+leaf_load_raw(Image const image) {
   unsigned short *pixel;
   int r, c, row, col;
 
@@ -513,8 +529,7 @@ leaf_load_raw()
    For this function only, raw_width is in bytes, not pixels!
  */
 void
-packed_12_load_raw()
-{
+packed_12_load_raw(Image const image) {
   int row, col;
 
   getbits(ifp, -1);
@@ -527,8 +542,7 @@ packed_12_load_raw()
 }
 
 void
-unpacked_load_raw()
-{
+unpacked_load_raw(Image const image) {
   unsigned short *pixel;
   int row, col;
 
@@ -543,8 +557,7 @@ unpacked_load_raw()
 }
 
 void
-olympus_e300_load_raw()
-{
+olympus_e300_load_raw(Image const image) {
   unsigned char  *data,  *dp;
   unsigned short *pixel, *pix;
   int dwide, row, col;
@@ -567,8 +580,7 @@ olympus_e300_load_raw()
 }
 
 void
-olympus_cseries_load_raw()
-{
+olympus_cseries_load_raw(Image const image) {
   int irow, row, col;
 
   for (irow=0; irow < height; irow++) {
@@ -583,8 +595,7 @@ olympus_cseries_load_raw()
 }
 
 void
-eight_bit_load_raw()
-{
+eight_bit_load_raw(Image const image) {
   unsigned char *pixel;
   int row, col;
 
@@ -600,8 +611,7 @@ eight_bit_load_raw()
 }
 
 void
-casio_qv5700_load_raw()
-{
+casio_qv5700_load_raw(Image const image) {
   unsigned char  data[3232],  *dp;
   unsigned short pixel[2576], *pix;
   int row, col;
@@ -621,8 +631,7 @@ casio_qv5700_load_raw()
 }
 
 void
-nucore_load_raw()
-{
+nucore_load_raw(Image const image) {
   unsigned short *pixel;
   int irow, row, col;
 
@@ -684,79 +693,91 @@ static int  radc_token (int tree)
 : (buf[c][y-1][x+1] + 2*buf[c][y-1][x] + buf[c][y][x+1]) / 4)
 
 void
-kodak_radc_load_raw()
-{
-  int row, col, tree, nreps, rep, step, i, c, s, r, x, y, val;
-  short last[3] = { 16,16,16 }, mul[3], buf[3][3][386];
+kodak_radc_load_raw(Image const image) {
+    int row, col, tree, nreps, rep, step, c, s, r, x, y, val;
+    unsigned int i;
+    short last[3] = { 16,16,16 }, mul[3], buf[3][3][386];
 
-  init_decoder();
-  getbits(ifp, -1);
-  for (i=0; i < sizeof(buf)/sizeof(short); i++)
-    buf[0][0][i] = 2048;
-  for (row=0; row < height; row+=4) {
-    for (i=0; i < 3; i++)
-      mul[i] = getbits(ifp, 6);
-    FORC3 {
-      val = ((0x1000000/last[c] + 0x7ff) >> 12) * mul[c];
-      s = val > 65564 ? 10:12;
-      x = ~(-1 << (s-1));
-      val <<= 12-s;
-      for (i=0; i < sizeof(buf[0])/sizeof(short); i++)
-    buf[c][0][i] = (buf[c][0][i] * val + x) >> s;
-      last[c] = mul[c];
-      for (r=0; r <= !c; r++) {
-    buf[c][1][width/2] = buf[c][2][width/2] = mul[c] << 7;
-    for (tree=1, col=width/2; col > 0; ) {
-      if ((tree = radc_token(tree))) {
-        col -= 2;
-        if (tree == 8)
-          FORYX buf[c][y][x] = radc_token(tree+10) * mul[c];
-        else
-          FORYX buf[c][y][x] = radc_token(tree+10) * 16 + PREDICTOR;
-      } else
-        do {
-          nreps = (col > 2) ? radc_token(9) + 1 : 1;
-          for (rep=0; rep < 8 && rep < nreps && col > 0; rep++) {
-        col -= 2;
-        FORYX buf[c][y][x] = PREDICTOR;
-        if (rep & 1) {
-          step = radc_token(10) << 4;
-          FORYX buf[c][y][x] += step;
+    init_decoder();
+    getbits(ifp, -1);
+    for (i = 0; i < ARRAY_SIZE(buf); ++i) {
+        unsigned int j;
+        for (j = 0; j < ARRAY_SIZE(buf[0]); ++j) {
+            unsigned int k;
+            for (k = 0; k < ARRAY_SIZE(buf[0][0]); ++k)
+                buf[i][j][k] = 2048;
         }
-          }
-        } while (nreps == 9);
-    }
-    for (y=0; y < 2; y++)
-      for (x=0; x < width/2; x++) {
-        val = (buf[c][y+1][x] << 4) / mul[c];
-        if (val < 0) val = 0;
-        if (c)
-          BAYER(row+y*2+c-1,x*2+2-c) = val;
-        else
-          BAYER(row+r*2+y,x*2+y) = val;
-      }
-    memcpy (buf[c][0]+!c, buf[c][2], sizeof buf[c][0]-2*!c);
-      }
-    }
-    for (y=row; y < row+4; y++)
-      for (x=0; x < width; x++)
-    if ((x+y) & 1) {
-      val = (BAYER(y,x)-2048)*2 + (BAYER(y,x-1)+BAYER(y,x+1))/2;
-      if (val < 0) val = 0;
-      BAYER(y,x) = val;
     }
-  }
-  maximum = 0x1fff;     /* wild guess */
+    for (row=0; row < height; row+=4) {
+        unsigned int i;
+        for (i = 0; i < 3; ++i)
+            mul[i] = getbits(ifp, 6);
+        FORC3 {
+            val = ((0x1000000/last[c] + 0x7ff) >> 12) * mul[c];
+            s = val > 65564 ? 10:12;
+            x = ~(-1 << (s-1));
+            val <<= 12-s;
+            for (i=0; i < ARRAY_SIZE(buf[c][0]); i++)
+                buf[c][0][i] = (buf[c][0][i] * val + x) >> s;
+            last[c] = mul[c];
+            for (r=0; r <= !c; r++) {
+                buf[c][1][width/2] = buf[c][2][width/2] = mul[c] << 7;
+                for (tree=1, col=width/2; col > 0; ) {
+                    if ((tree = radc_token(tree))) {
+                        col -= 2;
+                        if (tree == 8)
+                            FORYX buf[c][y][x] =
+                                radc_token(tree+10) * mul[c];
+                        else
+                            FORYX buf[c][y][x] =
+                                radc_token(tree+10) * 16 + PREDICTOR;
+                    } else
+                        do {
+                            nreps = (col > 2) ? radc_token(9) + 1 : 1;
+                            for (rep=0;
+                                 rep < 8 && rep < nreps && col > 0;
+                                 rep++) {
+                                col -= 2;
+                                FORYX buf[c][y][x] = PREDICTOR;
+                                if (rep & 1) {
+                                    step = radc_token(10) << 4;
+                                    FORYX buf[c][y][x] += step;
+                                }
+                            }
+                        } while (nreps == 9);
+                }
+                for (y=0; y < 2; y++)
+                    for (x=0; x < width/2; x++) {
+                        val = (buf[c][y+1][x] << 4) / mul[c];
+                        if (val < 0) val = 0;
+                        if (c)
+                            BAYER(row+y*2+c-1,x*2+2-c) = val;
+                        else
+                            BAYER(row+r*2+y,x*2+y) = val;
+                    }
+                memcpy (buf[c][0]+!c, buf[c][2], sizeof buf[c][0]-2*!c);
+            }
+        }
+        for (y=row; y < row+4; y++)
+            for (x=0; x < width; x++)
+                if ((x+y) & 1) {
+                    val = (BAYER(y,x)-2048)*2 + (BAYER(y,x-1)+BAYER(y,x+1))/2;
+                    if (val < 0) val = 0;
+                    BAYER(y,x) = val;
+                }
+    }
+    maximum = 0x1fff;     /* wild guess */
 }
 
 #undef FORYX
 #undef PREDICTOR
 
 #ifndef HAVE_JPEG
-void kodak_jpeg_load_raw() {}
+void
+kodak_jpeg_load_raw(Image const Image) {}
 #else
 
-static boolean
+static bool
 fill_input_buffer (j_decompress_ptr cinfo)
 {
   static char jpeg_buffer[4096];
@@ -770,7 +791,7 @@ fill_input_buffer (j_decompress_ptr cinfo)
 }
 
 void
-kodak_jpeg_load_raw()
+kodak_jpeg_load_raw(Image const image)
 {
   struct jpeg_decompress_struct cinfo;
   struct jpeg_error_mgr jerr;
@@ -811,7 +832,7 @@ kodak_jpeg_load_raw()
 #endif
 
 void
-kodak_dc120_load_raw()
+kodak_dc120_load_raw(Image const image)
 {
   static const int mul[4] = { 162, 192, 187,  92 };
   static const int add[4] = {   0, 636, 424, 212 };
@@ -847,7 +868,7 @@ kodak_dc20_coeff (float const juice)
 }
 
 void
-kodak_easy_load_raw()
+kodak_easy_load_raw(Image const image)
 {
   unsigned char *pixel;
   unsigned row, col, icol;
@@ -875,7 +896,7 @@ kodak_easy_load_raw()
 }
 
 void
-kodak_compressed_load_raw()
+kodak_compressed_load_raw(Image const image)
 {
   unsigned char c, blen[256];
   unsigned short raw[6];
@@ -939,7 +960,7 @@ kodak_compressed_load_raw()
 }
 
 void
-kodak_yuv_load_raw()
+kodak_yuv_load_raw(Image const image)
 {
   unsigned char c, blen[384];
   unsigned row, col, len, bits=0;
@@ -1030,7 +1051,7 @@ static void  sony_decrypt (unsigned *data, int len, int start, int key)
 }
 
 void
-sony_load_raw()
+sony_load_raw(Image const image)
 {
   unsigned char head[40];
   struct pixel {
@@ -1298,12 +1319,6 @@ parse_mos(FILE * const ifp,
         fread (data, 1, 40, ifp);
         skip = get4(ifp);
         from = ftell(ifp);
-#ifdef USE_LCMS
-        if (!strcmp(data,"icc_camera_profile")) {
-            profile_length = skip;
-            profile_offset = from;
-        }
-#endif
         if (!strcmp(data,"NeutObj_neutrals")) {
             for (i=0; i < 4; i++)
                 fscanf (ifp, "%d", neut+i);
diff --git a/converter/other/cameratopam/camera.h b/converter/other/cameratopam/camera.h
index a1e884cf..02c3f2af 100644
--- a/converter/other/cameratopam/camera.h
+++ b/converter/other/cameratopam/camera.h
@@ -1,5 +1,10 @@
+#ifndef CAMERA_H_INCLUDED
+#define CAMERA_H_INCLUDED
+
 #include <stdio.h>
 
+#include "cameratopam.h"
+
 void 
 parse_ciff(FILE * const ifp,
            int    const offset,
@@ -21,20 +26,18 @@ void
 parse_mos(FILE * const ifp,
           int    const offset);
 
-void
-adobe_dng_load_raw_lj(void);
+typedef void LoadRawFn(Image const image);
 
-void
-adobe_dng_load_raw_nc(void);
+LoadRawFn adobe_dng_load_raw_lj;
+
+LoadRawFn adobe_dng_load_raw_nc;
 
 int
 nikon_is_compressed(void);
 
-void
-nikon_compressed_load_raw(void);
+LoadRawFn nikon_compressed_load_raw;
 
-void
-nikon_e950_load_raw(void);
+LoadRawFn nikon_e950_load_raw;
 
 void
 nikon_e950_coeff(void);
@@ -45,87 +48,63 @@ nikon_e990(void);
 int
 nikon_e2100(void);
 
-void
-nikon_e2100_load_raw(void);
+LoadRawFn nikon_e2100_load_raw;
 
 int
 minolta_z2(void);
 
-void
-fuji_s2_load_raw(void);
+LoadRawFn fuji_s2_load_raw;
 
-void
-fuji_s3_load_raw(void);
+LoadRawFn fuji_s3_load_raw;
 
-void
-fuji_s5000_load_raw(void);
+LoadRawFn fuji_s5000_load_raw;
 
-void
-unpacked_load_raw(void);
+LoadRawFn unpacked_load_raw;
 
-void
-fuji_s7000_load_raw(void);
+LoadRawFn fuji_s7000_load_raw;
 
-void
-fuji_f700_load_raw(void);
+LoadRawFn fuji_f700_load_raw;
 
-void
-packed_12_load_raw(void);
+LoadRawFn packed_12_load_raw;
 
-void
-eight_bit_load_raw(void);
+LoadRawFn eight_bit_load_raw;
 
-void
-phase_one_load_raw(void);
+LoadRawFn phase_one_load_raw;
 
-void
-ixpress_load_raw(void);
+LoadRawFn ixpress_load_raw;
 
-void
-leaf_load_raw(void);
+LoadRawFn leaf_load_raw;
 
-void
-olympus_e300_load_raw(void);
+LoadRawFn olympus_e300_load_raw;
 
-void
-olympus_cseries_load_raw(void);
+LoadRawFn olympus_cseries_load_raw;
 
-void
-sony_load_raw(void);
+LoadRawFn sony_load_raw;
 
-void
-kodak_easy_load_raw(void);
+LoadRawFn kodak_easy_load_raw;
 
-void
-kodak_compressed_load_raw(void);
+LoadRawFn kodak_compressed_load_raw;
 
-void
-kodak_yuv_load_raw(void);
+LoadRawFn kodak_yuv_load_raw;
 
 void
 kodak_dc20_coeff (float const juice);
 
-void
-kodak_radc_load_raw(void);
+LoadRawFn kodak_radc_load_raw;
 
-void
-kodak_jpeg_load_raw(void);
+LoadRawFn kodak_jpeg_load_raw;
 
-void
-kodak_dc120_load_raw(void);
+LoadRawFn kodak_dc120_load_raw;
 
-void
-rollei_load_raw(void);
+LoadRawFn rollei_load_raw;
 
-void
-casio_qv5700_load_raw(void);
+LoadRawFn casio_qv5700_load_raw;
 
-void
-nucore_load_raw(void);
+LoadRawFn nucore_load_raw;
 
-void
-nikon_load_raw(void);
+LoadRawFn nikon_load_raw;
 
 int
 pentax_optio33(void);
 
+#endif
diff --git a/converter/other/cameratopam/cameratopam.c b/converter/other/cameratopam/cameratopam.c
index b2d6da9b..ec33dd31 100644
--- a/converter/other/cameratopam/cameratopam.c
+++ b/converter/other/cameratopam/cameratopam.c
@@ -7,8 +7,11 @@
  */
 
 
-#define _BSD_SOURCE 1   /* Make sure string.h contains strcasecmp() */
-#define _XOPEN_SOURCE  /* Make sure unistd.h contains swab() */
+#define _BSD_SOURCE 1   /* Make sure string.h contains strdup() */
+#define _XOPEN_SOURCE 500
+   /* Make sure unistd.h contains swab(), string.h constains strdup() */
+
+#include "pm_config.h"
 
 #include <ctype.h>
 #include <unistd.h>
@@ -23,10 +26,10 @@
 #include <stdlib.h>
 #include <string.h>
 
-#ifdef __CYGWIN__
+#ifdef HAVE_IO_H
   #include <io.h>
 #endif
-#if !defined(WIN32) || defined(__CYGWIN__)
+#if !MSVCRT
   #include <unistd.h>
 #endif
 
@@ -36,6 +39,7 @@
 #include "pam.h"
 
 #include "global_variables.h"
+#include "cameratopam.h"
 #include "util.h"
 #include "decode.h"
 #include "identify.h"
@@ -59,22 +63,19 @@ int height, width, fuji_width, colors, tiff_samples;
 int black, maximum, clip_max;
 int iheight, iwidth, shrink;
 int is_dng, is_canon, is_foveon, use_coeff, use_gamma;
-int trim, flip, xmag, ymag;
+int flip, xmag, ymag;
 int zero_after_ff;
 unsigned filters;
-unsigned short (*image)[4], white[8][8], curve[0x1000];
+unsigned short  white[8][8];
+unsigned short  curve[0x1000];
 int fuji_secondary;
-float cam_mul[4], pre_mul[4], coeff[3][4];
+float cam_mul[4], coeff[3][4];
+float pre_mul[4];
 int histogram[3][0x2000];
 jmp_buf failure;
-bool use_secondary;
+int use_secondary;
 bool verbose;
 
-#ifdef USE_LCMS
-#include <lcms.h>
-int profile_offset, profile_length;
-#endif
-
 #define CLASS
 
 #define FORC3 for (c=0; c < 3; c++)
@@ -86,7 +87,7 @@ static void CLASS merror (const void *ptr, const char *where)
         pm_error ("Out of memory in %s", where);
 }
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -110,11 +111,11 @@ struct cmdlineInfo {
 };
 
 
-static struct cmdlineInfo cmdline;
+static struct CmdlineInfo cmdline;
 
 static void
 parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo *cmdlineP) {
+                 struct CmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
    *cmdlineP structure are actually in the supplied argv array.  And
@@ -167,7 +168,7 @@ parseCommandLine(int argc, char ** argv,
     OPTENT3(0,   "linear",   
             OPT_FLAG,    NULL, &cmdlineP->linear, 0);
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
 
     if (!brightSpec)
         cmdlineP->bright = 1.0;
@@ -189,615 +190,694 @@ parseCommandLine(int argc, char ** argv,
 }
 
   
-/*
-   Seach from the current directory up to the root looking for
-   a ".badpixels" file, and fix those pixels now.
- */
-static void CLASS bad_pixels()
-{
-  FILE *fp=NULL;
-  char *fname, *cp, line[128];
-  int len, time, row, col, r, c, rad, tot, n, fixed=0;
-
-  if (!filters) return;
-  for (len=16 ; ; len *= 2) {
-    fname = malloc (len);
-    if (!fname) return;
-    if (getcwd (fname, len-12)) break;
-    free (fname);
-    if (errno != ERANGE) return;
-  }
-#ifdef WIN32
-  if (fname[1] == ':')
-    memmove (fname, fname+2, len-2);
-  for (cp=fname; *cp; cp++)
-    if (*cp == '\\') *cp = '/';
+
+static void CLASS
+fixBadPixels(Image const image) {
+/*----------------------------------------------------------------------------
+  Search from the current directory up to the root looking for
+  a ".badpixels" file, and fix those pixels now.
+-----------------------------------------------------------------------------*/
+    if (filters) {
+        FILE *fp;
+        char *fname, *cp, line[128];
+        int len, time, row, col, rad, tot, n, fixed=0;
+
+        for (len=16 ; ; len *= 2) {
+            fname = malloc (len);
+            if (!fname) return;
+            if (getcwd (fname, len-12))
+                break;
+            free (fname);
+            if (errno != ERANGE)
+                return;
+        }
+#if MSVCRT
+        if (fname[1] == ':')
+            memmove (fname, fname+2, len-2);
+        for (cp=fname; *cp; cp++)
+            if (*cp == '\\') *cp = '/';
 #endif
-  cp = fname + strlen(fname);
-  if (cp[-1] == '/') cp--;
-  while (*fname == '/') {
-    strcpy (cp, "/.badpixels");
-    if ((fp = fopen (fname, "r"))) break;
-    if (cp == fname) break;
-    while (*--cp != '/');
-  }
-  free (fname);
-  if (!fp) return;
-  while (fgets (line, 128, fp)) {
-    cp = strchr (line, '#');
-    if (cp) *cp = 0;
-    if (sscanf (line, "%d %d %d", &col, &row, &time) != 3) continue;
-    if ((unsigned) col >= width || (unsigned) row >= height) continue;
-    if (time > timestamp) continue;
-    for (tot=n=0, rad=1; rad < 3 && n==0; rad++)
-      for (r = row-rad; r <= row+rad; r++)
-    for (c = col-rad; c <= col+rad; c++)
-      if ((unsigned) r < height && (unsigned) c < width &&
-        (r != row || c != col) && FC(r,c) == FC(row,col)) {
-        tot += BAYER(r,c);
-        n++;
-      }
-    BAYER(row,col) = tot/n;
-    if (cmdline.verbose) {
-      if (!fixed++)
-          pm_message ("Fixed bad pixels at: %d,%d", col, row);
+        cp = fname + strlen(fname);
+        if (cp[-1] == '/')
+            --cp;
+        fp = NULL; /* initial value */
+        while (*fname == '/') {
+            strcpy (cp, "/.badpixels");
+            fp = fopen (fname, "r");
+            if (fp)
+                break;
+            if (cp == fname)
+                break;
+            while (*--cp != '/');
+        }
+        free (fname);
+        if (fp) {
+            while (fgets (line, 128, fp)) {
+                char * cp;
+                cp = strchr (line, '#');
+                if (cp) *cp = 0;
+                if (sscanf (line, "%d %d %d", &col, &row, &time) != 3)
+                    continue;
+                if ((unsigned) col >= width || (unsigned) row >= height)
+                    continue;
+                if (time > timestamp) continue;
+                for (tot=n=0, rad=1; rad < 3 && n==0; rad++) {
+                    unsigned int r;
+                    for (r = row-rad; r <= row+rad; ++r) {
+                        unsigned int c;
+                        for (c = col-rad; c <= col+rad; ++c) {
+                            if ((unsigned) r < height &&
+                                (unsigned) c < width  &&
+                                (r != row || c != col) &&
+                                FC(r,c) == FC(row,col)) {
+                                tot += BAYER(r,c);
+                                ++n;
+                            }
+                        }
+                    }
+                }
+                BAYER(row,col) = tot/n;
+                if (cmdline.verbose) {
+                    if (!fixed++)
+                        pm_message ("Fixed bad pixels at: %d,%d", col, row);
+                }
+            }
+            fclose (fp);
+        }
     }
-  }
-  fclose (fp);
 }
 
-static void CLASS scale_colors()
-{
-  int row, col, c, val, shift=0;
-  int min[4], max[4], count[4];
-  double sum[4], dmin;
-
-  maximum -= black;
-  if (cmdline.use_auto_wb || (cmdline.use_camera_wb && camera_red == -1)) {
-    FORC4 min[c] = INT_MAX;
-    FORC4 max[c] = count[c] = sum[c] = 0;
-    for (row=0; row < height; row++)
-      for (col=0; col < width; col++)
-    FORC4 {
-      val = image[row*width+col][c];
-      if (!val) continue;
-      if (min[c] > val) min[c] = val;
-      if (max[c] < val) max[c] = val;
-      val -= black;
-      if (val > maximum-25) continue;
-      if (val < 0) val = 0;
-      sum[c] += val;
-      count[c]++;
+
+
+static void CLASS
+scaleColors(Image const image) {
+
+    int row;
+    int c;
+    int val;
+    int shift;
+    int min[4], max[4], count[4];
+    double sum[4], dmin;
+    int scaleMax;
+
+    scaleMax = maximum - black;  /* initial value */
+    if (cmdline.use_auto_wb || (cmdline.use_camera_wb && camera_red == -1)) {
+        unsigned int row;
+        FORC4 min  [c] = INT_MAX;
+        FORC4 max  [c] = 0;
+        FORC4 count[c] = 0;
+        FORC4 sum  [c] = 0;
+        for (row = 0; row < height; ++row) {
+            unsigned int col;
+            for (col = 0; col < width; ++col) {
+                FORC4 {
+                    int val;
+                    val = image[row*width+col][c];
+                    if (val != 0) {
+                        if (min[c] > val)
+                            min[c] = val;
+                        if (max[c] < val)
+                            max[c] = val;
+                        val -= black;
+                        if (val <= scaleMax-25) {
+                            sum  [c] += MAX(0, val);
+                            count[c] += 1;
+                        }
+                    }
+                }
+            }
+        }
+        FORC4 pre_mul[c] = count[c] / sum[c];
     }
-    FORC4 pre_mul[c] = count[c] / sum[c];
-  }
-  if (cmdline.use_camera_wb && camera_red != -1) {
-    FORC4 count[c] = sum[c] = 0;
-    for (row=0; row < 8; row++)
-      for (col=0; col < 8; col++) {
-    c = FC(row,col);
-    if ((val = white[row][col] - black) > 0)
-      sum[c] += val;
-    count[c]++;
-      }
-    val = 1;
-    FORC4 if (sum[c] == 0) val = 0;
-    if (val)
-      FORC4 pre_mul[c] = count[c] / sum[c];
-    else if (camera_red && camera_blue)
-      memcpy (pre_mul, cam_mul, sizeof pre_mul);
-    else
-      pm_message ("Cannot use camera white balance.");
-  }
-  if (!use_coeff) {
-    pre_mul[0] *= cmdline.red_scale;
-    pre_mul[2] *= cmdline.blue_scale;
-  }
-  dmin = DBL_MAX;
-  FORC4 if (dmin > pre_mul[c])
+    if (cmdline.use_camera_wb && camera_red != -1) {
+        unsigned int row;
+        FORC4 count[c] = sum[c] = 0;
+        for (row = 0; row < 8; ++row) {
+            unsigned int col;
+            for (col = 0; col < 8; ++col) {
+                c = FC(row,col);
+                if ((val = white[row][col] - black) > 0)
+                    sum[c] += val;
+                ++count[c];
+            }
+        }
+        val = 1;
+        FORC4 if (sum[c] == 0) val = 0;
+        if (val)
+            FORC4 pre_mul[c] = count[c] / sum[c];
+        else if (camera_red && camera_blue)
+            memcpy(pre_mul, cam_mul, sizeof pre_mul);
+        else
+            pm_message ("Cannot use camera white balance.");
+    }
+    if (!use_coeff) {
+        pre_mul[0] *= cmdline.red_scale;
+        pre_mul[2] *= cmdline.blue_scale;
+    }
+    dmin = DBL_MAX;
+    FORC4 if (dmin > pre_mul[c])
         dmin = pre_mul[c];
-  FORC4 pre_mul[c] /= dmin;
-
-  while (maximum << shift < 0x8000) shift++;
-  FORC4 pre_mul[c] *= 1 << shift;
-  maximum <<= shift;
-
-  if (cmdline.linear || cmdline.bright < 1) {
-      maximum *= cmdline.bright;
-      if (maximum > 0xffff)
-          maximum = 0xffff;
-      FORC4 pre_mul[c] *= cmdline.bright;
-  }
-  if (cmdline.verbose) {
-    fprintf (stderr, "Scaling with black=%d, pre_mul[] =", black);
-    FORC4 fprintf (stderr, " %f", pre_mul[c]);
-    fputc ('\n', stderr);
-  }
-  clip_max = cmdline.no_clip_color ? 0xffff : maximum;
-  for (row=0; row < height; row++)
-    for (col=0; col < width; col++)
-      FORC4 {
-    val = image[row*width+col][c];
-    if (!val) continue;
-    val -= black;
-    val *= pre_mul[c];
-    if (val < 0) val = 0;
-    if (val > clip_max) val = clip_max;
-    image[row*width+col][c] = val;
-      }
-}
-
-/*
-   This algorithm is officially called:
+    FORC4 pre_mul[c] /= dmin;
 
-   "Interpolation using a Threshold-based variable number of gradients"
+    for (shift = 0; scaleMax << shift < 0x8000; ++shift);
 
-   described in http://www-ise.stanford.edu/~tingchen/algodep/vargra.html
+    FORC4 pre_mul[c] *= 1 << shift;
+    scaleMax <<= shift;
 
-   I've extended the basic idea to work with non-Bayer filter arrays.
-   Gradients are numbered clockwise from NW=0 to W=7.
- */
-static void CLASS vng_interpolate()
-{
-  static const signed char *cp, terms[] = {
-    -2,-2,+0,-1,0,(char)0x01, -2,-2,+0,+0,1,(char)0x01, -2,-1,-1,+0,0,(char)0x01,
-    -2,-1,+0,-1,0,(char)0x02, -2,-1,+0,+0,0,(char)0x03, -2,-1,+0,+1,1,(char)0x01,
-    -2,+0,+0,-1,0,(char)0x06, -2,+0,+0,+0,1,(char)0x02, -2,+0,+0,+1,0,(char)0x03,
-    -2,+1,-1,+0,0,(char)0x04, -2,+1,+0,-1,1,(char)0x04, -2,+1,+0,+0,0,(char)0x06,
-    -2,+1,+0,+1,0,(char)0x02, -2,+2,+0,+0,1,(char)0x04, -2,+2,+0,+1,0,(char)0x04,
-    -1,-2,-1,+0,0,(char)0x80, -1,-2,+0,-1,0,(char)0x01, -1,-2,+1,-1,0,(char)0x01,
-    -1,-2,+1,+0,1,(char)0x01, -1,-1,-1,+1,0,(char)0x88, -1,-1,+1,-2,0,(char)0x40,
-    -1,-1,+1,-1,0,(char)0x22, -1,-1,+1,+0,0,(char)0x33, -1,-1,+1,+1,1,(char)0x11,
-    -1,+0,-1,+2,0,(char)0x08, -1,+0,+0,-1,0,(char)0x44, -1,+0,+0,+1,0,(char)0x11,
-    -1,+0,+1,-2,1,(char)0x40, -1,+0,+1,-1,0,(char)0x66, -1,+0,+1,+0,1,(char)0x22,
-    -1,+0,+1,+1,0,(char)0x33, -1,+0,+1,+2,1,(char)0x10, -1,+1,+1,-1,1,(char)0x44,
-    -1,+1,+1,+0,0,(char)0x66, -1,+1,+1,+1,0,(char)0x22, -1,+1,+1,+2,0,(char)0x10,
-    -1,+2,+0,+1,0,(char)0x04, -1,+2,+1,+0,1,(char)0x04, -1,+2,+1,+1,0,(char)0x04,
-    +0,-2,+0,+0,1,(char)0x80, +0,-1,+0,+1,1,(char)0x88, +0,-1,+1,-2,0,(char)0x40,
-    +0,-1,+1,+0,0,(char)0x11, +0,-1,+2,-2,0,(char)0x40, +0,-1,+2,-1,0,(char)0x20,
-    +0,-1,+2,+0,0,(char)0x30, +0,-1,+2,+1,1,(char)0x10, +0,+0,+0,+2,1,(char)0x08,
-    +0,+0,+2,-2,1,(char)0x40, +0,+0,+2,-1,0,(char)0x60, +0,+0,+2,+0,1,(char)0x20,
-    +0,+0,+2,+1,0,(char)0x30, +0,+0,+2,+2,1,(char)0x10, +0,+1,+1,+0,0,(char)0x44,
-    +0,+1,+1,+2,0,(char)0x10, +0,+1,+2,-1,1,(char)0x40, +0,+1,+2,+0,0,(char)0x60,
-    +0,+1,+2,+1,0,(char)0x20, +0,+1,+2,+2,0,(char)0x10, +1,-2,+1,+0,0,(char)0x80,
-    +1,-1,+1,+1,0,(char)0x88, +1,+0,+1,+2,0,(char)0x08, +1,+0,+2,-1,0,(char)0x40,
-    +1,+0,+2,+1,0,(char)0x10
-  }, chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 };
-  unsigned short (*brow[5])[4], *pix;
-  int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4];
-  int row, col, shift, x, y, x1, x2, y1, y2, t, weight, grads, color, diag;
-  int g, diff, thold, num, c;
-
-  for (row=0; row < 8; row++) {     /* Precalculate for bilinear */
-    for (col=1; col < 3; col++) {
-      ip = code[row][col & 1];
-      memset (sum, 0, sizeof sum);
-      for (y=-1; y <= 1; y++)
-    for (x=-1; x <= 1; x++) {
-      shift = (y==0) + (x==0);
-      if (shift == 2) continue;
-      color = FC(row+y,col+x);
-      *ip++ = (width*y + x)*4 + color;
-      *ip++ = shift;
-      *ip++ = color;
-      sum[color] += 1 << shift;
-    }
-      FORC4
-    if (c != FC(row,col)) {
-      *ip++ = c;
-      *ip++ = sum[c];
+    if (cmdline.linear || cmdline.bright < 1) {
+        scaleMax = MIN(0xffff, scaleMax * cmdline.bright);
+        FORC4 pre_mul[c] *= cmdline.bright;
     }
+    if (cmdline.verbose) {
+        fprintf(stderr, "Scaling with black=%d, ", black);
+        fprintf(stderr, "pre_mul[] = ");
+        FORC4 fprintf (stderr, " %f", pre_mul[c]);
+        fprintf(stderr, "\n");
     }
-  }
-  for (row=1; row < height-1; row++) {  /* Do bilinear interpolation */
-    for (col=1; col < width-1; col++) {
-      pix = image[row*width+col];
-      ip = code[row & 7][col & 1];
-      memset (sum, 0, sizeof sum);
-      for (g=8; g--; ) {
-    diff = pix[*ip++];
-    diff <<= *ip++;
-    sum[*ip++] += diff;
-      }
-      for (g=colors; --g; ) {
-    c = *ip++;
-    pix[c] = sum[c] / *ip++;
-      }
+    clip_max = cmdline.no_clip_color ? 0xffff : scaleMax;
+    for (row = 0; row < height; ++row) {
+        unsigned int col;
+        for (col = 0; col < width; ++col) {
+            unsigned int c;
+            for (c = 0; c < colors; ++c) {
+                int val;
+                val = image[row*width+col][c];
+                if (val != 0) {
+                    val -= black;
+                    val *= pre_mul[c];
+                    image[row*width+col][c] = MAX(0, MIN(clip_max, val));
+                }
+            }
+        }
     }
-  }
-  if (cmdline.quick_interpolate)
-    return;
-
-  for (row=0; row < 8; row++) {     /* Precalculate for VNG */
-    for (col=0; col < 2; col++) {
-      ip = code[row][col];
-      for (cp=terms, t=0; t < 64; t++) {
-    y1 = *cp++;  x1 = *cp++;
-    y2 = *cp++;  x2 = *cp++;
-    weight = *cp++;
-    grads = *cp++;
-    color = FC(row+y1,col+x1);
-    if (FC(row+y2,col+x2) != color) continue;
-    diag = (FC(row,col+1) == color && FC(row+1,col) == color) ? 2:1;
-    if (abs(y1-y2) == diag && abs(x1-x2) == diag) continue;
-    *ip++ = (y1*width + x1)*4 + color;
-    *ip++ = (y2*width + x2)*4 + color;
-    *ip++ = weight;
-    for (g=0; g < 8; g++)
-      if (grads & 1<<g) *ip++ = g;
-    *ip++ = -1;
-      }
-      *ip++ = INT_MAX;
-      for (cp=chood, g=0; g < 8; g++) {
-    y = *cp++;  x = *cp++;
-    *ip++ = (y*width + x) * 4;
-    color = FC(row,col);
-    if (FC(row+y,col+x) != color && FC(row+y*2,col+x*2) == color)
-      *ip++ = (y*width + x) * 8 + color;
-    else
-      *ip++ = 0;
-      }
+}
+
+
+
+static void CLASS
+vngInterpolate(Image const image) {
+
+    /*
+      This algorithm is officially called "Interpolation using a
+      Threshold-based variable number of gradients," described in
+      http://www-ise.stanford.edu/~tingchen/algodep/vargra.html
+
+      I've extended the basic idea to work with non-Bayer filter arrays.
+      Gradients are numbered clockwise from NW=0 to W=7.
+    */
+
+    static const signed char *cp, terms[] = {
+  -2,-2,+0,-1,0,(char)0x01, -2,-2,+0,+0,1,(char)0x01, -2,-1,-1,+0,0,(char)0x01,
+  -2,-1,+0,-1,0,(char)0x02, -2,-1,+0,+0,0,(char)0x03, -2,-1,+0,+1,1,(char)0x01,
+  -2,+0,+0,-1,0,(char)0x06, -2,+0,+0,+0,1,(char)0x02, -2,+0,+0,+1,0,(char)0x03,
+  -2,+1,-1,+0,0,(char)0x04, -2,+1,+0,-1,1,(char)0x04, -2,+1,+0,+0,0,(char)0x06,
+  -2,+1,+0,+1,0,(char)0x02, -2,+2,+0,+0,1,(char)0x04, -2,+2,+0,+1,0,(char)0x04,
+  -1,-2,-1,+0,0,(char)0x80, -1,-2,+0,-1,0,(char)0x01, -1,-2,+1,-1,0,(char)0x01,
+  -1,-2,+1,+0,1,(char)0x01, -1,-1,-1,+1,0,(char)0x88, -1,-1,+1,-2,0,(char)0x40,
+  -1,-1,+1,-1,0,(char)0x22, -1,-1,+1,+0,0,(char)0x33, -1,-1,+1,+1,1,(char)0x11,
+  -1,+0,-1,+2,0,(char)0x08, -1,+0,+0,-1,0,(char)0x44, -1,+0,+0,+1,0,(char)0x11,
+  -1,+0,+1,-2,1,(char)0x40, -1,+0,+1,-1,0,(char)0x66, -1,+0,+1,+0,1,(char)0x22,
+  -1,+0,+1,+1,0,(char)0x33, -1,+0,+1,+2,1,(char)0x10, -1,+1,+1,-1,1,(char)0x44,
+  -1,+1,+1,+0,0,(char)0x66, -1,+1,+1,+1,0,(char)0x22, -1,+1,+1,+2,0,(char)0x10,
+  -1,+2,+0,+1,0,(char)0x04, -1,+2,+1,+0,1,(char)0x04, -1,+2,+1,+1,0,(char)0x04,
+  +0,-2,+0,+0,1,(char)0x80, +0,-1,+0,+1,1,(char)0x88, +0,-1,+1,-2,0,(char)0x40,
+  +0,-1,+1,+0,0,(char)0x11, +0,-1,+2,-2,0,(char)0x40, +0,-1,+2,-1,0,(char)0x20,
+  +0,-1,+2,+0,0,(char)0x30, +0,-1,+2,+1,1,(char)0x10, +0,+0,+0,+2,1,(char)0x08,
+  +0,+0,+2,-2,1,(char)0x40, +0,+0,+2,-1,0,(char)0x60, +0,+0,+2,+0,1,(char)0x20,
+  +0,+0,+2,+1,0,(char)0x30, +0,+0,+2,+2,1,(char)0x10, +0,+1,+1,+0,0,(char)0x44,
+  +0,+1,+1,+2,0,(char)0x10, +0,+1,+2,-1,1,(char)0x40, +0,+1,+2,+0,0,(char)0x60,
+  +0,+1,+2,+1,0,(char)0x20, +0,+1,+2,+2,0,(char)0x10, +1,-2,+1,+0,0,(char)0x80,
+  +1,-1,+1,+1,0,(char)0x88, +1,+0,+1,+2,0,(char)0x08, +1,+0,+2,-1,0,(char)0x40,
+  +1,+0,+2,+1,0,(char)0x10
+    }, chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 };
+    unsigned short (*brow[5])[4], *pix;
+    int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4];
+    int row, col, shift, x, y, x1, x2, y1, y2, t, weight, grads, color, diag;
+    int g, diff, thold, num, c;
+
+    for (row=0; row < 8; row++) {     /* Precalculate for bilinear */
+        for (col=1; col < 3; col++) {
+            ip = code[row][col & 1];
+            memset (sum, 0, sizeof sum);
+            for (y=-1; y <= 1; y++)
+                for (x=-1; x <= 1; x++) {
+                    shift = (y==0) + (x==0);
+                    if (shift == 2) continue;
+                    color = FC(row+y,col+x);
+                    *ip++ = (width*y + x)*4 + color;
+                    *ip++ = shift;
+                    *ip++ = color;
+                    sum[color] += 1 << shift;
+                }
+            FORC4
+                if (c != FC(row,col)) {
+                    *ip++ = c;
+                    *ip++ = sum[c];
+                }
+        }
     }
-  }
-  brow[4] = calloc (width*3, sizeof **brow);
-  merror (brow[4], "vng_interpolate()");
-  for (row=0; row < 3; row++)
-    brow[row] = brow[4] + row*width;
-  for (row=2; row < height-2; row++) {      /* Do VNG interpolation */
-    for (col=2; col < width-2; col++) {
-      pix = image[row*width+col];
-      ip = code[row & 7][col & 1];
-      memset (gval, 0, sizeof gval);
-      while ((g = ip[0]) != INT_MAX) {      /* Calculate gradients */
-    num = (diff = pix[g] - pix[ip[1]]) >> 31;
-    gval[ip[3]] += (diff = ((diff ^ num) - num) << ip[2]);
-    ip += 5;
-    if ((g = ip[-1]) == -1) continue;
-    gval[g] += diff;
-    while ((g = *ip++) != -1)
-      gval[g] += diff;
-      }
-      ip++;
-      gmin = gmax = gval[0];            /* Choose a threshold */
-      for (g=1; g < 8; g++) {
-    if (gmin > gval[g]) gmin = gval[g];
-    if (gmax < gval[g]) gmax = gval[g];
-      }
-      if (gmax == 0) {
-    memcpy (brow[2][col], pix, sizeof *image);
-    continue;
-      }
-      thold = gmin + (gmax >> 1);
-      memset (sum, 0, sizeof sum);
-      color = FC(row,col);
-      for (num=g=0; g < 8; g++,ip+=2) {     /* Average the neighbors */
-    if (gval[g] <= thold) {
-      FORC4
-        if (c == color && ip[1])
-          sum[c] += (pix[c] + pix[ip[1]]) >> 1;
-        else
-          sum[c] += pix[ip[0] + c];
-      num++;
+    for (row=1; row < height-1; row++) {  /* Do bilinear interpolation */
+        for (col=1; col < width-1; col++) {
+            pix = image[row*width+col];
+            ip = code[row & 7][col & 1];
+            memset (sum, 0, sizeof sum);
+            for (g=8; g--; ) {
+                diff = pix[*ip++];
+                diff <<= *ip++;
+                sum[*ip++] += diff;
+            }
+            for (g=colors; --g; ) {
+                c = *ip++;
+                pix[c] = sum[c] / *ip++;
+            }
+        }
     }
-      }
-      FORC4 {                   /* Save to buffer */
-    t = pix[color];
-    if (c != color) {
-      t += (sum[c] - sum[color])/num;
-      if (t < 0) t = 0;
-      if (t > clip_max) t = clip_max;
+    if (cmdline.quick_interpolate)
+        return;
+
+    for (row=0; row < 8; row++) {     /* Precalculate for VNG */
+        for (col=0; col < 2; col++) {
+            ip = code[row][col];
+            for (cp=terms, t=0; t < 64; t++) {
+                y1 = *cp++;  x1 = *cp++;
+                y2 = *cp++;  x2 = *cp++;
+                weight = *cp++;
+                grads = *cp++;
+                color = FC(row+y1,col+x1);
+                if (FC(row+y2,col+x2) != color) continue;
+                diag =
+                    (FC(row,col+1) == color && FC(row+1,col) == color) ? 2:1;
+                if (abs(y1-y2) == diag && abs(x1-x2) == diag) continue;
+                *ip++ = (y1*width + x1)*4 + color;
+                *ip++ = (y2*width + x2)*4 + color;
+                *ip++ = weight;
+                for (g=0; g < 8; g++)
+                    if (grads & 1<<g) *ip++ = g;
+                *ip++ = -1;
+            }
+            *ip++ = INT_MAX;
+            for (cp=chood, g=0; g < 8; g++) {
+                y = *cp++;  x = *cp++;
+                *ip++ = (y*width + x) * 4;
+                color = FC(row,col);
+                if (FC(row+y,col+x) != color && FC(row+y*2,col+x*2) == color)
+                    *ip++ = (y*width + x) * 8 + color;
+                else
+                    *ip++ = 0;
+            }
+        }
     }
-    brow[2][col][c] = t;
-      }
+    brow[4] = calloc (width*3, sizeof **brow);
+    merror (brow[4], "vngInterpolate()");
+    for (row=0; row < 3; row++)
+        brow[row] = brow[4] + row*width;
+    for (row=2; row < height-2; row++) {      /* Do VNG interpolation */
+        for (col=2; col < width-2; col++) {
+            pix = image[row*width+col];
+            ip = code[row & 7][col & 1];
+            memset (gval, 0, sizeof gval);
+            while ((g = ip[0]) != INT_MAX) {      /* Calculate gradients */
+                num = (diff = pix[g] - pix[ip[1]]) >> 31;
+                gval[ip[3]] += (diff = ((diff ^ num) - num) << ip[2]);
+                ip += 5;
+                if ((g = ip[-1]) == -1) continue;
+                gval[g] += diff;
+                while ((g = *ip++) != -1)
+                    gval[g] += diff;
+            }
+            ip++;
+            gmin = gmax = gval[0];            /* Choose a threshold */
+            for (g=1; g < 8; g++) {
+                if (gmin > gval[g]) gmin = gval[g];
+                if (gmax < gval[g]) gmax = gval[g];
+            }
+            if (gmax == 0) {
+                memcpy (brow[2][col], pix, sizeof *image);
+                continue;
+            }
+            thold = gmin + (gmax >> 1);
+            memset (sum, 0, sizeof sum);
+            color = FC(row,col);
+            for (num=g=0; g < 8; g++,ip+=2) {     /* Average the neighbors */
+                if (gval[g] <= thold) {
+                    FORC4
+                        if (c == color && ip[1])
+                            sum[c] += (pix[c] + pix[ip[1]]) >> 1;
+                        else
+                            sum[c] += pix[ip[0] + c];
+                    num++;
+                }
+            }
+            FORC4 {                   /* Save to buffer */
+                t = pix[color];
+                if (c != color) {
+                    t += (sum[c] - sum[color])/num;
+                    if (t < 0) t = 0;
+                    if (t > clip_max) t = clip_max;
+                }
+                brow[2][col][c] = t;
+            }
+        }
+        if (row > 3)                /* Write buffer to image */
+            memcpy(image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
+        for (g=0; g < 4; g++)
+            brow[(g-1) & 3] = brow[g];
     }
-    if (row > 3)                /* Write buffer to image */
-      memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
-    for (g=0; g < 4; g++)
-      brow[(g-1) & 3] = brow[g];
-  }
-  memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
-  memcpy (image[(row-1)*width+2], brow[1]+2, (width-4)*sizeof *image);
-  free (brow[4]);
+    memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image);
+    memcpy (image[(row-1)*width+2], brow[1]+2, (width-4)*sizeof *image);
+    free (brow[4]);
 }
 
-#ifdef USE_LCMS
-static void 
-apply_profile(FILE *       const ifP,
-              const char * const pfname)
-{
-  char *prof;
-  cmsHPROFILE hInProfile=NULL, hOutProfile;
-  cmsHTRANSFORM hTransform;
-
-  if (pfname)
-    hInProfile = cmsOpenProfileFromFile (pfname, "r");
-  else if (profile_length) {
-    prof = malloc (profile_length);
-    merror (prof, "apply_profile()");
-    fseek (ifP, profile_offset, SEEK_SET);
-    fread (prof, 1, profile_length, ifP);
-    hInProfile = cmsOpenProfileFromMem (prof, profile_length);
-    free (prof);
-  }
-  if (!hInProfile) return;
-  if (cmdline.verbose)
-      pm_message( "Applying color profile...");
-  maximum = 0xffff;
-  use_gamma = use_coeff = 0;
-
-  hOutProfile = cmsCreate_sRGBProfile();
-  hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16,
-    hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0);
-  cmsDoTransform (hTransform, image, image, width*height);
-
-  cmsDeleteTransform (hTransform);
-  cmsCloseProfile (hInProfile);
-  cmsCloseProfile (hOutProfile);
-}
-#else
-static void 
-apply_profile(FILE *       const ifP,
-              const char * const pfname)
-{
-}
-#endif
 
-/*
+
+static void CLASS
+convertToRgb(Image        const image,
+             unsigned int const trim) {
+/*----------------------------------------------------------------------------
    Convert the entire image to RGB colorspace and build a histogram.
- */
-static void CLASS convert_to_rgb()
-{
-  int row, col, r, g, c=0;
-  unsigned short *img;
-  float rgb[3];
-
-  if (cmdline.document_mode)
-    colors = 1;
-  memset (histogram, 0, sizeof histogram);
-  for (row = trim; row < height-trim; row++)
-    for (col = trim; col < width-trim; col++) {
-      img = image[row*width+col];
-      if (cmdline.document_mode)
-    c = FC(row,col);
-      if (colors == 4 && !use_coeff)    /* Recombine the greens */
-    img[1] = (img[1] + img[3]) >> 1;
-      if (colors == 1)          /* RGB from grayscale */
-    for (r=0; r < 3; r++)
-      rgb[r] = img[c];
-      else if (use_coeff) {     /* RGB via coeff[][] */
-    for (r=0; r < 3; r++)
-      for (rgb[r]=g=0; g < colors; g++)
-        rgb[r] += img[g] * coeff[r][g];
-      } else                /* RGB from RGB (easy) */
-    goto norgb;
-      for (r=0; r < 3; r++) {
-    if (rgb[r] < 0)        rgb[r] = 0;
-    if (rgb[r] > clip_max) rgb[r] = clip_max;
-    img[r] = rgb[r];
-      }
-norgb:
-      for (r=0; r < 3; r++)
-    histogram[r][img[r] >> 3]++;
+
+   We modify 'image' to change it from whatever it is now to RGB.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+    unsigned int c;
+    float rgb[3];  /* { red, green, blue } */
+
+    c = 0;  /* initial value */
+
+    if (cmdline.document_mode)
+        colors = 1;
+
+    memset(histogram, 0, sizeof histogram);
+
+    for (row = 0 + trim; row < height - trim; ++row) {
+        unsigned int col;
+        for (col = 0 + trim; col < width - trim; ++col) {
+            unsigned short * const img = image[row*width+col];
+
+            if (cmdline.document_mode)
+                c = FC(row,col);
+
+            if (colors == 4 && !use_coeff)
+                /* Recombine the greens */
+                img[1] = (img[1] + img[3]) / 2;
+
+            if (colors == 1) {
+                /* RGB from grayscale */
+                unsigned int i;
+                for (i = 0; i < 3; ++i)
+                    rgb[i] = img[c];
+            } else if (use_coeff) {
+                /* RGB via coeff[][] */
+                unsigned int i;
+                for (i = 0; i < 3; ++i) {
+                    unsigned int j;
+                    for (j = 0, rgb[i]= 0; j < colors; ++j)
+                        rgb[i] += img[j] * coeff[i][j];
+                }
+            } else {
+                /* RGB from RGB (easy) */
+                unsigned int i;
+                for (i = 0; i < 3; ++i)
+                    rgb[i] = img[i];
+            }
+            {
+                unsigned int i;
+                for (i = 0; i < 3; ++i)
+                    img[i] = MIN(clip_max, MAX(0, rgb[i]));
+            }
+            {
+                unsigned int i;
+                for (i = 0; i < 3; ++i)
+                    ++histogram[i][img[i] >> 3];
+            }
+        }
     }
 }
 
-static void CLASS fuji_rotate()
-{
-  int i, wide, high, row, col;
-  double step;
-  float r, c, fr, fc;
-  unsigned ur, uc;
-  unsigned short (*img)[4], (*pix)[4];
-
-  if (!fuji_width) return;
-  if (cmdline.verbose)
-    pm_message ("Rotating image 45 degrees...");
-  fuji_width = (fuji_width + shrink) >> shrink;
-  step = sqrt(0.5);
-  wide = fuji_width / step;
-  high = (height - fuji_width) / step;
-  img = calloc (wide*high, sizeof *img);
-  merror (img, "fuji_rotate()");
-
-  for (row=0; row < high; row++)
-    for (col=0; col < wide; col++) {
-      ur = r = fuji_width + (row-col)*step;
-      uc = c = (row+col)*step;
-      if (ur > height-2 || uc > width-2) continue;
-      fr = r - ur;
-      fc = c - uc;
-      pix = image + ur*width + uc;
-      for (i=0; i < colors; i++)
-    img[row*wide+col][i] =
-      (pix[    0][i]*(1-fc) + pix[      1][i]*fc) * (1-fr) +
-      (pix[width][i]*(1-fc) + pix[width+1][i]*fc) * fr;
+
+
+static void CLASS
+fujiRotate(Image * const imageP) {
+
+    int wide;
+    int high;
+    unsigned int row;
+    double step;
+    float r, c, fr, fc;
+    unsigned short (*newImage)[4];
+    unsigned short (*pix)[4];
+
+    if (fuji_width > 0) {
+        if (cmdline.verbose)
+            pm_message ("Rotating image 45 degrees...");
+
+        fuji_width = (fuji_width + shrink) >> shrink;
+        step = sqrt(0.5);
+        wide = fuji_width / step;
+        high = (height - fuji_width) / step;
+        newImage = calloc (wide*high, sizeof *newImage);
+        merror (newImage, "fujiRotate()");
+
+        for (row = 0; row < high; ++row) {
+            unsigned int col;
+            for (col = 0; col < wide; ++col) {
+                unsigned int ur = r = fuji_width + (row-col)*step;
+                unsigned int uc = c = (row+col)*step;
+
+                unsigned int i;
+
+                if (ur > height-2 || uc > width-2)
+                    continue;
+
+                fr = r - ur;
+                fc = c - uc;
+                pix = (*imageP) + ur * width + uc;
+
+                for (i = 0; i < colors; ++i) {
+                    newImage[row*wide+col][i] =
+                        (pix[    0][i]*(1-fc) + pix[      1][i]*fc) * (1-fr) +
+                        (pix[width][i]*(1-fc) + pix[width+1][i]*fc) * fr;
+                } 
+            }
+        }        
+        free(*imageP);
+        width  = wide;
+        height = high;
+        *imageP  = newImage;
+        fuji_width = 0;
     }
-  free (image);
-  width  = wide;
-  height = high;
-  image  = img;
-  fuji_width = 0;
 }
 
-static void CLASS flip_image()
-{
+
+
+static void CLASS
+flipImage(Image const image) {
+
     unsigned *flag;
-    int size, base, dest, next, row, col, temp;
+    int size, base, dest;
 
     struct imageCell {
         unsigned char contents[8];
     };
     struct imageCell * img;
-    struct imageCell hold;
 
     switch ((flip+3600) % 360) {
-    case 270:  flip = 5;  break;
-    case 180:  flip = 3;  break;
-    case  90:  flip = 6;
+    case 270:  flip = 0x5;  break;
+    case 180:  flip = 0x3;  break;
+    case  90:  flip = 0x6;
     }
     img = (struct imageCell *) image;
     size = height * width;
     flag = calloc ((size+31) >> 5, sizeof *flag);
-    merror (flag, "flip_image()");
-    for (base = 0; base < size; base++) {
-        if (flag[base >> 5] & (1 << (base & 31)))
-            continue;
-        dest = base;
-        hold = img[base];
-        while (1) {
-            if (flip & 4) {
-                row = dest % height;
-                col = dest / height;
-            } else {
-                row = dest / width;
-                col = dest % width;
+    merror (flag, "flipImage()");
+    for (base = 0; base < size; ++base) {
+        if (flag[base >> 5] & (1 << (base & 31))) {
+            /* nothing */
+        } else {
+            struct imageCell const hold = img[base];
+            dest = base;
+            while (true) {
+                unsigned int col;
+                unsigned int row;
+                int next;
+                if (flip & 0x4) {
+                    row = dest % height;
+                    col = dest / height;
+                } else {
+                    row = dest / width;
+                    col = dest % width;
+                }
+                if (flip & 0x2)
+                    row = height - 1 - row;
+                if (flip & 1)
+                    col = width - 1 - col;
+                next = row * width + col;
+                if (next == base)
+                    break;
+                flag[next >> 5] |= 1 << (next & 31);
+                img[dest] = img[next];
+                dest = next;
             }
-            if (flip & 2)
-                row = height - 1 - row;
-            if (flip & 1)
-                col = width - 1 - col;
-            next = row * width + col;
-            if (next == base) break;
-            flag[next >> 5] |= 1 << (next & 31);
-            img[dest] = img[next];
-            dest = next;
+            img[dest] = hold;
         }
-        img[dest] = hold;
     }
     free (flag);
-    if (flip & 4) {
-        temp = height;
+    if (flip & 0x4) {
+        int const oldHeight = height;
+        int const oldYmag = ymag;
+
         height = width;
-        width = temp;
-        temp = ymag;
+        width = oldHeight;
         ymag = xmag;
-        xmag = temp;
+        xmag = oldYmag;
     }
 }
 
-/*
-   Write the image as an RGB PAM image
- */
-static void CLASS write_pam_nonlinear (FILE *ofp)
-{
-  unsigned char lut[0x10000];
-  int perc, c, val, total, i, row, col;
-  float white=0, r;
-  struct pam pam;
-  tuple * tuplerow;
-
-  pam.size   = sizeof(pam);
-  pam.len    = PAM_STRUCT_SIZE(tuple_type);
-  pam.file   = ofp;
-  pam.width  = xmag*(width-trim*2);
-  pam.height = ymag*(height-trim*2);
-  pam.depth  = 3;
-  pam.format = PAM_FORMAT;
-  pam.maxval = 255;
-  strcpy(pam.tuple_type, "RGB");
-
-  pnm_writepaminit(&pam);
-
-  tuplerow = pnm_allocpamrow(&pam);
-
-  perc = width * height * 0.01;     /* 99th percentile white point */
-  if (fuji_width) perc /= 2;
-  FORC3 {
-    for (val=0x2000, total=0; --val > 32; )
-      if ((total += histogram[c][val]) > perc) break;
-    if (white < val) white = val;
-  }
-  white *= 8 / cmdline.bright;
-  for (i=0; i < 0x10000; i++) {
-    r = i / white;
-    val = 256 * ( !use_gamma ? r :
-    r <= 0.018 ? r*4.5 : pow(r,0.45)*1.099-0.099 );
-    if (val > 255) val = 255;
-    lut[i] = val;
-  }
-  for (row=trim; row < height-trim; row++) {
-      for (col=trim; col < width-trim; col++) {
-          unsigned int plane;
-          for (plane=0; plane < pam.depth; ++plane) {
-              unsigned int copy;
-              for (copy=0; copy < xmag; ++copy) {
-                  unsigned int const pamcol = xmag*(col-trim)+copy;
-                  tuplerow[pamcol][plane] = lut[image[row*width+col][plane]];
-              }
-          }
-      }
-      {
-          unsigned int copy;
-          for (copy=0; copy < ymag; ++copy)
-              pnm_writepamrow(&pam, tuplerow);
-      }
-  }
-  pnm_freepamrow(tuplerow);
+
+
+static void CLASS
+writePamLinear(FILE *       const ofP,
+               Image        const image,
+               unsigned int const trim) {
+/*----------------------------------------------------------------------------
+   Write the image 'image' to a 16-bit PAM file with linear color space
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    struct pam pam;
+    tuple * tuplerow;
+
+    pam.size   = sizeof(pam);
+    pam.len    = PAM_STRUCT_SIZE(tuple_type);
+    pam.file   = ofP;
+    pam.width  = width - trim - trim;
+    pam.height = height - trim - trim;
+    pam.depth  = 3;
+    pam.format = PAM_FORMAT;
+    pam.maxval = MAX(maximum, 256);
+    strcpy(pam.tuple_type, "RGB");
+
+    pnm_writepaminit(&pam);
+
+    tuplerow = pnm_allocpamrow(&pam);
+
+    for (row = 0 + trim; row < height - trim; ++row) {
+        unsigned int col;
+        for (col = 0 + trim; col < width - trim; ++col) {
+            unsigned int const pamCol = col - trim;
+            unsigned int plane;
+            for (plane = 0; plane < 3; ++plane)
+                tuplerow[pamCol][plane] = image[row*width+col][plane];
+        }
+        pnm_writepamrow(&pam, tuplerow);
+    }
+    pnm_freepamrow(tuplerow);
 }
 
-/*
-   Write the image to a 16-bit PAM file with linear color space
- */
-static void CLASS write_pam_linear (FILE *ofp)
-{
-  int row;
-
-  struct pam pam;
-  tuple * tuplerow;
-
-  if (maximum < 256) maximum = 256;
-
-  pam.size   = sizeof(pam);
-  pam.len    = PAM_STRUCT_SIZE(tuple_type);
-  pam.file   = ofp;
-  pam.width  = width-trim*2;
-  pam.height = height-trim*2;
-  pam.depth  = 3;
-  pam.format = PAM_FORMAT;
-  pam.maxval = MAX(maximum, 256);
-  strcpy(pam.tuple_type, "RGB");
-
-  pnm_writepaminit(&pam);
-
-  tuplerow = pnm_allocpamrow(&pam);
-
-  for (row = trim; row < height-trim; row++) {
-      unsigned int col;
-      for (col = trim; col < width-trim; col++) {
-          unsigned int const pamCol = col - trim;
-          unsigned int plane;
-          for (plane = 0; plane < 3; ++plane)
-              tuplerow[pamCol][plane] = image[row*width+col][plane];
-      }
-      pnm_writepamrow(&pam, tuplerow);
-  }
-  pnm_freepamrow(tuplerow);
+
+
+static void CLASS
+writePamNonlinear(FILE *       const ofP,
+                  Image        const image,
+                  unsigned int const trim) {
+/*----------------------------------------------------------------------------
+  Write the image 'image' as an RGB PAM image
+-----------------------------------------------------------------------------*/
+    unsigned char lut[0x10000];
+    int perc;
+    int c;
+    int total;
+    int i;
+    unsigned int row;
+    float white;
+    float r;
+    struct pam pam;
+    tuple * tuplerow;
+
+    white = 0;  /* initial value */
+
+    pam.size   = sizeof(pam);
+    pam.len    = PAM_STRUCT_SIZE(tuple_type);
+    pam.file   = ofP;
+    pam.width  = xmag*(width-trim*2);
+    pam.height = ymag*(height-trim*2);
+    pam.depth  = 3;
+    pam.format = PAM_FORMAT;
+    pam.maxval = 255;
+    strcpy(pam.tuple_type, "RGB");
+
+    pnm_writepaminit(&pam);
+
+    tuplerow = pnm_allocpamrow(&pam);
+
+    perc = width * height * 0.01;     /* 99th percentile white point */
+    if (fuji_width) perc /= 2;
+    FORC3 {
+        int val;
+        for (val=0x2000, total=0; --val > 32; )
+            if ((total += histogram[c][val]) > perc) break;
+        if (white < val)
+            white = val;
+    }
+    white *= 8 / cmdline.bright;
+    for (i=0; i < 0x10000; ++i) {
+        int val;
+        r = i / white;
+        val = 256 * ( !use_gamma ? r :
+                      r <= 0.018 ? r*4.5 : pow(r,0.45)*1.099-0.099 );
+        lut[i] = MIN(255, val);
+    }
+
+    for (row = 0 + trim; row < height - trim; ++row) {
+        unsigned int col;
+        for (col = 0 + trim; col < width - trim; ++col) {
+            unsigned int plane;
+            for (plane = 0; plane < pam.depth; ++plane) {
+                sample const value = lut[image[row*width+col][plane]];
+                unsigned int copy;
+                for (copy = 0; copy < xmag; ++copy) {
+                    unsigned int const pamcol = xmag*(col-trim)+copy;
+                    tuplerow[pamcol][plane] = value;
+                }
+            }
+        }
+        {
+            unsigned int copy;
+            for (copy = 0; copy < ymag; ++copy)
+                pnm_writepamrow(&pam, tuplerow);
+        }
+    }
+    pnm_freepamrow(tuplerow);
 }
 
 
 
 static void CLASS
-writePam(FILE * const ofP,
-         bool   const linear) {
+writePam(FILE *       const ofP,
+         Image        const image,
+         bool         const linear,
+         unsigned int const trim) {
 
     if (linear)
-        write_pam_linear(ofP);
+        writePamLinear(ofP, image, trim);
     else
-        write_pam_nonlinear(ofP);
+        writePamNonlinear(ofP, image, trim);
 }
 
 
 
-
 static void CLASS
-convertIt(FILE *    const ifP,
-          FILE *    const ofP,
-          loadRawFn const load_raw) {
+convertIt(FILE *       const ifP,
+          FILE *       const ofP,
+          LoadRawFn *  const load_raw) {
+
+    Image image;
+    unsigned int trim;
 
     shrink = cmdline.half_size && filters;
     iheight = (height + shrink) >> shrink;
     iwidth  = (width  + shrink) >> shrink;
-    image = calloc (iheight*iwidth*sizeof *image + meta_length, 1);
+    image = calloc (iheight*iwidth*sizeof(*image) + meta_length, 1);
     merror (image, "main()");
     meta_data = (char *) (image + iheight*iwidth);
     if (cmdline.verbose)
@@ -807,43 +887,49 @@ convertIt(FILE *    const ifP,
 
     ifp = ifP;  /* Set global variable for (*load_raw)() */
 
-    load_raw();
-    bad_pixels();
+    load_raw(image);
+    fixBadPixels(image);
     height = iheight;
     width  = iwidth;
     if (is_foveon) {
         if (cmdline.verbose)
             pm_message ("Foveon interpolation...");
-        foveon_interpolate(coeff);
+        foveon_interpolate(image, coeff);
     } else {
-        scale_colors();
+        scaleColors(image);
     }
-    if (shrink) filters = 0;
-    trim = 0;
+    if (shrink)
+        filters = 0;
+
     if (filters && !cmdline.document_mode) {
         trim = 1;
         if (cmdline.verbose)
             pm_message ("%s interpolation...",
                         cmdline.quick_interpolate ? "Bilinear":"VNG");
-        vng_interpolate();
-    }
-    fuji_rotate();
-    apply_profile(ifP, cmdline.profile);
+        vngInterpolate(image);
+    } else
+        trim = 0;
+
+    fujiRotate(&image);
+
     if (cmdline.verbose)
         pm_message ("Converting to RGB colorspace...");
-    convert_to_rgb();
+    convertToRgb(image, trim);
 
     if (flip) {
         if (cmdline.verbose)
             pm_message ("Flipping image %c:%c:%c...",
                         flip & 1 ? 'H':'0', flip & 2 ? 'V':'0', 
                         flip & 4 ? 'T':'0');
-        flip_image();
+        flipImage(image);
     }
-    writePam(ofP, cmdline.linear);
+    writePam(ofP, image, cmdline.linear, trim);
+
+    free(image);
 }
 
 
+
 int 
 main (int argc, char **argv) {
 
@@ -851,7 +937,7 @@ main (int argc, char **argv) {
 
     FILE * ifP;
     int rc;
-    loadRawFn load_raw;
+    LoadRawFn * load_raw;
 
     pnm_init(&argc, argv);
 
@@ -861,8 +947,6 @@ main (int argc, char **argv) {
 
     ifP = pm_openr(cmdline.inputFileName);
 
-    image = NULL;
-    
     rc = identify(ifP,
                   cmdline.use_secondary, cmdline.use_camera_rgb,
                   cmdline.red_scale, cmdline.blue_scale,
@@ -881,7 +965,6 @@ main (int argc, char **argv) {
     }
     pm_close(ifP);
     pm_close(ofP);
-    free(image);
 
     return 0;
 }
diff --git a/converter/other/cameratopam/cameratopam.h b/converter/other/cameratopam/cameratopam.h
new file mode 100644
index 00000000..9ff33cb3
--- /dev/null
+++ b/converter/other/cameratopam/cameratopam.h
@@ -0,0 +1,6 @@
+#ifndef CAMERATOPAM_H_INCLUDED
+#define CAMERATOPAM_H_INCLUDED
+
+typedef unsigned short (*Image)[4];
+
+#endif
diff --git a/converter/other/cameratopam/canon.c b/converter/other/cameratopam/canon.c
index a34771d0..96a6210b 100644
--- a/converter/other/cameratopam/canon.c
+++ b/converter/other/cameratopam/canon.c
@@ -9,7 +9,7 @@
 
 
 void 
-canon_600_load_raw(void) {
+canon_600_load_raw(Image const image) {
     unsigned char  data[1120], *dp;
     unsigned short pixel[896], *pix;
     int irow, orow, col;
@@ -42,7 +42,7 @@ canon_600_load_raw(void) {
 
 
 void
-canon_a5_load_raw(void) {
+canon_a5_load_raw(Image const image) {
     unsigned char  data[1940], *dp;
     unsigned short pixel[1552], *pix;
     int row, col;
@@ -97,7 +97,7 @@ canon_has_lowbits()
 
 
 void 
-canon_compressed_load_raw(void) {
+canon_compressed_load_raw(Image const image) {
     unsigned short *pixel, *prow;
     int lowbits, i, row, r, col, save, val;
     unsigned irow, icol;
diff --git a/converter/other/cameratopam/canon.h b/converter/other/cameratopam/canon.h
index 041cdc4d..fe6a5a08 100644
--- a/converter/other/cameratopam/canon.h
+++ b/converter/other/cameratopam/canon.h
@@ -1,13 +1,12 @@
 #ifndef CANON_H_INCLUDED
 #define CANON_H_INCLUDED
 
-void 
-canon_600_load_raw(void);
+#include "camera.h"
 
-void
-canon_a5_load_raw(void);
+LoadRawFn canon_600_load_raw;
 
-void 
-canon_compressed_load_raw(void);
+LoadRawFn canon_a5_load_raw;
+
+LoadRawFn canon_compressed_load_raw;
 
 #endif
diff --git a/converter/other/cameratopam/dng.c b/converter/other/cameratopam/dng.c
index 44d45b02..bddfd9f4 100644
--- a/converter/other/cameratopam/dng.c
+++ b/converter/other/cameratopam/dng.c
@@ -4,70 +4,105 @@
 #include "dng.h"
 
 void 
-dng_coeff (double cc[4][4], double cm[4][3], double xyz[3])
-{
-  static const double rgb_xyz[3][3] = {     /* RGB from XYZ */
-    {  3.240479, -1.537150, -0.498535 },
-    { -0.969256,  1.875992,  0.041556 },
-    {  0.055648, -0.204043,  1.057311 } };
+dng_coeff (double cc[4][4],
+           double cm[4][3],
+           double xyz[3]) {
+    static const double rgb_xyz[3][3] = {     /* RGB from XYZ */
+        {  3.240479, -1.537150, -0.498535 },
+        { -0.969256,  1.875992,  0.041556 },
+        {  0.055648, -0.204043,  1.057311 } };
 #if 0
-  static const double xyz_rgb[3][3] = {     /* XYZ from RGB */
-    { 0.412453, 0.357580, 0.180423 },
-    { 0.212671, 0.715160, 0.072169 },
-    { 0.019334, 0.119193, 0.950227 } };
+    static const double xyz_rgb[3][3] = {     /* XYZ from RGB */
+        { 0.412453, 0.357580, 0.180423 },
+        { 0.212671, 0.715160, 0.072169 },
+        { 0.019334, 0.119193, 0.950227 } };
 #endif
-  double cam_xyz[4][3], xyz_cam[3][4], invert[3][6], num;
-  int i, j, k;
+    double cam_xyz[4][3], xyz_cam[3][4], invert[3][6];
+    unsigned int i;
 
-  memset (cam_xyz, 0, sizeof cam_xyz);
-  for (i=0; i < colors; i++)
-    for (j=0; j < 3; j++)
-      for (k=0; k < colors; k++)
-    cam_xyz[i][j] += cc[i][k] * cm[k][j] * xyz[j];
+    for (i = 0; i < colors; ++i) {
+        unsigned int j;
+        for (j = 0; j < 3; ++j) {
+            unsigned int k;
+            for (k = 0, cam_xyz[i][j] = 0.0; k < colors; ++k) {
+                cam_xyz[i][j] += cc[i][k] * cm[k][j] * xyz[j];
+            }
+        }
+    }
+    for (i = 0; i < colors; ++i) {
+        unsigned int j;
+        double camXyzSum;
+
+        for (j = 0, camXyzSum = 0.0; j < 3; ++j)
+            camXyzSum += cam_xyz[i][j];
 
-  for (i=0; i < colors; i++) {
-    for (num=j=0; j < 3; j++)
-      num += cam_xyz[i][j];
-    for (j=0; j < 3; j++)
-      cam_xyz[i][j] /= num;
-    pre_mul[i] = 1 / num;
-  }
-  for (i=0; i < 3; i++) {
-    for (j=0; j < 6; j++)
-      invert[i][j] = j == i+3;
-    for (j=0; j < 3; j++)
-      for (k=0; k < colors; k++)
-    invert[i][j] += cam_xyz[k][i] * cam_xyz[k][j];
-  }
-  for (i=0; i < 3; i++) {
-    num = invert[i][i];
-    for (j=0; j < 6; j++)       /* Normalize row i */
-      invert[i][j] /= num;
-    for (k=0; k < 3; k++) {     /* Subtract it from other rows */
-      if (k==i) continue;
-      num = invert[k][i];
-      for (j=0; j < 6; j++)
-    invert[k][j] -= invert[i][j] * num;
+        for (j = 0; j < 3; ++j)
+            cam_xyz[i][j] /= camXyzSum;
+
+        pre_mul[i] = 1 / camXyzSum;
+    }
+    for (i = 0; i < 3; ++i) {
+        unsigned int j;
+        for (j = 0; j < 6; ++j)
+            invert[i][j] = j == i+3;
+        for (j = 0; j < 3; ++j) {
+            unsigned int k;
+            for (k = 0; k < colors; ++k)
+                invert[i][j] += cam_xyz[k][i] * cam_xyz[k][j];
+        }
     }
-  }
-  memset (xyz_cam, 0, sizeof xyz_cam);
-  for (i=0; i < 3; i++)
-    for (j=0; j < colors; j++)
-      for (k=0; k < 3; k++)
-    xyz_cam[i][j] += invert[i][k+3] * cam_xyz[j][k];
+    for (i = 0; i < 3; ++i) {
+        double const num = invert[i][i];
+        unsigned int j;
+        unsigned int k;
+        for (j = 0; j < 6; ++j)       /* Normalize row i */
+            invert[i][j] /= num;
+        for (k = 0; k < 3; ++k) {     /* Subtract it from other rows */
+            if (k != i) {
+                double const num = invert[k][i];
+                unsigned int j;
+                for (j = 0; j < 6; ++j)
+                    invert[k][j] -= invert[i][j] * num;
+            }
+        }
+    }
+
+    memset(xyz_cam, 0, sizeof xyz_cam);
 
-  memset (coeff, 0, sizeof coeff);
-  for (i=0; i < 3; i++)
-    for (j=0; j < colors; j++)
-      for (k=0; k < 3; k++)
-    coeff[i][j] += rgb_xyz[i][k] * xyz_cam[k][j];
+    for (i = 0; i < 3; ++i) {
+        unsigned int j;
+        for (j = 0; j < colors; ++j) {
+            unsigned int k;
+            for (k = 0; k < 3; ++k)
+                xyz_cam[i][j] += invert[i][k+3] * cam_xyz[j][k];
+        }
+    }
+    memset (coeff, 0, sizeof coeff);
 
-  for (num=j=0; j < colors; j++)
-    num += coeff[1][j];
-  for (i=0; i < 3; i++) {
-    for (j=0; j < colors; j++)
-      coeff[i][j] /= num;
-  }
-  use_coeff = 1;
+    for (i = 0; i < 3; ++i) {
+        unsigned int j;
+        for (j = 0; j < colors; ++j) {
+            unsigned int k;
+            for (k = 0; k < 3; ++k)
+                coeff[i][j] += rgb_xyz[i][k] * xyz_cam[k][j];
+        }
+    }
+    {
+        double greenSum;
+        unsigned int j;
+        unsigned int i;
+
+        for (j = 0, greenSum = 0.0; j < colors; ++j)
+            greenSum += coeff[1][j];
+
+        for (i = 0; i < 3; ++i) {
+            unsigned int j;
+            for (j = 0; j < colors; ++j)
+                coeff[i][j] /= greenSum;
+        }
+    }
+    use_coeff = 1;
 }
 
+
+
diff --git a/converter/other/cameratopam/dng.h b/converter/other/cameratopam/dng.h
index 01e7e0a5..56293563 100644
--- a/converter/other/cameratopam/dng.h
+++ b/converter/other/cameratopam/dng.h
@@ -1,2 +1,4 @@
 void 
-dng_coeff (double cc[4][4], double cm[4][3], double xyz[3]);
+dng_coeff(double cc[4][4],
+          double cm[4][3],
+          double xyz[3]);
diff --git a/converter/other/cameratopam/foveon.c b/converter/other/cameratopam/foveon.c
index a8d62bee..a3e5449a 100644
--- a/converter/other/cameratopam/foveon.c
+++ b/converter/other/cameratopam/foveon.c
@@ -141,8 +141,8 @@ parse_foveon(FILE * const ifp) {
 
 
 void  
-foveon_coeff(bool * const useCoeffP,
-             float        coeff[3][4]) {
+foveon_coeff(int * const useCoeffP,
+             float       coeff[3][4]) {
 
     static const float foveon[3][3] =
     { {  1.4032, -0.2231, -0.1016 },
@@ -211,7 +211,7 @@ foveon_load_camf() {
 
 
 void  
-foveon_load_raw() {
+foveon_load_raw(Image const image) {
 
     struct decode *dindex;
     short diff[1024], pred[3];
@@ -391,7 +391,8 @@ static int  foveon_apply_curve (short *curve, int i)
 }
 
 void  
-foveon_interpolate(float coeff[3][4]) {
+foveon_interpolate(Image const image,
+                   float coeff[3][4]) {
 
     static const short hood[] = { 
         -1,-1, -1,0, -1,1, 0,-1, 0,1, 1,-1, 1,0, 1,1 };
@@ -472,18 +473,22 @@ foveon_interpolate(float coeff[3][4]) {
     sgrow = calloc (dim[1], sizeof *sgrow);
     sgx = (width + dim[1]-2) / (dim[1]-1);
 
-    black = calloc (height, sizeof *black);
-    for (row=0; row < height; row++) {
-        for (i=0; i < 6; i++)
-            ddft[0][0][i] = ddft[1][0][i] +
-                row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]);
+    black = calloc (height, sizeof(black[0]));
+    for (row=0; row < height; ++row) {
+        unsigned int i;
+        for (i=0; i < 3; ++i) {
+            unsigned int j;
+            for (j = 0; j < 2; ++j)
+                ddft[0][i][j] = ddft[1][i][j] +
+                    row / (height-1.0) * (ddft[2][i][j] - ddft[1][i][j]);
+        }
         FORC3 black[row][c] =
             ( foveon_avg (image[row*width]+c, dscr[0], cfilt) +
               foveon_avg (image[row*width]+c, dscr[1], cfilt) * 3
               - ddft[0][c][0] ) / 4 - ddft[0][c][1];
     }
-    memcpy (black, black+8, sizeof (*black)*8);
-    memcpy (black+height-11, black+height-22, 11*sizeof *black);
+    memcpy (black, black+8, 8 * sizeof(black[0]));
+    memcpy (black+height-11, black+height-22, 11*(sizeof black[0]));
     memcpy (last, black, sizeof last);
 
     for (row=1; row < height-1; row++) {
@@ -522,9 +527,13 @@ foveon_interpolate(float coeff[3][4]) {
         FORC3 black[row][c] += fsum[c]/2 + total[c]/(total[3]*100.0);
 
     for (row=0; row < height; row++) {
-        for (i=0; i < 6; i++)
-            ddft[0][0][i] = ddft[1][0][i] +
-                row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]);
+        unsigned int i;
+        for (i = 0; i < 3; ++i) {
+            unsigned int j;
+            for (j = 0; j < 2; ++j)
+                ddft[0][i][j] = ddft[1][i][j] +
+                    row / (height-1.0) * (ddft[2][i][j] - ddft[1][i][j]);
+        }
         pix = (short*)image[row*width];
         memcpy (prev, pix, sizeof prev);
         frow = row / (height-1.0) * (dim[2]-1);
diff --git a/converter/other/cameratopam/foveon.h b/converter/other/cameratopam/foveon.h
index 57be2244..c9bf48a8 100644
--- a/converter/other/cameratopam/foveon.h
+++ b/converter/other/cameratopam/foveon.h
@@ -1,14 +1,17 @@
 #include "pm.h"
 
+#include "cameratopam.h"
+#include "camera.h"
+
 void 
 parse_foveon(FILE * const ifp);
 
 void  
-foveon_interpolate(float coeff[3][4]);
+foveon_interpolate(Image const image,
+                   float coeff[3][4]);
 
-void 
-foveon_load_raw(void);
+LoadRawFn foveon_load_raw;
 
 void  
-foveon_coeff(bool * const useCoeffP,
-             float        coeff[3][4]);
+foveon_coeff(int * const useCoeffP,
+             float       coeff[3][4]);
diff --git a/converter/other/cameratopam/global_variables.h b/converter/other/cameratopam/global_variables.h
index c8732d5a..2bfc08c9 100644
--- a/converter/other/cameratopam/global_variables.h
+++ b/converter/other/cameratopam/global_variables.h
@@ -23,7 +23,6 @@ extern time_t timestamp;
 extern int is_foveon;
 extern int is_dng;
 extern int is_canon;
-extern unsigned short (*image)[4];
 extern int maximum;
 extern int clip_max;
 extern short order;
diff --git a/converter/other/cameratopam/identify.c b/converter/other/cameratopam/identify.c
index a101c8ad..02208be6 100644
--- a/converter/other/cameratopam/identify.c
+++ b/converter/other/cameratopam/identify.c
@@ -2,6 +2,8 @@
 #include <string.h>
 
 #include "pm.h"
+#include "pm_c_util.h"
+#include "nstring.h"
 
 #include "global_variables.h"
 #include "util.h"
@@ -15,235 +17,250 @@
 
 
 #if HAVE_INT64
-   static bool const have64BitArithmetic = true;
+static bool const have64BitArithmetic = true;
 #else
-   static bool const have64BitArithmetic = false;
+static bool const have64BitArithmetic = false;
 #endif
 
 
-static loadRawFn load_raw;
 
 /* This does the same as the function of the same name in the GNU C library */
 static const char *memmem_internal (const char *haystack, size_t haystacklen,
-                     const char *needle, size_t needlelen)
+                                    const char *needle, size_t needlelen)
 {
-  const char *c;
-  for (c = haystack; c <= haystack + haystacklen - needlelen; c++)
-    if (!memcmp (c, needle, needlelen))
-      return c;
-  return NULL;
+    const char *c;
+    for (c = haystack; c <= haystack + haystacklen - needlelen; c++)
+        if (!memcmp (c, needle, needlelen))
+            return c;
+    return NULL;
 }
 
-/*
-   Thanks to Adobe for providing these excellent CAM -> XYZ matrices!
- */
+
+
 static void 
-adobe_coeff()
-{
-  static const struct {
-    const char *prefix;
-    short trans[12];
-  } table[] = {
-    { "Canon EOS D2000C",
-    { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
-    { "Canon EOS D30",
-    { 9805,-2689,-1312,-5803,13064,3068,-2438,3075,8775 } },
-    { "Canon EOS D60",
-    { 6188,-1341,-890,-7168,14489,2937,-2640,3228,8483 } },
-    { "Canon EOS 10D",
-    { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
-    { "Canon EOS 20D",
-    { 6599,-537,-891,-8071,15783,2424,-1983,2234,7462 } },
-    { "Canon EOS-1Ds Mark II",
-    { 6517,-602,-867,-8180,15926,2378,-1618,1771,7633 } },
-    { "Canon EOS-1D Mark II",
-    { 6264,-582,-724,-8312,15948,2504,-1744,1919,8664 } },
-    { "Canon EOS-1DS",
-    { 4374,3631,-1743,-7520,15212,2472,-2892,3632,8161 } },
-    { "Canon EOS-1D",
-    { 6906,-278,-1017,-6649,15074,1621,-2848,3897,7611 } },
-    { "Canon EOS",
-    { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
-    { "Canon PowerShot 600",
-    { -3822,10019,1311,4085,-157,3386,-5341,10829,4812,-1969,10969,1126 } },
-    { "Canon PowerShot A50",
-    { -5300,9846,1776,3436,684,3939,-5540,9879,6200,-1404,11175,217 } },
-    { "Canon PowerShot A5",
-    { -4801,9475,1952,2926,1611,4094,-5259,10164,5947,-1554,10883,547 } },
-    { "Canon PowerShot G1",
-    { -4778,9467,2172,4743,-1141,4344,-5146,9908,6077,-1566,11051,557 } },
-    { "Canon PowerShot G2",
-    { 9087,-2693,-1049,-6715,14382,2537,-2291,2819,7790 } },
-    { "Canon PowerShot G3",
-    { 9212,-2781,-1073,-6573,14189,2605,-2300,2844,7664 } },
-    { "Canon PowerShot G5",
-    { 9757,-2872,-933,-5972,13861,2301,-1622,2328,7212 } },
-    { "Canon PowerShot G6",
-    { 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } },
-    { "Canon PowerShot Pro1",
-    { 10062,-3522,-999,-7643,15117,2730,-765,817,7323 } },
-    { "Canon PowerShot Pro70",
-    { -4155,9818,1529,3939,-25,4522,-5521,9870,6610,-2238,10873,1342 } },
-    { "Canon PowerShot Pro90",
-    { -4963,9896,2235,4642,-987,4294,-5162,10011,5859,-1770,11230,577 } },
-    { "Canon PowerShot S30",
-    { 10566,-3652,-1129,-6552,14662,2006,-2197,2581,7670 } },
-    { "Canon PowerShot S40",
-    { 8510,-2487,-940,-6869,14231,2900,-2318,2829,9013 } },
-    { "Canon PowerShot S45",
-    { 8163,-2333,-955,-6682,14174,2751,-2077,2597,8041 } },
-    { "Canon PowerShot S50",
-    { 8882,-2571,-863,-6348,14234,2288,-1516,2172,6569 } },
-    { "Canon PowerShot S70",
-    { 9976,-3810,-832,-7115,14463,2906,-901,989,7889 } },
-    { "Contax N Digital",
-    { 7777,1285,-1053,-9280,16543,2916,-3677,5679,7060 } },
-    { "EPSON R-D1",
-    { 6827,-1878,-732,-8429,16012,2564,-704,592,7145 } },
-    { "FUJIFILM FinePix E550",
-    { 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } },
-    { "FUJIFILM FinePix F700",
-    { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
-    { "FUJIFILM FinePix S20Pro",
-    { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
-    { "FUJIFILM FinePix S2Pro",
-    { 12492,-4690,-1402,-7033,15423,1647,-1507,2111,7697 } },
-    { "FUJIFILM FinePix S3Pro",
-    { 11807,-4612,-1294,-8927,16968,1988,-2120,2741,8006 } },
-    { "FUJIFILM FinePix S5000",
-    { 8754,-2732,-1019,-7204,15069,2276,-1702,2334,6982 } },
-    { "FUJIFILM FinePix S5100",
-    { 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } },
-    { "FUJIFILM FinePix S7000",
-    { 10190,-3506,-1312,-7153,15051,2238,-2003,2399,7505 } },
-    { "Kodak DCS315C",
-    { 17523,-4827,-2510,756,8546,-137,6113,1649,2250 } },
-    { "Kodak DCS330C",
-    { 20620,-7572,-2801,-103,10073,-396,3551,-233,2220 } },
-    { "KODAK DCS420",
-    { 10868,-1852,-644,-1537,11083,484,2343,628,2216 } },
-    { "KODAK DCS460",
-    { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
-    { "KODAK EOSDCS1",
-    { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
-    { "KODAK EOSDCS3B",
-    { 9898,-2700,-940,-2478,12219,206,1985,634,1031 } },
-    { "Kodak DCS520C",
-    { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
-    { "Kodak DCS560C",
-    { 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } },
-    { "Kodak DCS620C",
-    { 23617,-10175,-3149,-2054,11749,-272,2586,-489,3453 } },
-    { "Kodak DCS620X",
-    { 13095,-6231,154,12221,-21,-2137,895,4602,2258 } },
-    { "Kodak DCS660C",
-    { 18244,-6351,-2739,-791,11193,-521,3711,-129,2802 } },
-    { "Kodak DCS720X",
-    { 11775,-5884,950,9556,1846,-1286,-1019,6221,2728 } },
-    { "Kodak DCS760C",
-    { 16623,-6309,-1411,-4344,13923,323,2285,274,2926 } },
-    { "Kodak DCS Pro SLR",
-    { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
-    { "Kodak DCS Pro 14nx",
-    { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
-    { "Kodak DCS Pro 14",
-    { 7791,3128,-776,-8588,16458,2039,-2455,4006,6198 } },
-    { "Kodak ProBack645",
-    { 16414,-6060,-1470,-3555,13037,473,2545,122,4948 } },
-    { "Kodak ProBack",
-    { 21179,-8316,-2918,-915,11019,-165,3477,-180,4210 } },
-    { "LEICA DIGILUX 2",
-    { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } },
-    { "Leaf Valeo",
-    { 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 } },
-    { "Minolta DiMAGE 5",
-    { 8983,-2942,-963,-6556,14476,2237,-2426,2887,8014 } },
-    { "Minolta DiMAGE 7",
-    { 9144,-2777,-998,-6676,14556,2281,-2470,3019,7744 } },
-    { "Minolta DiMAGE A1",
-    { 9274,-2547,-1167,-8220,16323,1943,-2273,2720,8340 } },
-    { "MINOLTA DiMAGE A200",
-    { 8560,-2487,-986,-8112,15535,2771,-1209,1324,7743 } },
-    { "Minolta DiMAGE A2",
-    { 9097,-2726,-1053,-8073,15506,2762,-966,981,7763 } },
-    { "MINOLTA DYNAX 7D",
-    { 10239,-3104,-1099,-8037,15727,2451,-927,925,6871 } },
-    { "NIKON D100",
-    { 5915,-949,-778,-7516,15364,2282,-1228,1337,6404 } },
-    { "NIKON D1H",
-    { 7577,-2166,-926,-7454,15592,1934,-2377,2808,8606 } },
-    { "NIKON D1X",
-    { 7620,-2173,-966,-7604,15843,1805,-2356,2811,8439 } },
-    { "NIKON D1",
-    { 7559,-2130,-965,-7611,15713,1972,-2478,3042,8290 } },
-    { "NIKON D2H",
-    { 5710,-901,-615,-8594,16617,2024,-2975,4120,6830 } },
-    { "NIKON D70",
-    { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } },
-    { "NIKON E995", /* copied from E5000 */
-    { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
-    { "NIKON E2500",
-    { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
-    { "NIKON E4500",
-    { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
-    { "NIKON E5000",
-    { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
-    { "NIKON E5400",
-    { 9349,-2987,-1001,-7919,15766,2266,-2098,2680,6839 } },
-    { "NIKON E5700",
-    { -5368,11478,2368,5537,-113,3148,-4969,10021,5782,778,9028,211 } },
-    { "NIKON E8400",
-    { 7842,-2320,-992,-8154,15718,2599,-1098,1342,7560 } },
-    { "NIKON E8700",
-    { 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } },
-    { "NIKON E8800",
-    { 7971,-2314,-913,-8451,15762,2894,-1442,1520,7610 } },
-    { "OLYMPUS C5050",
-    { 10508,-3124,-1273,-6079,14294,1901,-1653,2306,6237 } },
-    { "OLYMPUS C5060",
-    { 10445,-3362,-1307,-7662,15690,2058,-1135,1176,7602 } },
-    { "OLYMPUS C70",
-    { 10793,-3791,-1146,-7498,15177,2488,-1390,1577,7321 } },
-    { "OLYMPUS C80",
-    { 8606,-2509,-1014,-8238,15714,2703,-942,979,7760 } },
-    { "OLYMPUS E-10",
-    { 12745,-4500,-1416,-6062,14542,1580,-1934,2256,6603 } },
-    { "OLYMPUS E-1",
-    { 11846,-4767,-945,-7027,15878,1089,-2699,4122,8311 } },
-    { "OLYMPUS E-20",
-    { 13173,-4732,-1499,-5807,14036,1895,-2045,2452,7142 } },
-    { "OLYMPUS E-300",
-    { 7828,-1761,-348,-5788,14071,1830,-2853,4518,6557 } },
-    { "PENTAX *ist D",
-    { 9651,-2059,-1189,-8881,16512,2487,-1460,1345,10687 } },
-    { "Panasonic DMC-LC1",
-    { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } },
-    { "SONY DSC-F828",
-    { 7924,-1910,-777,-8226,15459,2998,-1517,2199,6818,-7242,11401,3481 } },
-    { "SONY DSC-V3",
-    { 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } }
-  };
-  double cc[4][4], cm[4][3], xyz[] = { 1,1,1 };
-  char name[130];
-  int i, j;
+adobeCoeff(const char * const make,
+           const char * const model) {
+    /*
+      Thanks to Adobe for providing these excellent CAM -> XYZ matrices!
+    */
+    struct CoeffTableEntry {
+        const char * prefix;
+        short trans[12];
+    }; 
+
+    static struct CoeffTableEntry const table[] = {
+        { "Canon EOS D2000C",
+          { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
+        { "Canon EOS D30",
+          { 9805,-2689,-1312,-5803,13064,3068,-2438,3075,8775 } },
+        { "Canon EOS D60",
+          { 6188,-1341,-890,-7168,14489,2937,-2640,3228,8483 } },
+        { "Canon EOS 10D",
+          { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
+        { "Canon EOS 20D",
+          { 6599,-537,-891,-8071,15783,2424,-1983,2234,7462 } },
+        { "Canon EOS-1Ds Mark II",
+          { 6517,-602,-867,-8180,15926,2378,-1618,1771,7633 } },
+        { "Canon EOS-1D Mark II",
+          { 6264,-582,-724,-8312,15948,2504,-1744,1919,8664 } },
+        { "Canon EOS-1DS",
+          { 4374,3631,-1743,-7520,15212,2472,-2892,3632,8161 } },
+        { "Canon EOS-1D",
+          { 6906,-278,-1017,-6649,15074,1621,-2848,3897,7611 } },
+        { "Canon EOS",
+          { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } },
+        { "Canon PowerShot 600",
+          { -3822,10019,1311,4085,-157,3386,-5341,10829,4812,-1969,10969,
+            1126} },
+        { "Canon PowerShot A50",
+          { -5300,9846,1776,3436,684,3939,-5540,9879,6200,-1404,11175,217 } },
+        { "Canon PowerShot A5",
+          { -4801,9475,1952,2926,1611,4094,-5259,10164,5947,-1554,10883,547} },
+        { "Canon PowerShot G1",
+          { -4778,9467,2172,4743,-1141,4344,-5146,9908,6077,-1566,11051,557} },
+        { "Canon PowerShot G2",
+          { 9087,-2693,-1049,-6715,14382,2537,-2291,2819,7790 } },
+        { "Canon PowerShot G3",
+          { 9212,-2781,-1073,-6573,14189,2605,-2300,2844,7664 } },
+        { "Canon PowerShot G5",
+          { 9757,-2872,-933,-5972,13861,2301,-1622,2328,7212 } },
+        { "Canon PowerShot G6",
+          { 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } },
+        { "Canon PowerShot Pro1",
+          { 10062,-3522,-999,-7643,15117,2730,-765,817,7323 } },
+        { "Canon PowerShot Pro70",
+          { -4155,9818,1529,3939,-25,4522,-5521,9870,6610,-2238,10873,1342 } },
+        { "Canon PowerShot Pro90",
+          { -4963,9896,2235,4642,-987,4294,-5162,10011,5859,-1770,11230,577} },
+        { "Canon PowerShot S30",
+          { 10566,-3652,-1129,-6552,14662,2006,-2197,2581,7670 } },
+        { "Canon PowerShot S40",
+          { 8510,-2487,-940,-6869,14231,2900,-2318,2829,9013 } },
+        { "Canon PowerShot S45",
+          { 8163,-2333,-955,-6682,14174,2751,-2077,2597,8041 } },
+        { "Canon PowerShot S50",
+          { 8882,-2571,-863,-6348,14234,2288,-1516,2172,6569 } },
+        { "Canon PowerShot S70",
+          { 9976,-3810,-832,-7115,14463,2906,-901,989,7889 } },
+        { "Contax N Digital",
+          { 7777,1285,-1053,-9280,16543,2916,-3677,5679,7060 } },
+        { "EPSON R-D1",
+          { 6827,-1878,-732,-8429,16012,2564,-704,592,7145 } },
+        { "FUJIFILM FinePix E550",
+          { 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } },
+        { "FUJIFILM FinePix F700",
+          { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
+        { "FUJIFILM FinePix S20Pro",
+          { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } },
+        { "FUJIFILM FinePix S2Pro",
+          { 12492,-4690,-1402,-7033,15423,1647,-1507,2111,7697 } },
+        { "FUJIFILM FinePix S3Pro",
+          { 11807,-4612,-1294,-8927,16968,1988,-2120,2741,8006 } },
+        { "FUJIFILM FinePix S5000",
+          { 8754,-2732,-1019,-7204,15069,2276,-1702,2334,6982 } },
+        { "FUJIFILM FinePix S5100",
+          { 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } },
+        { "FUJIFILM FinePix S7000",
+          { 10190,-3506,-1312,-7153,15051,2238,-2003,2399,7505 } },
+        { "Kodak DCS315C",
+          { 17523,-4827,-2510,756,8546,-137,6113,1649,2250 } },
+        { "Kodak DCS330C",
+          { 20620,-7572,-2801,-103,10073,-396,3551,-233,2220 } },
+        { "KODAK DCS420",
+          { 10868,-1852,-644,-1537,11083,484,2343,628,2216 } },
+        { "KODAK DCS460",
+          { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
+        { "KODAK EOSDCS1",
+          { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } },
+        { "KODAK EOSDCS3B",
+          { 9898,-2700,-940,-2478,12219,206,1985,634,1031 } },
+        { "Kodak DCS520C",
+          { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } },
+        { "Kodak DCS560C",
+          { 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } },
+        { "Kodak DCS620C",
+          { 23617,-10175,-3149,-2054,11749,-272,2586,-489,3453 } },
+        { "Kodak DCS620X",
+          { 13095,-6231,154,12221,-21,-2137,895,4602,2258 } },
+        { "Kodak DCS660C",
+          { 18244,-6351,-2739,-791,11193,-521,3711,-129,2802 } },
+        { "Kodak DCS720X",
+          { 11775,-5884,950,9556,1846,-1286,-1019,6221,2728 } },
+        { "Kodak DCS760C",
+          { 16623,-6309,-1411,-4344,13923,323,2285,274,2926 } },
+        { "Kodak DCS Pro SLR",
+          { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
+        { "Kodak DCS Pro 14nx",
+          { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } },
+        { "Kodak DCS Pro 14",
+          { 7791,3128,-776,-8588,16458,2039,-2455,4006,6198 } },
+        { "Kodak ProBack645",
+          { 16414,-6060,-1470,-3555,13037,473,2545,122,4948 } },
+        { "Kodak ProBack",
+          { 21179,-8316,-2918,-915,11019,-165,3477,-180,4210 } },
+        { "LEICA DIGILUX 2",
+          { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } },
+        { "Leaf Valeo",
+          { 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 } },
+        { "Minolta DiMAGE 5",
+          { 8983,-2942,-963,-6556,14476,2237,-2426,2887,8014 } },
+        { "Minolta DiMAGE 7",
+          { 9144,-2777,-998,-6676,14556,2281,-2470,3019,7744 } },
+        { "Minolta DiMAGE A1",
+          { 9274,-2547,-1167,-8220,16323,1943,-2273,2720,8340 } },
+        { "MINOLTA DiMAGE A200",
+          { 8560,-2487,-986,-8112,15535,2771,-1209,1324,7743 } },
+        { "Minolta DiMAGE A2",
+          { 9097,-2726,-1053,-8073,15506,2762,-966,981,7763 } },
+        { "MINOLTA DYNAX 7D",
+          { 10239,-3104,-1099,-8037,15727,2451,-927,925,6871 } },
+        { "NIKON D100",
+          { 5915,-949,-778,-7516,15364,2282,-1228,1337,6404 } },
+        { "NIKON D1H",
+          { 7577,-2166,-926,-7454,15592,1934,-2377,2808,8606 } },
+        { "NIKON D1X",
+          { 7620,-2173,-966,-7604,15843,1805,-2356,2811,8439 } },
+        { "NIKON D1",
+          { 7559,-2130,-965,-7611,15713,1972,-2478,3042,8290 } },
+        { "NIKON D2H",
+          { 5710,-901,-615,-8594,16617,2024,-2975,4120,6830 } },
+        { "NIKON D70",
+          { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } },
+        { "NIKON E995", /* copied from E5000 */
+          { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+        { "NIKON E2500",
+          { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+        { "NIKON E4500",
+          { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+        { "NIKON E5000",
+          { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } },
+        { "NIKON E5400",
+          { 9349,-2987,-1001,-7919,15766,2266,-2098,2680,6839 } },
+        { "NIKON E5700",
+          { -5368,11478,2368,5537,-113,3148,-4969,10021,5782,778,9028,211 } },
+        { "NIKON E8400",
+          { 7842,-2320,-992,-8154,15718,2599,-1098,1342,7560 } },
+        { "NIKON E8700",
+          { 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } },
+        { "NIKON E8800",
+          { 7971,-2314,-913,-8451,15762,2894,-1442,1520,7610 } },
+        { "OLYMPUS C5050",
+          { 10508,-3124,-1273,-6079,14294,1901,-1653,2306,6237 } },
+        { "OLYMPUS C5060",
+          { 10445,-3362,-1307,-7662,15690,2058,-1135,1176,7602 } },
+        { "OLYMPUS C70",
+          { 10793,-3791,-1146,-7498,15177,2488,-1390,1577,7321 } },
+        { "OLYMPUS C80",
+          { 8606,-2509,-1014,-8238,15714,2703,-942,979,7760 } },
+        { "OLYMPUS E-10",
+          { 12745,-4500,-1416,-6062,14542,1580,-1934,2256,6603 } },
+        { "OLYMPUS E-1",
+          { 11846,-4767,-945,-7027,15878,1089,-2699,4122,8311 } },
+        { "OLYMPUS E-20",
+          { 13173,-4732,-1499,-5807,14036,1895,-2045,2452,7142 } },
+        { "OLYMPUS E-300",
+          { 7828,-1761,-348,-5788,14071,1830,-2853,4518,6557 } },
+        { "PENTAX *ist D",
+          { 9651,-2059,-1189,-8881,16512,2487,-1460,1345,10687 } },
+        { "Panasonic DMC-LC1",
+          { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } },
+        { "SONY DSC-F828",
+          { 7924,-1910,-777,-8226,15459,2998,-1517,2199,6818,-7242,11401,
+            3481} },
+        { "SONY DSC-V3",
+          { 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } }
+    };
+    double cc[4][4];
+    double cm[4][3];
+    double xyz[] = { 1,1,1 };
+    char name[130];
+    unsigned int i;
 
-  for (i=0; i < 4; i++)
-    for (j=0; j < 4; j++)
-      cc[i][j] = i == j;
-  sprintf (name, "%s %s", make, model);
-  for (i=0; i < sizeof table / sizeof *table; i++)
-    if (!strncmp (name, table[i].prefix, strlen(table[i].prefix))) {
-      for (j=0; j < 12; j++)
-    cm[0][j] = table[i].trans[j];
-      dng_coeff (cc, cm, xyz);
-      break;
+    /* Make an identity matrix (1's on the diagonal) */
+    for (i = 0; i < 4; ++i) {
+        unsigned int j;
+        for (j = 0; j < 4; ++j)
+            cc[i][j] = (i == j);
+    }
+    sprintf (name, "%s %s", make, model);
+
+    for (i = 0; i < ARRAY_SIZE(table); ++i) {
+        const struct CoeffTableEntry * const entryP = &table[i];
+
+        if (strneq(name, entryP->prefix, strlen(entryP->prefix))) {
+            unsigned int j;
+            for (j = 0; j < 12; ++j)
+                cm[j/3][j%3] = entryP->trans[j];
+            dng_coeff(cc, cm, xyz);
+
+            break;
+        }
     }
 }
 
-/*
-   Identify which camera created this file, and set global variables
-   accordingly.  Return nonzero if the file cannot be decoded.
- */
+
+
+
 int
 identify(FILE *       const ifp,
          bool         const use_secondary,
@@ -252,932 +269,937 @@ identify(FILE *       const ifp,
          float        const blue_scale,
          unsigned int const four_color_rgb,
          const char * const inputFileName,
-         loadRawFn *  const loadRawFnP)
-{
-  char head[32];
-  char * c;
-  unsigned hlen, fsize, i;
-  static const struct {
-    int fsize;
-    char make[12], model[15], withjpeg;
-  } table[] = {
-    {    62464, "Kodak",    "DC20"       ,0 },
-    {   124928, "Kodak",    "DC20"       ,0 },
-    {   311696, "ST Micro", "STV680 VGA" ,0 },  /* SPYz */
-    {   787456, "Creative", "PC-CAM 600" ,0 },
-    {  2465792, "NIKON",    "E950"       ,1 },  /* or E800,E700 */
-    {  2940928, "NIKON",    "E2100"      ,1 },  /* or E2500 */
-    {  4771840, "NIKON",    "E990"       ,1 },  /* or E995 */
-    {  4775936, "NIKON",    "E3700"      ,1 },  /* or Optio 33WR */
-    {  5869568, "NIKON",    "E4300"      ,1 },  /* or DiMAGE Z2 */
-    {  5865472, "NIKON",    "E4500"      ,0 },
-    {  1976352, "CASIO",    "QV-2000UX"  ,0 },
-    {  3217760, "CASIO",    "QV-3*00EX"  ,0 },
-    {  6218368, "CASIO",    "QV-5700"    ,0 },
-    {  7530816, "CASIO",    "QV-R51"     ,1 },
-    {  7684000, "CASIO",    "QV-4000"    ,0 },
-    {  7753344, "CASIO",    "EX-Z55"     ,1 },
-    {  9313536, "CASIO",    "EX-P600"    ,1 },
-    { 10979200, "CASIO",    "EX-P700"    ,1 },
-    {  3178560, "PENTAX",   "Optio S"    ,1 },  /*  8-bit */
-    {  4841984, "PENTAX",   "Optio S"    ,1 },  /* 12-bit */
-    {  6114240, "PENTAX",   "Optio S4"   ,1 },  /* or S4i */
-    { 12582980, "Sinar",    ""           ,0 } };
-  static const char *corp[] =
-    { "Canon", "NIKON", "EPSON", "Kodak", "OLYMPUS", "PENTAX",
-      "MINOLTA", "Minolta", "Konica", "CASIO" };
-  float tmp;
-
-  /*  What format is this file?  Set make[] if we recognize it. */
+         LoadRawFn ** const loadRawFnP) {
+/*----------------------------------------------------------------------------
+  Identify which camera created this file, and set global variables
+  accordingly.  Return nonzero if the file cannot be decoded.
+-----------------------------------------------------------------------------*/
+    char head[32];
+    char * c;
+    unsigned hlen, fsize, i;
+    static const struct {
+        int fsize;
+        char make[12], model[15], withjpeg;
+    } table[] = {
+        {    62464, "Kodak",    "DC20"       ,0 },
+        {   124928, "Kodak",    "DC20"       ,0 },
+        {   311696, "ST Micro", "STV680 VGA" ,0 },  /* SPYz */
+        {   787456, "Creative", "PC-CAM 600" ,0 },
+        {  2465792, "NIKON",    "E950"       ,1 },  /* or E800,E700 */
+        {  2940928, "NIKON",    "E2100"      ,1 },  /* or E2500 */
+        {  4771840, "NIKON",    "E990"       ,1 },  /* or E995 */
+        {  4775936, "NIKON",    "E3700"      ,1 },  /* or Optio 33WR */
+        {  5869568, "NIKON",    "E4300"      ,1 },  /* or DiMAGE Z2 */
+        {  5865472, "NIKON",    "E4500"      ,0 },
+        {  1976352, "CASIO",    "QV-2000UX"  ,0 },
+        {  3217760, "CASIO",    "QV-3*00EX"  ,0 },
+        {  6218368, "CASIO",    "QV-5700"    ,0 },
+        {  7530816, "CASIO",    "QV-R51"     ,1 },
+        {  7684000, "CASIO",    "QV-4000"    ,0 },
+        {  7753344, "CASIO",    "EX-Z55"     ,1 },
+        {  9313536, "CASIO",    "EX-P600"    ,1 },
+        { 10979200, "CASIO",    "EX-P700"    ,1 },
+        {  3178560, "PENTAX",   "Optio S"    ,1 },  /*  8-bit */
+        {  4841984, "PENTAX",   "Optio S"    ,1 },  /* 12-bit */
+        {  6114240, "PENTAX",   "Optio S4"   ,1 },  /* or S4i */
+        { 12582980, "Sinar",    ""           ,0 } };
+    static const char *corp[] =
+        { "Canon", "NIKON", "EPSON", "Kodak", "OLYMPUS", "PENTAX",
+          "MINOLTA", "Minolta", "Konica", "CASIO" };
+    float tmp;
+    LoadRawFn * load_raw;
 
-  raw_height = raw_width = fuji_width = flip = 0;
-  make[0] = model[0] = model2[0] = 0;
-  memset (white, 0, sizeof white);
-  timestamp = tiff_samples = 0;
-  data_offset = meta_length = tiff_data_compression = 0;
-  zero_after_ff = is_dng = fuji_secondary = filters = 0;
-  black = is_foveon = use_coeff = 0;
-  use_gamma = xmag = ymag = 1;
-  for (i=0; i < 4; i++) {
-    cam_mul[i] = 1 & i;
-    pre_mul[i] = 1;
-  }
-  colors = 3;
-  for (i=0; i < 0x1000; i++) curve[i] = i;
-  maximum = 0xfff;
-#ifdef USE_LCMS
-  profile_length = 0;
-#endif
+    /*  What format is this file?  Set make[] if we recognize it. */
 
-  order = get2(ifp);
-  hlen = get4(ifp);
-  fseek (ifp, 0, SEEK_SET);
-  fread (head, 1, 32, ifp);
-  fseek (ifp, 0, SEEK_END);
-  fsize = ftell(ifp);
-  if ((c = (char*)memmem_internal(head, 32, "MMMMRawT", 8))) {
-    strcpy (make, "Phase One");
-    data_offset = c - head;
-    fseek (ifp, data_offset + 8, SEEK_SET);
-    fseek (ifp, get4(ifp) + 136, SEEK_CUR);
-    raw_width = get4(ifp);
-    fseek (ifp, 12, SEEK_CUR);
-    raw_height = get4(ifp);
-  } else if (order == 0x4949 || order == 0x4d4d) {
-    if (!memcmp (head+6, "HEAPCCDR", 8)) {
-      data_offset = hlen;
-      parse_ciff(ifp, hlen, fsize - hlen);
-    } else {
-      parse_tiff(ifp, 0);
-      if (!strncmp(make,"NIKON",5) && filters == 0)
-    make[0] = 0;
+    raw_height = raw_width = fuji_width = flip = 0;
+    make[0] = model[0] = model2[0] = 0;
+    memset (white, 0, sizeof white);
+    timestamp = tiff_samples = 0;
+    data_offset = meta_length = tiff_data_compression = 0;
+    zero_after_ff = is_dng = fuji_secondary = filters = 0;
+    black = is_foveon = use_coeff = 0;
+    use_gamma = xmag = ymag = 1;
+    for (i=0; i < 4; i++) {
+        cam_mul[i] = 1 & i;
+        pre_mul[i] = 1;
     }
-  } else if (!memcmp (head, "\0MRM", 4))
-    parse_minolta(ifp);
+    colors = 3;
+    for (i=0; i < 0x1000; i++) curve[i] = i;
+    maximum = 0xfff;
+
+    order = get2(ifp);
+    hlen = get4(ifp);
+    fseek (ifp, 0, SEEK_SET);
+    fread (head, 1, 32, ifp);
+    fseek (ifp, 0, SEEK_END);
+    fsize = ftell(ifp);
+    if ((c = (char*)memmem_internal(head, 32, "MMMMRawT", 8))) {
+        strcpy (make, "Phase One");
+        data_offset = c - head;
+        fseek (ifp, data_offset + 8, SEEK_SET);
+        fseek (ifp, get4(ifp) + 136, SEEK_CUR);
+        raw_width = get4(ifp);
+        fseek (ifp, 12, SEEK_CUR);
+        raw_height = get4(ifp);
+    } else if (order == 0x4949 || order == 0x4d4d) {
+        if (!memcmp (head+6, "HEAPCCDR", 8)) {
+            data_offset = hlen;
+            parse_ciff(ifp, hlen, fsize - hlen);
+        } else {
+            parse_tiff(ifp, 0);
+            if (!strncmp(make,"NIKON",5) && filters == 0)
+                make[0] = 0;
+        }
+    } else if (!memcmp (head, "\0MRM", 4))
+        parse_minolta(ifp);
     else if (!memcmp (head, "\xff\xd8\xff\xe1", 4) &&
-         !memcmp (head+6, "Exif", 4)) {
-    fseek (ifp, 4, SEEK_SET);
-    fseek (ifp, 4 + get2(ifp), SEEK_SET);
-    if (fgetc(ifp) != 0xff)
-      parse_tiff(ifp, 12);
-  } else if (!memcmp (head, "BM", 2)) {
-    data_offset = 0x1000;
-    order = 0x4949;
-    fseek (ifp, 38, SEEK_SET);
-    if (get4(ifp) == 2834 && get4(ifp) == 2834) {
-      strcpy (model, "BMQ");
-      flip = 3;
-      goto nucore;
-    }
-  } else if (!memcmp (head, "BR", 2)) {
-    strcpy (model, "RAW");
-nucore:
-    strcpy (make, "Nucore");
-    order = 0x4949;
-    fseek (ifp, 10, SEEK_SET);
-    data_offset += get4(ifp);
-    get4(ifp);
-    raw_width  = get4(ifp);
-    raw_height = get4(ifp);
-    if (model[0] == 'B' && raw_width == 2597) {
-      raw_width++;
-      data_offset -= 0x1000;
-    }
-  } else if (!memcmp (head+25, "ARECOYK", 7)) {
-    strcpy (make, "Contax");
-    strcpy (model,"N Digital");
-    fseek (ifp, 60, SEEK_SET);
-    camera_red  = get4(ifp);
-    camera_red /= get4(ifp);
-    camera_blue = get4(ifp);
-    camera_blue = get4(ifp) / camera_blue;
-  } else if (!memcmp (head, "FUJIFILM", 8)) {
-    long data_offset_long;
-    fseek (ifp, 84, SEEK_SET);
-    parse_tiff(ifp, get4(ifp)+12);
-    fseek (ifp, 100, SEEK_SET);
-    pm_readbiglong(ifp, &data_offset_long);
-    data_offset = data_offset_long;
-  } else if (!memcmp (head, "DSC-Image", 9))
-    parse_rollei(ifp);
-  else if (!memcmp (head, "FOVb", 4))
-    parse_foveon(ifp);
-  else
-      for (i=0; i < sizeof table / sizeof *table; i++)
-          if (fsize == table[i].fsize) {
-              strcpy (make,  table[i].make );
-              strcpy (model, table[i].model);
-              if (table[i].withjpeg)
-                  parse_external_jpeg(inputFileName);
-          }
-  parse_mos(ifp, 8);
-  parse_mos(ifp, 3472);
+             !memcmp (head+6, "Exif", 4)) {
+        fseek (ifp, 4, SEEK_SET);
+        fseek (ifp, 4 + get2(ifp), SEEK_SET);
+        if (fgetc(ifp) != 0xff)
+            parse_tiff(ifp, 12);
+    } else if (!memcmp (head, "BM", 2)) {
+        data_offset = 0x1000;
+        order = 0x4949;
+        fseek (ifp, 38, SEEK_SET);
+        if (get4(ifp) == 2834 && get4(ifp) == 2834) {
+            strcpy (model, "BMQ");
+            flip = 3;
+            goto nucore;
+        }
+    } else if (!memcmp (head, "BR", 2)) {
+        strcpy (model, "RAW");
+    nucore:
+        strcpy (make, "Nucore");
+        order = 0x4949;
+        fseek (ifp, 10, SEEK_SET);
+        data_offset += get4(ifp);
+        get4(ifp);
+        raw_width  = get4(ifp);
+        raw_height = get4(ifp);
+        if (model[0] == 'B' && raw_width == 2597) {
+            raw_width++;
+            data_offset -= 0x1000;
+        }
+    } else if (!memcmp (head+25, "ARECOYK", 7)) {
+        strcpy (make, "Contax");
+        strcpy (model,"N Digital");
+        fseek (ifp, 60, SEEK_SET);
+        camera_red  = get4(ifp);
+        camera_red /= get4(ifp);
+        camera_blue = get4(ifp);
+        camera_blue = get4(ifp) / camera_blue;
+    } else if (!memcmp (head, "FUJIFILM", 8)) {
+        long data_offset_long;
+        fseek (ifp, 84, SEEK_SET);
+        parse_tiff(ifp, get4(ifp)+12);
+        fseek (ifp, 100, SEEK_SET);
+        pm_readbiglong(ifp, &data_offset_long);
+        data_offset = data_offset_long;
+    } else if (!memcmp (head, "DSC-Image", 9))
+        parse_rollei(ifp);
+    else if (!memcmp (head, "FOVb", 4))
+        parse_foveon(ifp);
+    else
+        for (i=0; i < sizeof table / sizeof *table; i++)
+            if (fsize == table[i].fsize) {
+                strcpy (make,  table[i].make );
+                strcpy (model, table[i].model);
+                if (table[i].withjpeg)
+                    parse_external_jpeg(inputFileName);
+            }
+    parse_mos(ifp, 8);
+    parse_mos(ifp, 3472);
+
+    for (i=0; i < sizeof corp / sizeof *corp; i++)
+        if (strstr (make, corp[i]))     /* Simplify company names */
+            strcpy (make, corp[i]);
+    if (!strncmp (make, "KODAK", 5))
+        make[16] = model[16] = 0;
+    c = make + strlen(make);      /* Remove trailing spaces */
+    while (*--c == ' ') *c = 0;
+    c = model + strlen(model);
+    while (*--c == ' ') *c = 0;
+    i = strlen(make);         /* Remove make from model */
+    if (!strncmp (model, make, i++))
+        memmove (model, model+i, 64-i);
+    make[63] = model[63] = model2[63] = 0;
 
-  for (i=0; i < sizeof corp / sizeof *corp; i++)
-    if (strstr (make, corp[i]))     /* Simplify company names */
-    strcpy (make, corp[i]);
-  if (!strncmp (make, "KODAK", 5))
-    make[16] = model[16] = 0;
-  c = make + strlen(make);      /* Remove trailing spaces */
-  while (*--c == ' ') *c = 0;
-  c = model + strlen(model);
-  while (*--c == ' ') *c = 0;
-  i = strlen(make);         /* Remove make from model */
-  if (!strncmp (model, make, i++))
-    memmove (model, model+i, 64-i);
-  make[63] = model[63] = model2[63] = 0;
+    if (verbose)
+        fprintf(stderr, "Make = '%s', Model = '%s'\n", make, model);
 
-  if (make[0] == 0) {
-    pm_message ("unrecognized file format.");
-    return 1;
-  }
+    if (make[0] == 0) {
+        pm_message ("unrecognized file format.");
+        return 1;
+    }
 
 /*  File format is OK.  Do we know this camera? */
 /*  Start with some useful defaults:           */
 
-  top_margin = left_margin = 0;
-  if ((raw_height | raw_width) < 0)
-       raw_height = raw_width  = 0;
-  height = raw_height;
-  width  = raw_width;
-  if (fuji_width) {
-    width = height + fuji_width;
-    height = width - 1;
-    ymag = 1;
-  }
-  load_raw = NULL;
-  if (is_dng) {
-    strcat (model, " DNG");
-    if (!filters)
-      colors = tiff_samples;
-    if (tiff_data_compression == 1)
-      load_raw = adobe_dng_load_raw_nc;
-    if (tiff_data_compression == 7)
-      load_raw = adobe_dng_load_raw_lj;
-    goto dng_skip;
-  }
+    top_margin = left_margin = 0;
+    if ((raw_height | raw_width) < 0)
+        raw_height = raw_width  = 0;
+    height = raw_height;
+    width  = raw_width;
+    if (fuji_width) {
+        width = height + fuji_width;
+        height = width - 1;
+        ymag = 1;
+    }
+    load_raw = NULL;
+    if (is_dng) {
+        strcat (model, " DNG");
+        if (!filters)
+            colors = tiff_samples;
+        if (tiff_data_compression == 1)
+            load_raw = adobe_dng_load_raw_nc;
+        if (tiff_data_compression == 7)
+            load_raw = adobe_dng_load_raw_lj;
+        goto dng_skip;
+    }
 
-/*  We'll try to decode anything from Canon or Nikon. */
+    /*  We'll try to decode anything from Canon or Nikon. */
 
-  if (!filters) filters = 0x94949494;
-  if ((is_canon = !strcmp(make,"Canon")))
-    load_raw = memcmp (head+6,"HEAPCCDR",8) ?
-        lossless_jpeg_load_raw : canon_compressed_load_raw;
-  if (!strcmp(make,"NIKON"))
-    load_raw = nikon_is_compressed() ?
-    nikon_compressed_load_raw : nikon_load_raw;
+    if (!filters) filters = 0x94949494;
+    if ((is_canon = !strcmp(make,"Canon")))
+        load_raw = memcmp (head+6,"HEAPCCDR",8) ?
+            lossless_jpeg_load_raw : canon_compressed_load_raw;
+    if (!strcmp(make,"NIKON"))
+        load_raw = nikon_is_compressed() ?
+            nikon_compressed_load_raw : nikon_load_raw;
 
-/* Set parameters based on camera name (for non-DNG files). */
+    /* Set parameters based on camera name (for non-DNG files). */
 
-  if (is_foveon) {
-    if (!have64BitArithmetic)
-      pm_error("This program was built without 64 bit arithmetic "
-               "capability and the Foveon format requires it.");
-    if (height*2 < width) ymag = 2;
-    if (width < height) xmag = 2;
-    filters = 0;
-    load_raw = foveon_load_raw;
-    foveon_coeff(&use_coeff, coeff);
-  } else if (!strcmp(model,"PowerShot 600")) {
-    height = 613;
-    width  = 854;
-    colors = 4;
-    filters = 0xe1e4e1e4;
-    load_raw = canon_600_load_raw;
-  } else if (!strcmp(model,"PowerShot A5") ||
-         !strcmp(model,"PowerShot A5 Zoom")) {
-    height = 773;
-    width  = 960;
-    raw_width = 992;
-    colors = 4;
-    filters = 0x1e4e1e4e;
-    load_raw = canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot A50")) {
-    height =  968;
-    width  = 1290;
-    raw_width = 1320;
-    colors = 4;
-    filters = 0x1b4e4b1e;
-    load_raw = canon_a5_load_raw;
-  } else if (!strcmp(model,"PowerShot Pro70")) {
-    height = 1024;
-    width  = 1552;
-    colors = 4;
-    filters = 0x1e4b4e1b;
-    load_raw = canon_a5_load_raw;
-    black = 34;
-  } else if (!strcmp(model,"PowerShot Pro90 IS")) {
-    width  = 1896;
-    colors = 4;
-    filters = 0xb4b4b4b4;
-  } else if (is_canon && raw_width == 2144) {
-    height = 1550;
-    width  = 2088;
-    top_margin  = 8;
-    left_margin = 4;
-    if (!strcmp(model,"PowerShot G1")) {
-      colors = 4;
-      filters = 0xb4b4b4b4;
-    }
-  } else if (is_canon && raw_width == 2224) {
-    height = 1448;
-    width  = 2176;
-    top_margin  = 6;
-    left_margin = 48;
-  } else if (is_canon && raw_width == 2376) {
-    height = 1720;
-    width  = 2312;
-    top_margin  = 6;
-    left_margin = 12;
-  } else if (is_canon && raw_width == 2672) {
-    height = 1960;
-    width  = 2616;
-    top_margin  = 6;
-    left_margin = 12;
-  } else if (is_canon && raw_width == 3152) {
-    height = 2056;
-    width  = 3088;
-    top_margin  = 12;
-    left_margin = 64;
-    maximum = 0xfa0;
-  } else if (is_canon && raw_width == 3160) {
-    height = 2328;
-    width  = 3112;
-    top_margin  = 12;
-    left_margin = 44;
-  } else if (is_canon && raw_width == 3344) {
-    height = 2472;
-    width  = 3288;
-    top_margin  = 6;
-    left_margin = 4;
-  } else if (!strcmp(model,"EOS D2000C")) {
-    filters = 0x61616161;
-    black = curve[200];
-  } else if (!strcmp(model,"EOS-1D")) {
-    raw_height = height = 1662;
-    raw_width  = width  = 2496;
-    data_offset = 288912;
-    filters = 0x61616161;
-  } else if (!strcmp(model,"EOS-1DS")) {
-    raw_height = height = 2718;
-    raw_width  = width  = 4082;
-    data_offset = 289168;
-    filters = 0x61616161;
-  } else if (is_canon && raw_width == 3516) {
-    top_margin  = 14;
-    left_margin = 42;
-    goto canon_cr2;
-  } else if (is_canon && raw_width == 3596) {
-    top_margin  = 12;
-    left_margin = 74;
-    goto canon_cr2;
-  } else if (is_canon && raw_width == 5108) {
-    top_margin  = 13;
-    left_margin = 98;
-    maximum = 0xe80;
-canon_cr2:
-    height = raw_height - top_margin;
-    width  = raw_width - left_margin;
-  } else if (!strcmp(model,"D1")) {
-    camera_red  *= 256/527.0;
-    camera_blue *= 256/317.0;
-  } else if (!strcmp(model,"D1X")) {
-    width  = 4024;
-    ymag = 2;
-  } else if (!strcmp(model,"D100")) {
-    if (tiff_data_compression == 34713 && load_raw == nikon_load_raw)
-      raw_width = (width += 3) + 3;
-  } else if (!strcmp(model,"D2H")) {
-    width  = 2482;
-    left_margin = 6;
-  } else if (!strcmp(model,"D2X")) {
-    width  = 4312;
-    pre_mul[0] = 1.514;
-    pre_mul[2] = 1.727;
-  } else if (fsize == 2465792) {
-    height = 1203;
-    width  = 1616;
-    filters = 0x4b4b4b4b;
-    colors = 4;
-    load_raw = nikon_e950_load_raw;
-    nikon_e950_coeff();
-    pre_mul[0] = 1.18193;
-    pre_mul[2] = 1.16452;
-    pre_mul[3] = 1.17250;
-  } else if (!strcmp(model,"E990")) {
-    if (!timestamp && !nikon_e990()) goto cp_e995;
-    height = 1540;
-    width  = 2064;
-    colors = 4;
-    filters = 0xb4b4b4b4;
-    nikon_e950_coeff();
-    pre_mul[0] = 1.196;
-    pre_mul[1] = 1.246;
-    pre_mul[2] = 1.018;
-  } else if (!strcmp(model,"E995")) {
-cp_e995:
-    strcpy (model, "E995");
-    height = 1540;
-    width  = 2064;
-    colors = 4;
-    filters = 0xe1e1e1e1;
-  } else if (!strcmp(model,"E2100")) {
-    if (!timestamp && !nikon_e2100()) goto cp_e2500;
-    width = 1616;
-    height = 1206;
-    load_raw = nikon_e2100_load_raw;
-    pre_mul[0] = 1.945;
-    pre_mul[2] = 1.040;
-  } else if (!strcmp(model,"E2500")) {
-cp_e2500:
-    strcpy (model, "E2500");
-    width = 1616;
-    height = 1204;
-    colors = 4;
-    filters = 0x4b4b4b4b;
-  } else if (!strcmp(model,"E3700")) {
-    if (!timestamp && pentax_optio33()) goto optio_33wr;
-    height = 1542;
-    width  = 2064;
-    load_raw = nikon_e2100_load_raw;
-    pre_mul[0] = 1.818;
-    pre_mul[2] = 1.618;
-  } else if (!strcmp(model,"Optio 33WR")) {
-optio_33wr:
-    strcpy (make, "PENTAX");
-    strcpy (model,"Optio 33WR");
-    height = 1542;
-    width  = 2064;
-    load_raw = nikon_e2100_load_raw;
-    flip = 1;
-    filters = 0x16161616;
-    pre_mul[0] = 1.331;
-    pre_mul[2] = 1.820;
-  } else if (!strcmp(model,"E4300")) {
-    if (!timestamp && minolta_z2()) goto dimage_z2;
-    height = 1710;
-    width  = 2288;
-    filters = 0x16161616;
-    pre_mul[0] = 508;
-    pre_mul[1] = 256;
-    pre_mul[2] = 322;
-  } else if (!strcmp(model,"DiMAGE Z2")) {
-dimage_z2:
-    strcpy (make, "MINOLTA");
-    strcpy (model,"DiMAGE Z2");
-    height = 1710;
-    width  = 2288;
-    filters = 0x16161616;
-    load_raw = nikon_e2100_load_raw;
-    pre_mul[0] = 508;
-    pre_mul[1] = 256;
-    pre_mul[2] = 450;
-  } else if (!strcmp(model,"E4500")) {
-    height = 1708;
-    width  = 2288;
-    colors = 4;
-    filters = 0xb4b4b4b4;
-  } else if (!strcmp(model,"R-D1")) {
-    tiff_data_compression = 34713;
-    load_raw = nikon_load_raw;
-  } else if (!strcmp(model,"FinePixS2Pro")) {
-    height = 3584;
-    width  = 3583;
-    fuji_width = 2144;
-    filters = 0x61616161;
-    load_raw = fuji_s2_load_raw;
-    black = 128;
-    strcpy (model+7, " S2Pro");
-  } else if (!strcmp(model,"FinePix S3Pro")) {
-    height = 3583;
-    width  = 3584;
-    fuji_width = 2144;
-    if (fsize > 18000000 && use_secondary)
-      data_offset += 4352*2*1444;
-    filters = 0x49494949;
-    load_raw = fuji_s3_load_raw;
-    maximum = 0xffff;
-  } else if (!strcmp(model,"FinePix S5000")) {
-    height = 2499;
-    width  = 2500;
-    fuji_width = 1423;
-    filters = 0x49494949;
-    load_raw = fuji_s5000_load_raw;
-    maximum = 0x3e00;
-  } else if (!strcmp(model,"FinePix S5100") ||
-         !strcmp(model,"FinePix S5500")) {
-    height = 1735;
-    width  = 2304;
-    data_offset += width*10;
-    filters = 0x49494949;
-    load_raw = unpacked_load_raw;
-    maximum = 0xffff;
-  } else if (!strcmp(model,"FinePix E550") ||
-         !strcmp(model,"FinePix F810") ||
-         !strcmp(model,"FinePix S7000")) {
-    height = 3587;
-    width  = 3588;
-    fuji_width = 2047;
-    filters = 0x49494949;
-    load_raw = fuji_s7000_load_raw;
-    maximum = 0x3e00;
-  } else if (!strcmp(model,"FinePix F700") ||
-         !strcmp(model,"FinePix S20Pro")) {
-    height = 2523;
-    width  = 2524;
-    fuji_width = 1440;
-    filters = 0x49494949;
-    load_raw = fuji_f700_load_raw;
-    maximum = 0x3e00;
-  } else if (!strcmp(model,"Digital Camera KD-400Z")) {
-    height = 1712;
-    width  = 2312;
-    raw_width = 2336;
-    data_offset = 4034;
-    fseek (ifp, 2032, SEEK_SET);
-    goto konica_400z;
-  } else if (!strcmp(model,"Digital Camera KD-510Z")) {
-    data_offset = 4032;
-    pre_mul[0] = 1.297;
-    pre_mul[2] = 1.438;
-    fseek (ifp, 2032, SEEK_SET);
-    goto konica_510z;
-  } else if (!strcasecmp(make,"MINOLTA")) {
-    load_raw = unpacked_load_raw;
-    maximum = 0xf7d;
-    if (!strncmp(model,"DiMAGE A",8)) {
-      if (!strcmp(model,"DiMAGE A200")) {
-    filters = 0x49494949;
-    tmp = camera_red;
-    camera_red  = 1 / camera_blue;
-    camera_blue = 1 / tmp;
-      }
-      load_raw = packed_12_load_raw;
-      maximum = model[8] == '1' ? 0xf8b : 0xfff;
-    } else if (!strncmp(model,"ALPHA",5) ||
-           !strncmp(model,"DYNAX",5) ||
-           !strncmp(model,"MAXXUM",6)) {
-      load_raw = packed_12_load_raw;
-      maximum = 0xffb;
-    } else if (!strncmp(model,"DiMAGE G",8)) {
-      if (model[8] == '4') {
-    data_offset = 5056;
-    pre_mul[0] = 1.602;
-    pre_mul[2] = 1.441;
-    fseek (ifp, 2078, SEEK_SET);
-    height = 1716;
-    width  = 2304;
-      } else if (model[8] == '5') {
-    data_offset = 4016;
-    fseek (ifp, 1936, SEEK_SET);
-konica_510z:
-    height = 1956;
-    width  = 2607;
-    raw_width = 2624;
-      } else if (model[8] == '6') {
-    data_offset = 4032;
-    fseek (ifp, 2030, SEEK_SET);
-    height = 2136;
-    width  = 2848;
-      }
-      filters = 0x61616161;
-konica_400z:
-      load_raw = unpacked_load_raw;
-      maximum = 0x3df;
-      order = 0x4d4d;
-      camera_red   = get2(ifp);
-      camera_blue  = get2(ifp);
-      camera_red  /= get2(ifp);
-      camera_blue /= get2(ifp);
-    }
-    if (pre_mul[0] == 1 && pre_mul[2] == 1) {
-      pre_mul[0] = 1.42;
-      pre_mul[2] = 1.25;
-    }
-  } else if (!strcmp(model,"*ist D")) {
-    load_raw = unpacked_load_raw;
-  } else if (!strcmp(model,"*ist DS")) {
-    height--;
-    load_raw = packed_12_load_raw;
-  } else if (!strcmp(model,"Optio S")) {
-    if (fsize == 3178560) {
-      height = 1540;
-      width  = 2064;
-      load_raw = eight_bit_load_raw;
-      camera_red  *= 4;
-      camera_blue *= 4;
-      pre_mul[0] = 1.391;
-      pre_mul[2] = 1.188;
-    } else {
-      height = 1544;
-      width  = 2068;
-      raw_width = 3136;
-      load_raw = packed_12_load_raw;
-      maximum = 0xf7c;
-      pre_mul[0] = 1.137;
-      pre_mul[2] = 1.453;
-    }
-  } else if (!strncmp(model,"Optio S4",8)) {
-    height = 1737;
-    width  = 2324;
-    raw_width = 3520;
-    load_raw = packed_12_load_raw;
-    maximum = 0xf7a;
-    pre_mul[0] = 1.980;
-    pre_mul[2] = 1.570;
-  } else if (!strcmp(model,"STV680 VGA")) {
-    height = 484;
-    width  = 644;
-    load_raw = eight_bit_load_raw;
-    flip = 2;
-    filters = 0x16161616;
-    black = 16;
-    pre_mul[0] = 1.097;
-    pre_mul[2] = 1.128;
-  } else if (!strcmp(make,"Phase One")) {
-    switch (raw_height) {
-      case 2060:
-    strcpy (model, "LightPhase");
-    height = 2048;
-    width  = 3080;
-    top_margin  = 5;
-    left_margin = 22;
-    pre_mul[0] = 1.331;
-    pre_mul[2] = 1.154;
-    break;
-      case 2682:
-    strcpy (model, "H10");
-    height = 2672;
-    width  = 4012;
-    top_margin  = 5;
-    left_margin = 26;
-    break;
-      case 4128:
-    strcpy (model, "H20");
-    height = 4098;
-    width  = 4098;
-    top_margin  = 20;
-    left_margin = 26;
-    pre_mul[0] = 1.963;
-    pre_mul[2] = 1.430;
-    break;
-      case 5488:
-    strcpy (model, "H25");
-    height = 5458;
-    width  = 4098;
-    top_margin  = 20;
-    left_margin = 26;
-    pre_mul[0] = 2.80;
-    pre_mul[2] = 1.20;
-    }
-    filters = top_margin & 1 ? 0x94949494 : 0x49494949;
-    load_raw = phase_one_load_raw;
-    maximum = 0xffff;
-  } else if (!strcmp(model,"Ixpress")) {
-    height = 4084;
-    width  = 4080;
-    filters = 0x49494949;
-    load_raw = ixpress_load_raw;
-    maximum = 0xffff;
-    pre_mul[0] = 1.963;
-    pre_mul[2] = 1.430;
-  } else if (!strcmp(make,"Sinar") && !memcmp(head,"8BPS",4)) {
-    fseek (ifp, 14, SEEK_SET);
-    height = get4(ifp);
-    width  = get4(ifp);
-    filters = 0x61616161;
-    data_offset = 68;
-    load_raw = unpacked_load_raw;
-    maximum = 0xffff;
-  } else if (!strcmp(make,"Leaf")) {
-    if (height > width)
-      filters = 0x16161616;
-    load_raw = unpacked_load_raw;
-    maximum = 0x3fff;
-    strcpy (model, "Valeo");
-    if (raw_width == 2060) {
-      filters = 0;
-      load_raw = leaf_load_raw;
-      maximum = 0xffff;
-      strcpy (model, "Volare");
-    }
-  } else if (!strcmp(model,"DIGILUX 2") || !strcmp(model,"DMC-LC1")) {
-    height = 1928;
-    width  = 2568;
-    data_offset = 1024;
-    load_raw = unpacked_load_raw;
-    maximum = 0xfff0;
-  } else if (!strcmp(model,"E-1")) {
-    filters = 0x61616161;
-    load_raw = unpacked_load_raw;
-    maximum = 0xfff0;
-    black = 1024;
-  } else if (!strcmp(model,"E-10")) {
-    load_raw = unpacked_load_raw;
-    maximum = 0xfff0;
-    black = 2048;
-  } else if (!strncmp(model,"E-20",4)) {
-    load_raw = unpacked_load_raw;
-    maximum = 0xffc0;
-    black = 2560;
-  } else if (!strcmp(model,"E-300")) {
-    width -= 21;
-    load_raw = olympus_e300_load_raw;
-    if (fsize > 15728640) {
-      load_raw = unpacked_load_raw;
-      maximum = 0xfc30;
-    } else
-      black = 62;
-  } else if (!strcmp(make,"OLYMPUS")) {
-    load_raw = olympus_cseries_load_raw;
-    if (!strcmp(model,"C5050Z") ||
-    !strcmp(model,"C8080WZ"))
-      filters = 0x16161616;
-  } else if (!strcmp(model,"N Digital")) {
-    height = 2047;
-    width  = 3072;
-    filters = 0x61616161;
-    data_offset = 0x1a00;
-    load_raw = packed_12_load_raw;
-    maximum = 0xf1e;
-  } else if (!strcmp(model,"DSC-F828")) {
-    width = 3288;
-    left_margin = 5;
-    load_raw = sony_load_raw;
-    filters = 0x9c9c9c9c;
-    colors = 4;
-    black = 491;
-  } else if (!strcmp(model,"DSC-V3")) {
-    width = 3109;
-    left_margin = 59;
-    load_raw = sony_load_raw;
-  } else if (!strcasecmp(make,"KODAK")) {
-    filters = 0x61616161;
-    if (!strcmp(model,"NC2000F")) {
-      width -= 4;
-      left_margin = 1;
-      for (i=176; i < 0x1000; i++)
-    curve[i] = curve[i-1];
-      pre_mul[0] = 1.509;
-      pre_mul[2] = 2.686;
-    } else if (!strcmp(model,"EOSDCS3B")) {
-      width -= 4;
-      left_margin = 2;
-    } else if (!strcmp(model,"EOSDCS1")) {
-      width -= 4;
-      left_margin = 2;
-    } else if (!strcmp(model,"DCS315C")) {
-      black = 8;
-    } else if (!strcmp(model,"DCS330C")) {
-      black = 8;
-    } else if (!strcmp(model,"DCS420")) {
-      width -= 4;
-      left_margin = 2;
-    } else if (!strcmp(model,"DCS460")) {
-      width -= 4;
-      left_margin = 2;
-    } else if (!strcmp(model,"DCS460A")) {
-      width -= 4;
-      left_margin = 2;
-      colors = 1;
-      filters = 0;
-    } else if (!strcmp(model,"DCS520C")) {
-      black = 180;
-    } else if (!strcmp(model,"DCS560C")) {
-      black = 188;
-    } else if (!strcmp(model,"DCS620C")) {
-      black = 180;
-    } else if (!strcmp(model,"DCS620X")) {
-      black = 185;
-    } else if (!strcmp(model,"DCS660C")) {
-      black = 214;
-    } else if (!strcmp(model,"DCS660M")) {
-      black = 214;
-      colors = 1;
-      filters = 0;
-    } else if (!strcmp(model,"DCS760M")) {
-      colors = 1;
-      filters = 0;
-    }
-    switch (tiff_data_compression) {
-    case 0:               /* No compression */
-    case 1:
-        load_raw = kodak_easy_load_raw;
-        break;
-    case 7:               /* Lossless JPEG */
-        load_raw = lossless_jpeg_load_raw;
-    case 32867:
-        break;
-    case 65000:           /* Kodak DCR compression */
+    if (is_foveon) {
         if (!have64BitArithmetic)
             pm_error("This program was built without 64 bit arithmetic "
-                     "capability, and Kodak DCR compression requires it.");
-        if (kodak_data_compression == 32803)
-            load_raw = kodak_compressed_load_raw;
-        else {
-            load_raw = kodak_yuv_load_raw;
-            height = (height+1) & -2;
-            width  = (width +1) & -2;
+                     "capability and the Foveon format requires it.");
+        if (height*2 < width) ymag = 2;
+        if (width < height) xmag = 2;
+        filters = 0;
+        load_raw = foveon_load_raw;
+        foveon_coeff(&use_coeff, coeff);
+    } else if (!strcmp(model,"PowerShot 600")) {
+        height = 613;
+        width  = 854;
+        colors = 4;
+        filters = 0xe1e4e1e4;
+        load_raw = canon_600_load_raw;
+    } else if (!strcmp(model,"PowerShot A5") ||
+               !strcmp(model,"PowerShot A5 Zoom")) {
+        height = 773;
+        width  = 960;
+        raw_width = 992;
+        colors = 4;
+        filters = 0x1e4e1e4e;
+        load_raw = canon_a5_load_raw;
+    } else if (!strcmp(model,"PowerShot A50")) {
+        height =  968;
+        width  = 1290;
+        raw_width = 1320;
+        colors = 4;
+        filters = 0x1b4e4b1e;
+        load_raw = canon_a5_load_raw;
+    } else if (!strcmp(model,"PowerShot Pro70")) {
+        height = 1024;
+        width  = 1552;
+        colors = 4;
+        filters = 0x1e4b4e1b;
+        load_raw = canon_a5_load_raw;
+        black = 34;
+    } else if (!strcmp(model,"PowerShot Pro90 IS")) {
+        width  = 1896;
+        colors = 4;
+        filters = 0xb4b4b4b4;
+    } else if (is_canon && raw_width == 2144) {
+        height = 1550;
+        width  = 2088;
+        top_margin  = 8;
+        left_margin = 4;
+        if (!strcmp(model,"PowerShot G1")) {
+            colors = 4;
+            filters = 0xb4b4b4b4;
+        }
+    } else if (is_canon && raw_width == 2224) {
+        height = 1448;
+        width  = 2176;
+        top_margin  = 6;
+        left_margin = 48;
+    } else if (is_canon && raw_width == 2376) {
+        height = 1720;
+        width  = 2312;
+        top_margin  = 6;
+        left_margin = 12;
+    } else if (is_canon && raw_width == 2672) {
+        height = 1960;
+        width  = 2616;
+        top_margin  = 6;
+        left_margin = 12;
+    } else if (is_canon && raw_width == 3152) {
+        height = 2056;
+        width  = 3088;
+        top_margin  = 12;
+        left_margin = 64;
+        maximum = 0xfa0;
+    } else if (is_canon && raw_width == 3160) {
+        height = 2328;
+        width  = 3112;
+        top_margin  = 12;
+        left_margin = 44;
+    } else if (is_canon && raw_width == 3344) {
+        height = 2472;
+        width  = 3288;
+        top_margin  = 6;
+        left_margin = 4;
+    } else if (!strcmp(model,"EOS D2000C")) {
+        filters = 0x61616161;
+        black = curve[200];
+    } else if (!strcmp(model,"EOS-1D")) {
+        raw_height = height = 1662;
+        raw_width  = width  = 2496;
+        data_offset = 288912;
+        filters = 0x61616161;
+    } else if (!strcmp(model,"EOS-1DS")) {
+        raw_height = height = 2718;
+        raw_width  = width  = 4082;
+        data_offset = 289168;
+        filters = 0x61616161;
+    } else if (is_canon && raw_width == 3516) {
+        top_margin  = 14;
+        left_margin = 42;
+        goto canon_cr2;
+    } else if (is_canon && raw_width == 3596) {
+        top_margin  = 12;
+        left_margin = 74;
+        goto canon_cr2;
+    } else if (is_canon && raw_width == 5108) {
+        top_margin  = 13;
+        left_margin = 98;
+        maximum = 0xe80;
+    canon_cr2:
+        height = raw_height - top_margin;
+        width  = raw_width - left_margin;
+    } else if (!strcmp(model,"D1")) {
+        camera_red  *= 256/527.0;
+        camera_blue *= 256/317.0;
+    } else if (!strcmp(model,"D1X")) {
+        width  = 4024;
+        ymag = 2;
+    } else if (!strcmp(model,"D100")) {
+        if (tiff_data_compression == 34713 && load_raw == nikon_load_raw)
+            raw_width = (width += 3) + 3;
+    } else if (!strcmp(model,"D2H")) {
+        width  = 2482;
+        left_margin = 6;
+    } else if (!strcmp(model,"D2X")) {
+        width  = 4312;
+        pre_mul[0] = 1.514;
+        pre_mul[2] = 1.727;
+    } else if (fsize == 2465792) {
+        height = 1203;
+        width  = 1616;
+        filters = 0x4b4b4b4b;
+        colors = 4;
+        load_raw = nikon_e950_load_raw;
+        nikon_e950_coeff();
+        pre_mul[0] = 1.18193;
+        pre_mul[2] = 1.16452;
+        pre_mul[3] = 1.17250;
+    } else if (!strcmp(model,"E990")) {
+        if (!timestamp && !nikon_e990()) goto cp_e995;
+        height = 1540;
+        width  = 2064;
+        colors = 4;
+        filters = 0xb4b4b4b4;
+        nikon_e950_coeff();
+        pre_mul[0] = 1.196;
+        pre_mul[1] = 1.246;
+        pre_mul[2] = 1.018;
+    } else if (!strcmp(model,"E995")) {
+    cp_e995:
+        strcpy (model, "E995");
+        height = 1540;
+        width  = 2064;
+        colors = 4;
+        filters = 0xe1e1e1e1;
+    } else if (!strcmp(model,"E2100")) {
+        if (!timestamp && !nikon_e2100()) goto cp_e2500;
+        width = 1616;
+        height = 1206;
+        load_raw = nikon_e2100_load_raw;
+        pre_mul[0] = 1.945;
+        pre_mul[2] = 1.040;
+    } else if (!strcmp(model,"E2500")) {
+    cp_e2500:
+        strcpy (model, "E2500");
+        width = 1616;
+        height = 1204;
+        colors = 4;
+        filters = 0x4b4b4b4b;
+    } else if (!strcmp(model,"E3700")) {
+        if (!timestamp && pentax_optio33()) goto optio_33wr;
+        height = 1542;
+        width  = 2064;
+        load_raw = nikon_e2100_load_raw;
+        pre_mul[0] = 1.818;
+        pre_mul[2] = 1.618;
+    } else if (!strcmp(model,"Optio 33WR")) {
+    optio_33wr:
+        strcpy (make, "PENTAX");
+        strcpy (model,"Optio 33WR");
+        height = 1542;
+        width  = 2064;
+        load_raw = nikon_e2100_load_raw;
+        flip = 1;
+        filters = 0x16161616;
+        pre_mul[0] = 1.331;
+        pre_mul[2] = 1.820;
+    } else if (!strcmp(model,"E4300")) {
+        if (!timestamp && minolta_z2()) goto dimage_z2;
+        height = 1710;
+        width  = 2288;
+        filters = 0x16161616;
+        pre_mul[0] = 508;
+        pre_mul[1] = 256;
+        pre_mul[2] = 322;
+    } else if (!strcmp(model,"DiMAGE Z2")) {
+    dimage_z2:
+        strcpy (make, "MINOLTA");
+        strcpy (model,"DiMAGE Z2");
+        height = 1710;
+        width  = 2288;
+        filters = 0x16161616;
+        load_raw = nikon_e2100_load_raw;
+        pre_mul[0] = 508;
+        pre_mul[1] = 256;
+        pre_mul[2] = 450;
+    } else if (!strcmp(model,"E4500")) {
+        height = 1708;
+        width  = 2288;
+        colors = 4;
+        filters = 0xb4b4b4b4;
+    } else if (!strcmp(model,"R-D1")) {
+        tiff_data_compression = 34713;
+        load_raw = nikon_load_raw;
+    } else if (!strcmp(model,"FinePixS2Pro")) {
+        height = 3584;
+        width  = 3583;
+        fuji_width = 2144;
+        filters = 0x61616161;
+        load_raw = fuji_s2_load_raw;
+        black = 128;
+        strcpy (model+7, " S2Pro");
+    } else if (!strcmp(model,"FinePix S3Pro")) {
+        height = 3583;
+        width  = 3584;
+        fuji_width = 2144;
+        if (fsize > 18000000 && use_secondary)
+            data_offset += 4352*2*1444;
+        filters = 0x49494949;
+        load_raw = fuji_s3_load_raw;
+        maximum = 0xffff;
+    } else if (!strcmp(model,"FinePix S5000")) {
+        height = 2499;
+        width  = 2500;
+        fuji_width = 1423;
+        filters = 0x49494949;
+        load_raw = fuji_s5000_load_raw;
+        maximum = 0x3e00;
+    } else if (!strcmp(model,"FinePix S5100") ||
+               !strcmp(model,"FinePix S5500")) {
+        height = 1735;
+        width  = 2304;
+        data_offset += width*10;
+        filters = 0x49494949;
+        load_raw = unpacked_load_raw;
+        maximum = 0xffff;
+    } else if (!strcmp(model,"FinePix E550") ||
+               !strcmp(model,"FinePix F810") ||
+               !strcmp(model,"FinePix S7000")) {
+        height = 3587;
+        width  = 3588;
+        fuji_width = 2047;
+        filters = 0x49494949;
+        load_raw = fuji_s7000_load_raw;
+        maximum = 0x3e00;
+    } else if (!strcmp(model,"FinePix F700") ||
+               !strcmp(model,"FinePix S20Pro")) {
+        height = 2523;
+        width  = 2524;
+        fuji_width = 1440;
+        filters = 0x49494949;
+        load_raw = fuji_f700_load_raw;
+        maximum = 0x3e00;
+    } else if (!strcmp(model,"Digital Camera KD-400Z")) {
+        height = 1712;
+        width  = 2312;
+        raw_width = 2336;
+        data_offset = 4034;
+        fseek (ifp, 2032, SEEK_SET);
+        goto konica_400z;
+    } else if (!strcmp(model,"Digital Camera KD-510Z")) {
+        data_offset = 4032;
+        pre_mul[0] = 1.297;
+        pre_mul[2] = 1.438;
+        fseek (ifp, 2032, SEEK_SET);
+        goto konica_510z;
+    } else if (!strcasecmp(make,"MINOLTA")) {
+        load_raw = unpacked_load_raw;
+        maximum = 0xf7d;
+        if (!strncmp(model,"DiMAGE A",8)) {
+            if (!strcmp(model,"DiMAGE A200")) {
+                filters = 0x49494949;
+                tmp = camera_red;
+                camera_red  = 1 / camera_blue;
+                camera_blue = 1 / tmp;
+            }
+            load_raw = packed_12_load_raw;
+            maximum = model[8] == '1' ? 0xf8b : 0xfff;
+        } else if (!strncmp(model,"ALPHA",5) ||
+                   !strncmp(model,"DYNAX",5) ||
+                   !strncmp(model,"MAXXUM",6)) {
+            load_raw = packed_12_load_raw;
+            maximum = 0xffb;
+        } else if (!strncmp(model,"DiMAGE G",8)) {
+            if (model[8] == '4') {
+                data_offset = 5056;
+                pre_mul[0] = 1.602;
+                pre_mul[2] = 1.441;
+                fseek (ifp, 2078, SEEK_SET);
+                height = 1716;
+                width  = 2304;
+            } else if (model[8] == '5') {
+                data_offset = 4016;
+                fseek (ifp, 1936, SEEK_SET);
+            konica_510z:
+                height = 1956;
+                width  = 2607;
+                raw_width = 2624;
+            } else if (model[8] == '6') {
+                data_offset = 4032;
+                fseek (ifp, 2030, SEEK_SET);
+                height = 2136;
+                width  = 2848;
+            }
+            filters = 0x61616161;
+        konica_400z:
+            load_raw = unpacked_load_raw;
+            maximum = 0x3df;
+            order = 0x4d4d;
+            camera_red   = get2(ifp);
+            camera_blue  = get2(ifp);
+            camera_red  /= get2(ifp);
+            camera_blue /= get2(ifp);
+        }
+        if (pre_mul[0] == 1 && pre_mul[2] == 1) {
+            pre_mul[0] = 1.42;
+            pre_mul[2] = 1.25;
+        }
+    } else if (!strcmp(model,"*ist D")) {
+        load_raw = unpacked_load_raw;
+    } else if (!strcmp(model,"*ist DS")) {
+        height--;
+        load_raw = packed_12_load_raw;
+    } else if (!strcmp(model,"Optio S")) {
+        if (fsize == 3178560) {
+            height = 1540;
+            width  = 2064;
+            load_raw = eight_bit_load_raw;
+            camera_red  *= 4;
+            camera_blue *= 4;
+            pre_mul[0] = 1.391;
+            pre_mul[2] = 1.188;
+        } else {
+            height = 1544;
+            width  = 2068;
+            raw_width = 3136;
+            load_raw = packed_12_load_raw;
+            maximum = 0xf7c;
+            pre_mul[0] = 1.137;
+            pre_mul[2] = 1.453;
+        }
+    } else if (!strncmp(model,"Optio S4",8)) {
+        height = 1737;
+        width  = 2324;
+        raw_width = 3520;
+        load_raw = packed_12_load_raw;
+        maximum = 0xf7a;
+        pre_mul[0] = 1.980;
+        pre_mul[2] = 1.570;
+    } else if (!strcmp(model,"STV680 VGA")) {
+        height = 484;
+        width  = 644;
+        load_raw = eight_bit_load_raw;
+        flip = 2;
+        filters = 0x16161616;
+        black = 16;
+        pre_mul[0] = 1.097;
+        pre_mul[2] = 1.128;
+    } else if (!strcmp(make,"Phase One")) {
+        switch (raw_height) {
+        case 2060:
+            strcpy (model, "LightPhase");
+            height = 2048;
+            width  = 3080;
+            top_margin  = 5;
+            left_margin = 22;
+            pre_mul[0] = 1.331;
+            pre_mul[2] = 1.154;
+            break;
+        case 2682:
+            strcpy (model, "H10");
+            height = 2672;
+            width  = 4012;
+            top_margin  = 5;
+            left_margin = 26;
+            break;
+        case 4128:
+            strcpy (model, "H20");
+            height = 4098;
+            width  = 4098;
+            top_margin  = 20;
+            left_margin = 26;
+            pre_mul[0] = 1.963;
+            pre_mul[2] = 1.430;
+            break;
+        case 5488:
+            strcpy (model, "H25");
+            height = 5458;
+            width  = 4098;
+            top_margin  = 20;
+            left_margin = 26;
+            pre_mul[0] = 2.80;
+            pre_mul[2] = 1.20;
+        }
+        filters = top_margin & 1 ? 0x94949494 : 0x49494949;
+        load_raw = phase_one_load_raw;
+        maximum = 0xffff;
+    } else if (!strcmp(model,"Ixpress")) {
+        height = 4084;
+        width  = 4080;
+        filters = 0x49494949;
+        load_raw = ixpress_load_raw;
+        maximum = 0xffff;
+        pre_mul[0] = 1.963;
+        pre_mul[2] = 1.430;
+    } else if (!strcmp(make,"Sinar") && !memcmp(head,"8BPS",4)) {
+        fseek (ifp, 14, SEEK_SET);
+        height = get4(ifp);
+        width  = get4(ifp);
+        filters = 0x61616161;
+        data_offset = 68;
+        load_raw = unpacked_load_raw;
+        maximum = 0xffff;
+    } else if (!strcmp(make,"Leaf")) {
+        if (height > width)
+            filters = 0x16161616;
+        load_raw = unpacked_load_raw;
+        maximum = 0x3fff;
+        strcpy (model, "Valeo");
+        if (raw_width == 2060) {
             filters = 0;
+            load_raw = leaf_load_raw;
+            maximum = 0xffff;
+            strcpy (model, "Volare");
+        }
+    } else if (!strcmp(model,"DIGILUX 2") || !strcmp(model,"DMC-LC1")) {
+        height = 1928;
+        width  = 2568;
+        data_offset = 1024;
+        load_raw = unpacked_load_raw;
+        maximum = 0xfff0;
+    } else if (!strcmp(model,"E-1")) {
+        filters = 0x61616161;
+        load_raw = unpacked_load_raw;
+        maximum = 0xfff0;
+        black = 1024;
+    } else if (!strcmp(model,"E-10")) {
+        load_raw = unpacked_load_raw;
+        maximum = 0xfff0;
+        black = 2048;
+    } else if (!strncmp(model,"E-20",4)) {
+        load_raw = unpacked_load_raw;
+        maximum = 0xffc0;
+        black = 2560;
+    } else if (!strcmp(model,"E-300")) {
+        width -= 21;
+        load_raw = olympus_e300_load_raw;
+        if (fsize > 15728640) {
+            load_raw = unpacked_load_raw;
+            maximum = 0xfc30;
+        } else
+            black = 62;
+    } else if (!strcmp(make,"OLYMPUS")) {
+        load_raw = olympus_cseries_load_raw;
+        if (!strcmp(model,"C5050Z") ||
+            !strcmp(model,"C8080WZ"))
+            filters = 0x16161616;
+    } else if (!strcmp(model,"N Digital")) {
+        height = 2047;
+        width  = 3072;
+        filters = 0x61616161;
+        data_offset = 0x1a00;
+        load_raw = packed_12_load_raw;
+        maximum = 0xf1e;
+    } else if (!strcmp(model,"DSC-F828")) {
+        width = 3288;
+        left_margin = 5;
+        load_raw = sony_load_raw;
+        filters = 0x9c9c9c9c;
+        colors = 4;
+        black = 491;
+    } else if (!strcmp(model,"DSC-V3")) {
+        width = 3109;
+        left_margin = 59;
+        load_raw = sony_load_raw;
+    } else if (!strcasecmp(make,"KODAK")) {
+        filters = 0x61616161;
+        if (!strcmp(model,"NC2000F")) {
+            width -= 4;
+            left_margin = 1;
+            for (i=176; i < 0x1000; i++)
+                curve[i] = curve[i-1];
+            pre_mul[0] = 1.509;
+            pre_mul[2] = 2.686;
+        } else if (!strcmp(model,"EOSDCS3B")) {
+            width -= 4;
+            left_margin = 2;
+        } else if (!strcmp(model,"EOSDCS1")) {
+            width -= 4;
+            left_margin = 2;
+        } else if (!strcmp(model,"DCS315C")) {
+            black = 8;
+        } else if (!strcmp(model,"DCS330C")) {
+            black = 8;
+        } else if (!strcmp(model,"DCS420")) {
+            width -= 4;
+            left_margin = 2;
+        } else if (!strcmp(model,"DCS460")) {
+            width -= 4;
+            left_margin = 2;
+        } else if (!strcmp(model,"DCS460A")) {
+            width -= 4;
+            left_margin = 2;
+            colors = 1;
+            filters = 0;
+        } else if (!strcmp(model,"DCS520C")) {
+            black = 180;
+        } else if (!strcmp(model,"DCS560C")) {
+            black = 188;
+        } else if (!strcmp(model,"DCS620C")) {
+            black = 180;
+        } else if (!strcmp(model,"DCS620X")) {
+            black = 185;
+        } else if (!strcmp(model,"DCS660C")) {
+            black = 214;
+        } else if (!strcmp(model,"DCS660M")) {
+            black = 214;
+            colors = 1;
+            filters = 0;
+        } else if (!strcmp(model,"DCS760M")) {
+            colors = 1;
+            filters = 0;
+        }
+        switch (tiff_data_compression) {
+        case 0:               /* No compression */
+        case 1:
+            load_raw = kodak_easy_load_raw;
+            break;
+        case 7:               /* Lossless JPEG */
+            load_raw = lossless_jpeg_load_raw;
+        case 32867:
+            break;
+        case 65000:           /* Kodak DCR compression */
+            if (!have64BitArithmetic)
+                pm_error("This program was built without 64 bit arithmetic "
+                         "capability, and Kodak DCR compression requires it.");
+            if (kodak_data_compression == 32803)
+                load_raw = kodak_compressed_load_raw;
+            else {
+                load_raw = kodak_yuv_load_raw;
+                height = (height+1) & -2;
+                width  = (width +1) & -2;
+                filters = 0;
+            }
+            break;
+        default:
+            pm_message ("%s %s uses unrecognized compression method %d.",
+                        make, model, tiff_data_compression);
+            return 1;
+        }
+        if (!strcmp(model,"DC20")) {
+            height = 242;
+            if (fsize < 100000) {
+                width = 249;
+                raw_width = 256;
+            } else {
+                width = 501;
+                raw_width = 512;
+            }
+            data_offset = raw_width + 1;
+            colors = 4;
+            filters = 0x8d8d8d8d;
+            kodak_dc20_coeff (1.0);
+            pre_mul[1] = 1.179;
+            pre_mul[2] = 1.209;
+            pre_mul[3] = 1.036;
+            load_raw = kodak_easy_load_raw;
+        } else if (strstr(model,"DC25")) {
+            strcpy (model, "DC25");
+            height = 242;
+            if (fsize < 100000) {
+                width = 249;
+                raw_width = 256;
+                data_offset = 15681;
+            } else {
+                width = 501;
+                raw_width = 512;
+                data_offset = 15937;
+            }
+            colors = 4;
+            filters = 0xb4b4b4b4;
+            load_raw = kodak_easy_load_raw;
+        } else if (!strcmp(model,"Digital Camera 40")) {
+            strcpy (model, "DC40");
+            height = 512;
+            width = 768;
+            data_offset = 1152;
+            load_raw = kodak_radc_load_raw;
+        } else if (strstr(model,"DC50")) {
+            strcpy (model, "DC50");
+            height = 512;
+            width = 768;
+            data_offset = 19712;
+            load_raw = kodak_radc_load_raw;
+        } else if (strstr(model,"DC120")) {
+            strcpy (model, "DC120");
+            height = 976;
+            width = 848;
+            if (tiff_data_compression == 7)
+                load_raw = kodak_jpeg_load_raw;
+            else
+                load_raw = kodak_dc120_load_raw;
+        }
+    } else if (!strcmp(make,"Rollei")) {
+        switch (raw_width) {
+        case 1316:
+            height = 1030;
+            width  = 1300;
+            top_margin  = 1;
+            left_margin = 6;
+            break;
+        case 2568:
+            height = 1960;
+            width  = 2560;
+            top_margin  = 2;
+            left_margin = 8;
+        }
+        filters = 0x16161616;
+        load_raw = rollei_load_raw;
+        pre_mul[0] = 1.8;
+        pre_mul[2] = 1.3;
+    } else if (!strcmp(model,"PC-CAM 600")) {
+        height = 768;
+        data_offset = width = 1024;
+        filters = 0x49494949;
+        load_raw = eight_bit_load_raw;
+        pre_mul[0] = 1.14;
+        pre_mul[2] = 2.73;
+    } else if (!strcmp(model,"QV-2000UX")) {
+        height = 1208;
+        width  = 1632;
+        data_offset = width * 2;
+        load_raw = eight_bit_load_raw;
+    } else if (!strcmp(model,"QV-3*00EX")) {
+        height = 1546;
+        width  = 2070;
+        raw_width = 2080;
+        load_raw = eight_bit_load_raw;
+    } else if (!strcmp(model,"QV-4000")) {
+        height = 1700;
+        width  = 2260;
+        load_raw = unpacked_load_raw;
+        maximum = 0xffff;
+    } else if (!strcmp(model,"QV-5700")) {
+        height = 1924;
+        width  = 2576;
+        load_raw = casio_qv5700_load_raw;
+    } else if (!strcmp(model,"QV-R51")) {
+        height = 1926;
+        width  = 2576;
+        raw_width = 3904;
+        load_raw = packed_12_load_raw;
+        pre_mul[0] = 1.340;
+        pre_mul[2] = 1.672;
+    } else if (!strcmp(model,"EX-Z55")) {
+        height = 1960;
+        width  = 2570;
+        raw_width = 3904;
+        load_raw = packed_12_load_raw;
+        pre_mul[0] = 1.520;
+        pre_mul[2] = 1.316;
+    } else if (!strcmp(model,"EX-P600")) {
+        height = 2142;
+        width  = 2844;
+        raw_width = 4288;
+        load_raw = packed_12_load_raw;
+        pre_mul[0] = 1.797;
+        pre_mul[2] = 1.219;
+    } else if (!strcmp(model,"EX-P700")) {
+        height = 2318;
+        width  = 3082;
+        raw_width = 4672;
+        load_raw = packed_12_load_raw;
+        pre_mul[0] = 1.758;
+        pre_mul[2] = 1.504;
+    } else if (!strcmp(make,"Nucore")) {
+        filters = 0x61616161;
+        load_raw = unpacked_load_raw;
+        if (width == 2598) {
+            filters = 0x16161616;
+            load_raw = nucore_load_raw;
+            flip = 2;
         }
-        break;
-    default:
-        pm_message ("%s %s uses unrecognized compression method %d.",
-                    make, model, tiff_data_compression);
-        return 1;
-    }
-    if (!strcmp(model,"DC20")) {
-      height = 242;
-      if (fsize < 100000) {
-    width = 249;
-    raw_width = 256;
-      } else {
-    width = 501;
-    raw_width = 512;
-      }
-      data_offset = raw_width + 1;
-      colors = 4;
-      filters = 0x8d8d8d8d;
-      kodak_dc20_coeff (1.0);
-      pre_mul[1] = 1.179;
-      pre_mul[2] = 1.209;
-      pre_mul[3] = 1.036;
-      load_raw = kodak_easy_load_raw;
-    } else if (strstr(model,"DC25")) {
-      strcpy (model, "DC25");
-      height = 242;
-      if (fsize < 100000) {
-    width = 249;
-    raw_width = 256;
-    data_offset = 15681;
-      } else {
-    width = 501;
-    raw_width = 512;
-    data_offset = 15937;
-      }
-      colors = 4;
-      filters = 0xb4b4b4b4;
-      load_raw = kodak_easy_load_raw;
-    } else if (!strcmp(model,"Digital Camera 40")) {
-      strcpy (model, "DC40");
-      height = 512;
-      width = 768;
-      data_offset = 1152;
-      load_raw = kodak_radc_load_raw;
-    } else if (strstr(model,"DC50")) {
-      strcpy (model, "DC50");
-      height = 512;
-      width = 768;
-      data_offset = 19712;
-      load_raw = kodak_radc_load_raw;
-    } else if (strstr(model,"DC120")) {
-      strcpy (model, "DC120");
-      height = 976;
-      width = 848;
-      if (tiff_data_compression == 7)
-    load_raw = kodak_jpeg_load_raw;
-      else
-    load_raw = kodak_dc120_load_raw;
-    }
-  } else if (!strcmp(make,"Rollei")) {
-    switch (raw_width) {
-      case 1316:
-    height = 1030;
-    width  = 1300;
-    top_margin  = 1;
-    left_margin = 6;
-    break;
-      case 2568:
-    height = 1960;
-    width  = 2560;
-    top_margin  = 2;
-    left_margin = 8;
-    }
-    filters = 0x16161616;
-    load_raw = rollei_load_raw;
-    pre_mul[0] = 1.8;
-    pre_mul[2] = 1.3;
-  } else if (!strcmp(model,"PC-CAM 600")) {
-    height = 768;
-    data_offset = width = 1024;
-    filters = 0x49494949;
-    load_raw = eight_bit_load_raw;
-    pre_mul[0] = 1.14;
-    pre_mul[2] = 2.73;
-  } else if (!strcmp(model,"QV-2000UX")) {
-    height = 1208;
-    width  = 1632;
-    data_offset = width * 2;
-    load_raw = eight_bit_load_raw;
-  } else if (!strcmp(model,"QV-3*00EX")) {
-    height = 1546;
-    width  = 2070;
-    raw_width = 2080;
-    load_raw = eight_bit_load_raw;
-  } else if (!strcmp(model,"QV-4000")) {
-    height = 1700;
-    width  = 2260;
-    load_raw = unpacked_load_raw;
-    maximum = 0xffff;
-  } else if (!strcmp(model,"QV-5700")) {
-    height = 1924;
-    width  = 2576;
-    load_raw = casio_qv5700_load_raw;
-  } else if (!strcmp(model,"QV-R51")) {
-    height = 1926;
-    width  = 2576;
-    raw_width = 3904;
-    load_raw = packed_12_load_raw;
-    pre_mul[0] = 1.340;
-    pre_mul[2] = 1.672;
-  } else if (!strcmp(model,"EX-Z55")) {
-    height = 1960;
-    width  = 2570;
-    raw_width = 3904;
-    load_raw = packed_12_load_raw;
-    pre_mul[0] = 1.520;
-    pre_mul[2] = 1.316;
-  } else if (!strcmp(model,"EX-P600")) {
-    height = 2142;
-    width  = 2844;
-    raw_width = 4288;
-    load_raw = packed_12_load_raw;
-    pre_mul[0] = 1.797;
-    pre_mul[2] = 1.219;
-  } else if (!strcmp(model,"EX-P700")) {
-    height = 2318;
-    width  = 3082;
-    raw_width = 4672;
-    load_raw = packed_12_load_raw;
-    pre_mul[0] = 1.758;
-    pre_mul[2] = 1.504;
-  } else if (!strcmp(make,"Nucore")) {
-    filters = 0x61616161;
-    load_raw = unpacked_load_raw;
-    if (width == 2598) {
-      filters = 0x16161616;
-      load_raw = nucore_load_raw;
-      flip = 2;
     }
-  }
-  if (!use_coeff) adobe_coeff();
+    if (!use_coeff)
+        adobeCoeff(make, model);
 dng_skip:
-  if (!load_raw || !height) {
-    pm_message ("This program cannot handle data from %s %s.",
-                make, model);
-    return 1;
-  }
+    if (!load_raw || !height) {
+        pm_message ("This program cannot handle data from %s %s.",
+                    make, model);
+        return 1;
+    }
 #ifdef NO_JPEG
-  if (load_raw == kodak_jpeg_load_raw) {
-    pm_message ("decoder was not linked with libjpeg.");
-    return 1;
-  }
-#endif
-  if (!raw_height) raw_height = height;
-  if (!raw_width ) raw_width  = width;
-  if (use_camera_rgb && colors == 3)
-      use_coeff = 0;
-  if (use_coeff)         /* Apply user-selected color balance */
-    for (i=0; i < colors; i++) {
-      coeff[0][i] *= red_scale;
-      coeff[2][i] *= blue_scale;
+    if (load_raw == kodak_jpeg_load_raw) {
+        pm_message ("decoder was not linked with libjpeg.");
+        return 1;
     }
-  if (four_color_rgb && filters && colors == 3) {
-    for (i=0; i < 32; i+=4) {
-      if ((filters >> i & 15) == 9)
-    filters |= 2 << i;
-      if ((filters >> i & 15) == 6)
-    filters |= 8 << i;
+#endif
+    if (!raw_height) raw_height = height;
+    if (!raw_width ) raw_width  = width;
+    if (use_camera_rgb && colors == 3)
+        use_coeff = 0;
+    if (use_coeff)         /* Apply user-selected color balance */
+        for (i=0; i < colors; i++) {
+            coeff[0][i] *= red_scale;
+            coeff[2][i] *= blue_scale;
+        }
+    if (four_color_rgb && filters && colors == 3) {
+        for (i=0; i < 32; i+=4) {
+            if ((filters >> i & 15) == 9)
+                filters |= 2 << i;
+            if ((filters >> i & 15) == 6)
+                filters |= 8 << i;
+        }
+        colors++;
+        pre_mul[3] = pre_mul[1];
+        if (use_coeff)
+            for (i=0; i < 3; i++)
+                coeff[i][3] = coeff[i][1] /= 2;
     }
-    colors++;
-    pre_mul[3] = pre_mul[1];
-    if (use_coeff)
-      for (i=0; i < 3; i++)
-    coeff[i][3] = coeff[i][1] /= 2;
-  }
-  fseek (ifp, data_offset, SEEK_SET);
+    fseek (ifp, data_offset, SEEK_SET);
 
-  *loadRawFnP = load_raw;
+    *loadRawFnP = load_raw;
 
-  return 0;
+    return 0;
 }
diff --git a/converter/other/cameratopam/identify.h b/converter/other/cameratopam/identify.h
index 012b807c..62c9aae5 100644
--- a/converter/other/cameratopam/identify.h
+++ b/converter/other/cameratopam/identify.h
@@ -1,4 +1,4 @@
-typedef void (*loadRawFn)();
+#include "camera.h"
 
 int
 identify(FILE *       const ifp,
@@ -8,4 +8,4 @@ identify(FILE *       const ifp,
          float        const blue_scale,
          unsigned int const four_color_rgb,
          const char * const inputFileName,
-         loadRawFn *  const loadRawFnP);
+         LoadRawFn ** const loadRawFnP);
diff --git a/converter/other/cameratopam/ljpeg.c b/converter/other/cameratopam/ljpeg.c
index 4b092933..07791e25 100644
--- a/converter/other/cameratopam/ljpeg.c
+++ b/converter/other/cameratopam/ljpeg.c
@@ -20,127 +20,145 @@
  */
 
 int  
-ljpeg_start (FILE * ifp, struct jhead *jh)
-{
-  int i, tag, len;
-  unsigned char data[256], *dp;
-
-  init_decoder();
-  for (i=0; i < 4; i++)
-    jh->huff[i] = free_decode;
-  fread (data, 2, 1, ifp);
-  if (data[0] != 0xff || data[1] != 0xd8) return 0;
-  do {
-    fread (data, 2, 2, ifp);
-    tag =  data[0] << 8 | data[1];
-    len = (data[2] << 8 | data[3]);
-    if (len < 2)
-      pm_error("Length field is %u; must be at least 2", len);
-    else {
-      unsigned int const dataLen = len - 2;
-      if (tag <= 0xff00 || dataLen > 255) return 0;
-      fread (data, 1, dataLen, ifp);
-      switch (tag) {
-      case 0xffc3:
-        jh->bits = data[0];
-        jh->high = data[1] << 8 | data[2];
-        jh->wide = data[3] << 8 | data[4];
-        jh->clrs = data[5];
-        break;
-      case 0xffc4:
-        for (dp = data; dp < data+dataLen && *dp < 4; ) {
-          jh->huff[*dp] = free_decode;
-          dp = make_decoder (++dp, 0);
+ljpeg_start(FILE *         const ifP,
+            struct jhead * const jhP) {
+
+    int i, tag;
+    unsigned char data[256], *dp;
+
+    init_decoder();
+    for (i=0; i < 4; i++)
+        jhP->huff[i] = free_decode;
+    fread (data, 2, 1, ifP);
+    if (data[0] != 0xff || data[1] != 0xd8) return 0;
+    do {
+        unsigned int len;
+
+        fread (data, 2, 2, ifP);
+        tag =  data[0] << 8 | data[1];
+        len = data[2] << 8 | data[3];
+
+        if (len < 2)
+            pm_error("Length field is %u; must be at least 2", len);
+        else {
+            unsigned int const dataLen = len - 2;
+
+            if (tag <= 0xff00 || dataLen > 255) return 0;
+            fread (data, 1, dataLen, ifP);
+            switch (tag) {
+            case 0xffc3:
+                jhP->bits = data[0];
+                jhP->high = data[1] << 8 | data[2];
+                jhP->wide = data[3] << 8 | data[4];
+                jhP->clrs = data[5];
+                break;
+            case 0xffc4:
+                for (dp = data; dp < data + dataLen && *dp < 4; ) {
+                    jhP->huff[*dp] = free_decode;
+                    dp = make_decoder (++dp, 0);
+                }
+            }
         }
-      }
-    }
-  } while (tag != 0xffda);
-  jh->row = calloc (jh->wide*jh->clrs, 2);
-  if (jh->row == NULL)
-      pm_error("Out of memory in ljpeg_start()");
-  for (i=0; i < 4; i++)
-    jh->vpred[i] = 1 << (jh->bits-1);
-  zero_after_ff = 1;
-  getbits(ifp, -1);
-  return 1;
+    } while (tag != 0xffda);
+    jhP->row = calloc (jhP->wide*jhP->clrs, 2);
+    if (jhP->row == NULL)
+        pm_error("Out of memory in ljpeg_start()");
+    for (i=0; i < 4; i++)
+        jhP->vpred[i] = 1 << (jhP->bits-1);
+    zero_after_ff = 1;
+    getbits(ifP, -1);
+    return 1;
 }
 
+
+
 int 
-ljpeg_diff (struct decode *dindex)
-{
-  int len, diff;
-
-  while (dindex->branch[0])
-    dindex = dindex->branch[getbits(ifp, 1)];
-  diff = getbits(ifp, len = dindex->leaf);
-  if ((diff & (1 << (len-1))) == 0)
-    diff -= (1 << len) - 1;
-  return diff;
+ljpeg_diff(FILE *          const ifP,
+           struct decode * const dindexHeadP) {
+
+    int len;
+    int diff;
+    struct decode * dindexP;
+
+    for (dindexP = dindexHeadP; dindexP->branch[0]; )
+        dindexP = dindexP->branch[getbits(ifP, 1)];
+
+    diff = getbits(ifP, len = dindexP->leaf);
+
+    if ((diff & (1 << (len-1))) == 0)
+        diff -= (1 << len) - 1;
+
+    return diff;
 }
 
+
+
 void
-ljpeg_row (struct jhead *jh)
-{
-  int col, c, diff;
-  unsigned short *outp=jh->row;
-
-  for (col=0; col < jh->wide; col++)
-    for (c=0; c < jh->clrs; c++) {
-      diff = ljpeg_diff (jh->huff[c]);
-      *outp = col ? outp[-jh->clrs]+diff : (jh->vpred[c] += diff);
-      outp++;
-    }
+ljpeg_row(FILE *         const ifP,
+          struct jhead * const jhP) {
+
+    int col, c, diff;
+    unsigned short *outp=jhP->row;
+
+    for (col=0; col < jhP->wide; col++)
+        for (c=0; c < jhP->clrs; c++) {
+            diff = ljpeg_diff(ifP, jhP->huff[c]);
+            *outp = col ? outp[-jhP->clrs]+diff : (jhP->vpred[c] += diff);
+            outp++;
+        }
 }
 
+
+
 void  
-lossless_jpeg_load_raw(void)
-{
-  int jwide, jrow, jcol, val, jidx, i, row, col;
-  struct jhead jh;
-  int min=INT_MAX;
-
-  if (!ljpeg_start (ifp, &jh)) return;
-  jwide = jh.wide * jh.clrs;
-
-  for (jrow=0; jrow < jh.high; jrow++) {
-    ljpeg_row (&jh);
-    for (jcol=0; jcol < jwide; jcol++) {
-      val = curve[jh.row[jcol]];
-      jidx = jrow*jwide + jcol;
-      if (raw_width == 5108) {
-    i = jidx / (1680*jh.high);
-    if (i < 2) {
-      row = jidx / 1680 % jh.high;
-      col = jidx % 1680 + i*1680;
-    } else {
-      jidx -= 2*1680*jh.high;
-      row = jidx / 1748;
-      col = jidx % 1748 + 2*1680;
-    }
-      } else if (raw_width == 3516) {
-    row = jidx / 1758;
-    col = jidx % 1758;
-    if (row >= raw_height) {
-      row -= raw_height;
-      col += 1758;
-    }
-      } else {
-    row = jidx / raw_width;
-    col = jidx % raw_width;
-      }
-      if ((unsigned) (row-top_margin) >= height) continue;
-      if ((unsigned) (col-left_margin) < width) {
-    BAYER(row-top_margin,col-left_margin) = val;
-    if (min > val) min = val;
-      } else
-    black += val;
+lossless_jpeg_load_raw(Image  const image) {
+
+    int jwide, jrow, jcol, val, jidx, i, row, col;
+    struct jhead jh;
+    int min=INT_MAX;
+
+    if (!ljpeg_start (ifp, &jh)) return;
+    jwide = jh.wide * jh.clrs;
+
+    for (jrow=0; jrow < jh.high; jrow++) {
+        ljpeg_row (ifp, &jh);
+        for (jcol=0; jcol < jwide; jcol++) {
+            val = curve[jh.row[jcol]];
+            jidx = jrow*jwide + jcol;
+            if (raw_width == 5108) {
+                i = jidx / (1680*jh.high);
+                if (i < 2) {
+                    row = jidx / 1680 % jh.high;
+                    col = jidx % 1680 + i*1680;
+                } else {
+                    jidx -= 2*1680*jh.high;
+                    row = jidx / 1748;
+                    col = jidx % 1748 + 2*1680;
+                }
+            } else if (raw_width == 3516) {
+                row = jidx / 1758;
+                col = jidx % 1758;
+                if (row >= raw_height) {
+                    row -= raw_height;
+                    col += 1758;
+                }
+            } else {
+                row = jidx / raw_width;
+                col = jidx % raw_width;
+            }
+            if ((unsigned) (row-top_margin) >= height) continue;
+            if ((unsigned) (col-left_margin) < width) {
+                BAYER(row-top_margin,col-left_margin) = val;
+                if (min > val) min = val;
+            } else
+                black += val;
+        }
     }
-  }
-  free (jh.row);
-  if (raw_width > width)
-    black /= (raw_width - width) * height;
-  if (!strcasecmp(make,"KODAK"))
-    black = min;
+    free (jh.row);
+    if (raw_width > width)
+        black /= (raw_width - width) * height;
+    if (!strcasecmp(make,"KODAK"))
+        black = min;
 }
 
 
diff --git a/converter/other/cameratopam/ljpeg.h b/converter/other/cameratopam/ljpeg.h
index 60832a3d..9d9d8ee9 100644
--- a/converter/other/cameratopam/ljpeg.h
+++ b/converter/other/cameratopam/ljpeg.h
@@ -1,17 +1,21 @@
+#include "camera.h"
+
 struct jhead {
   int bits, high, wide, clrs, vpred[4];
   struct decode *huff[4];
   unsigned short *row;
 };
 
-void  
-lossless_jpeg_load_raw(void);
+LoadRawFn lossless_jpeg_load_raw;
 
 int  
-ljpeg_start (FILE * ifp, struct jhead *jh);
+ljpeg_start (FILE *         const ifP,
+             struct jhead * const jhP);
 
 int 
-ljpeg_diff (struct decode *dindex);
+ljpeg_diff (FILE *          const ifP,
+            struct decode * const dindexP);
 
 void
-ljpeg_row (struct jhead *jh);
+ljpeg_row(FILE *         const ifP,
+          struct jhead * const jhP);
diff --git a/converter/other/exif.c b/converter/other/exif.c
index f9592d6c..1bfe4b2b 100644
--- a/converter/other/exif.c
+++ b/converter/other/exif.c
@@ -21,6 +21,7 @@
   See the EXIF specs at http://exif.org (2001.09.01).
 
 --------------------------------------------------------------------------*/
+#include "pm_config.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <math.h>
@@ -30,7 +31,7 @@
 #include <limits.h>
 #include <ctype.h>
 
-#ifdef _WIN32
+#if MSVCRT
     #include <sys/utime.h>
 #else
     #include <utime.h>
@@ -45,22 +46,21 @@
 
 #include "exif.h"
 
-static unsigned char * LastExifRefd;
-static unsigned char * DirWithThumbnailPtrs;
+static const unsigned char * DirWithThumbnailPtrs;
 static double FocalplaneXRes;
 bool HaveXRes;
 static double FocalplaneUnits;
 static int ExifImageWidth;
-static int MotorolaOrder = 0;
 
 typedef struct {
     unsigned short Tag;
     const char * Desc;
-}TagTable_t;
+} TagTable;
+
 
 
 /* Describes format descriptor */
-static int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
+static int const bytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
 #define NUM_FORMATS 12
 
 #define FMT_BYTE       1 
@@ -119,7 +119,7 @@ static int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
 #define TAG_THUMBNAIL_OFFSET  0x0201
 #define TAG_THUMBNAIL_LENGTH  0x0202
 
-static TagTable_t const TagTable[] = {
+static TagTable const tagTable[] = {
   {   0x100,   "ImageWidth"},
   {   0x101,   "ImageLength"},
   {   0x102,   "BitsPerSample"},
@@ -207,498 +207,583 @@ static TagTable_t const TagTable[] = {
 
 
 
+typedef enum { NORMAL, MOTOROLA } ByteOrder;
+
+
+
+static uint16_t
+get16u(const void * const data,
+       ByteOrder    const byteOrder) {
 /*--------------------------------------------------------------------------
    Convert a 16 bit unsigned value from file's native byte order
 --------------------------------------------------------------------------*/
-static int Get16u(void * Short)
-{
-    if (MotorolaOrder){
-        return (((unsigned char *)Short)[0] << 8) | 
-            ((unsigned char *)Short)[1];
+    if (byteOrder == MOTOROLA){
+        return (((const unsigned char *)data)[0] << 8) | 
+            ((const unsigned char *)data)[1];
     }else{
-        return (((unsigned char *)Short)[1] << 8) | 
-            ((unsigned char *)Short)[0];
+        return (((const unsigned char *)data)[1] << 8) | 
+            ((const unsigned char *)data)[0];
     }
 }
 
+
+
+static int32_t
+get32s(const void * const data,
+       ByteOrder    const byteOrder) {
 /*--------------------------------------------------------------------------
    Convert a 32 bit signed value from file's native byte order
 --------------------------------------------------------------------------*/
-static int Get32s(void * Long)
-{
-    if (MotorolaOrder){
+    if (byteOrder == MOTOROLA){
         return  
-            ((( char *)Long)[0] << 24) | (((unsigned char *)Long)[1] << 16) |
-            (((unsigned char *)Long)[2] << 8 ) | 
-            (((unsigned char *)Long)[3] << 0 );
-    }else{
+            (((const char *)data)[0] << 24) |
+            (((const unsigned char *)data)[1] << 16) |
+            (((const unsigned char *)data)[2] << 8 ) | 
+            (((const unsigned char *)data)[3] << 0 );
+    } else {
         return  
-            ((( char *)Long)[3] << 24) | (((unsigned char *)Long)[2] << 16) |
-            (((unsigned char *)Long)[1] << 8 ) | 
-            (((unsigned char *)Long)[0] << 0 );
+            (((const char *)data)[3] << 24) |
+            (((const unsigned char *)data)[2] << 16) |
+            (((const unsigned char *)data)[1] << 8 ) | 
+            (((const unsigned char *)data)[0] << 0 );
     }
 }
 
+
+
+static uint32_t
+get32u(const void * const data,
+       ByteOrder    const byteOrder) {
 /*--------------------------------------------------------------------------
    Convert a 32 bit unsigned value from file's native byte order
 --------------------------------------------------------------------------*/
-static unsigned Get32u(void * Long)
-{
-    return (unsigned)Get32s(Long) & 0xffffffff;
+    return (uint32_t)get32s(data, byteOrder) & 0xffffffff;
 }
 
+
+
+static void
+printFormatNumber(FILE *       const fileP, 
+                  const void * const ValuePtr, 
+                  int          const Format,
+                  int          const ByteCount,
+                  ByteOrder    const byteOrder) {
 /*--------------------------------------------------------------------------
    Display a number as one of its many formats
 --------------------------------------------------------------------------*/
-static void PrintFormatNumber(FILE * const file, 
-                              void * const ValuePtr, 
-                              int const Format, int const ByteCount)
-{
     switch(Format){
     case FMT_SBYTE:
-    case FMT_BYTE:      printf("%02x\n",*(unsigned char *)ValuePtr); break;
-    case FMT_USHORT:    fprintf(file, "%d\n",Get16u(ValuePtr));    break;
+    case FMT_BYTE:
+        fprintf(fileP, "%02x\n", *(unsigned char *)ValuePtr);
+        break;
+    case FMT_USHORT:
+        fprintf(fileP, "%d\n",get16u(ValuePtr, byteOrder));
+        break;
     case FMT_ULONG:     
-    case FMT_SLONG:     fprintf(file, "%d\n",Get32s(ValuePtr));    break;
+    case FMT_SLONG:
+        fprintf(fileP, "%d\n",get32s(ValuePtr, byteOrder));
+        break;
     case FMT_SSHORT:    
-        fprintf(file, "%hd\n",(signed short)Get16u(ValuePtr));     break;
+        fprintf(fileP, "%hd\n",(signed short)get16u(ValuePtr, byteOrder));
+        break;
     case FMT_URATIONAL:
     case FMT_SRATIONAL: 
-        fprintf(file, "%d/%d\n",Get32s(ValuePtr), Get32s(4+(char *)ValuePtr));
+        fprintf(fileP, "%d/%d\n",get32s(ValuePtr, byteOrder),
+                get32s(4+(char *)ValuePtr, byteOrder));
         break;
     case FMT_SINGLE:    
-        fprintf(file, "%f\n",(double)*(float *)ValuePtr);          break;
-    case FMT_DOUBLE:    fprintf(file, "%f\n",*(double *)ValuePtr); break;
+        fprintf(fileP, "%f\n",(double)*(float *)ValuePtr);
+        break;
+    case FMT_DOUBLE:
+        fprintf(fileP, "%f\n",*(double *)ValuePtr);
+        break;
     default: 
-        fprintf(file, "Unknown format %d:", Format);
+        fprintf(fileP, "Unknown format %d:", Format);
         {
-            int a;
-            for (a=0; a < ByteCount && a < 16; ++a)
+            unsigned int a;
+            for (a = 0; a < ByteCount && a < 16; ++a)
                 printf("%02x", ((unsigned char *)ValuePtr)[a]);
         }
-        fprintf(file, "\n");
+        fprintf(fileP, "\n");
     }
 }
 
 
+
+static double
+convertAnyFormat(const void * const ValuePtr,
+                 int          const Format,
+                 ByteOrder    const byteOrder) {
 /*--------------------------------------------------------------------------
    Evaluate number, be it int, rational, or float from directory.
 --------------------------------------------------------------------------*/
-static double ConvertAnyFormat(void * ValuePtr, int Format)
-{
     double Value;
     Value = 0;
 
     switch(Format){
-        case FMT_SBYTE:     Value = *(signed char *)ValuePtr;  break;
-        case FMT_BYTE:      Value = *(unsigned char *)ValuePtr;        break;
-
-        case FMT_USHORT:    Value = Get16u(ValuePtr);          break;
-        case FMT_ULONG:     Value = Get32u(ValuePtr);          break;
-
-        case FMT_URATIONAL:
-        case FMT_SRATIONAL: 
-            {
-                int Num,Den;
-                Num = Get32s(ValuePtr);
-                Den = Get32s(4+(char *)ValuePtr);
-                if (Den == 0){
-                    Value = 0;
-                }else{
-                    Value = (double)Num/Den;
-                }
-                break;
-            }
-
-        case FMT_SSHORT:    Value = (signed short)Get16u(ValuePtr);  break;
-        case FMT_SLONG:     Value = Get32s(ValuePtr);                break;
+    case FMT_SBYTE:
+        Value = *(signed char *)ValuePtr;
+        break;
+    case FMT_BYTE:
+        Value = *(unsigned char *)ValuePtr;
+        break;
+    case FMT_USHORT:
+        Value = get16u(ValuePtr, byteOrder);
+        break;
+    case FMT_ULONG:
+        Value = get32u(ValuePtr, byteOrder);
+        break;
+    case FMT_URATIONAL:
+    case FMT_SRATIONAL: {
+        int num, den;
+        num = get32s(ValuePtr, byteOrder);
+        den = get32s(4+(char *)ValuePtr, byteOrder);
+        Value = den == 0 ? 0 : (double)(num/den);
+    } break;
+    case FMT_SSHORT:
+        Value = (signed short)get16u(ValuePtr, byteOrder);
+        break;
+    case FMT_SLONG:
+        Value = get32s(ValuePtr, byteOrder);
+        break;
 
-        /* Not sure if this is correct (never seen float used in Exif format)
-         */
-        case FMT_SINGLE:    Value = (double)*(float *)ValuePtr;      break;
-        case FMT_DOUBLE:    Value = *(double *)ValuePtr;             break;
+    /* Not sure if this is correct (never seen float used in Exif format) */
+    case FMT_SINGLE:
+        Value = (double)*(float *)ValuePtr;
+        break;
+    case FMT_DOUBLE:
+        Value = *(double *)ValuePtr;
+        break;
     }
     return Value;
 }
 
-/*--------------------------------------------------------------------------
-   Process one of the nested EXIF directories.
---------------------------------------------------------------------------*/
-static void 
-ProcessExifDir(unsigned char *  const ExifData, 
-               unsigned int     const ExifLength,
-               unsigned int     const DirOffset,
-               ImageInfo_t *    const ImageInfoP, 
-               int              const ShowTags,
-               unsigned char ** const LastExifRefdP) {
-
-    unsigned char * const DirStart = ExifData + DirOffset;
-    int de;
-    int a;
-    int NumDirEntries;
-    unsigned ThumbnailOffset = 0;
-    unsigned ThumbnailSize = 0;
-
-    NumDirEntries = Get16u(DirStart);
-    #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
 
-    {
-        unsigned char * DirEnd;
-        DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries);
-        if (DirEnd+4 > (ExifData+ExifLength)){
-            if (DirEnd+2 == ExifData+ExifLength || 
-                DirEnd == ExifData+ExifLength){
-                /* Version 1.3 of jhead would truncate a bit too much.
-                   This also caught later on as well.
-                */
-            }else{
-                /* Note: Files that had thumbnails trimmed with jhead
-                   1.3 or earlier might trigger this.
+
+static void
+traceTag(int                   const tag,
+         int                   const format,
+         const unsigned char * const valuePtr,
+         unsigned int          const byteCount,
+         ByteOrder             const byteOrder) {
+             
+    /* Show tag name */
+    unsigned int a;
+    bool found;
+    for (a = 0, found = false; !found; ++a){
+        if (tagTable[a].Tag == 0){
+            fprintf(stderr, "  Unknown Tag %04x Value = ", tag);
+            found = true;
+        }
+        if (tagTable[a].Tag == tag){
+            fprintf(stderr, "    %s = ",tagTable[a].Desc);
+            found = true;
+        }
+    }
+
+    /* Show tag value. */
+    switch(format){
+
+    case FMT_UNDEFINED:
+        /* Undefined is typically an ascii string. */
+
+    case FMT_STRING: {
+        /* String arrays printed without function call
+           (different from int arrays)
+        */
+        bool noPrint;
+        printf("\"");
+        for (a = 0, noPrint = false; a < byteCount; ++a){
+            if (ISPRINT((valuePtr)[a])){
+                fprintf(stderr, "%c", valuePtr[a]);
+                noPrint = false;
+            } else {
+                /* Avoiding indicating too many unprintable characters of
+                   proprietary bits of binary information this program may not
+                   know how to parse.
                 */
-                pm_message("Illegal directory entry size");
-                return;
+                if (!noPrint){
+                    fprintf(stderr, "?");
+                    noPrint = true;
+                }
             }
         }
-        if (DirEnd > LastExifRefd) LastExifRefd = DirEnd;
+        fprintf(stderr, "\"\n");
+    } break;
+
+    default:
+        /* Handle arrays of numbers later (will there ever be?)*/
+        printFormatNumber(stderr, valuePtr, format, byteCount, byteOrder);
     }
+}
+
+
+
+/* Forward declaration for recursion */
+
+static void 
+processExifDir(const unsigned char *  const ExifData, 
+               unsigned int           const ExifLength,
+               unsigned int           const DirOffset,
+               exif_ImageInfo *       const imageInfoP, 
+               ByteOrder              const byteOrder,
+               bool                   const wantTagTrace,
+               const unsigned char ** const LastExifRefdP);
+
+
+static void
+processDirEntry(const unsigned char *  const dirEntry,
+                const unsigned char *  const exifData,
+                unsigned int           const exifLength,
+                ByteOrder              const byteOrder,
+                bool                   const wantTagTrace,
+                exif_ImageInfo *       const imageInfoP, 
+                unsigned int *         const thumbnailOffsetP,
+                unsigned int *         const thumbnailSizeP,
+                bool *                 const haveThumbnailP,
+                const unsigned char ** const lastExifRefdP) {
+
+    int const tag        = get16u(&dirEntry[0], byteOrder);
+    int const format     = get16u(&dirEntry[2], byteOrder);
+    int const components = get32u(&dirEntry[4], byteOrder);
+
+    const unsigned char * valuePtr;
+        /* This actually can point to a variety of things; it must be cast to
+           other types when used.  But we use it as a byte-by-byte cursor, so
+           we declare it as a pointer to a generic byte here.
+        */
+    unsigned int byteCount;
 
-    if (ShowTags){
-        pm_message("Directory with %d entries",NumDirEntries);
+    if ((format-1) >= NUM_FORMATS) {
+        /* (-1) catches illegal zero case as unsigned underflows
+           to positive large.  
+        */
+        pm_message("Illegal number format %d for tag %04x", format, tag);
+        return;
+    }
+        
+    byteCount = components * bytesPerFormat[format];
+
+    if (byteCount > 4){
+        unsigned const offsetVal = get32u(&dirEntry[8], byteOrder);
+        /* If its bigger than 4 bytes, the dir entry contains an offset.*/
+        if (offsetVal + byteCount > exifLength){
+            /* Bogus pointer offset and / or bytecount value */
+            pm_message("Illegal pointer offset value in EXIF "
+                       "for tag %04x.  "
+                       "Offset %d bytes %d ExifLen %d\n",
+                       tag, offsetVal, byteCount, exifLength);
+            return;
+        }
+        valuePtr = &exifData[offsetVal];
+    } else {
+        /* 4 bytes or less and value is in the dir entry itself */
+        valuePtr = &dirEntry[8];
     }
 
-    for (de=0;de<NumDirEntries;de++){
-        int Tag, Format, Components;
-        unsigned char * ValuePtr;
-            /* This actually can point to a variety of things; it must
-               be cast to other types when used.  But we use it as a
-               byte-by-byte cursor, so we declare it as a pointer to a
-               generic byte here.  
-            */
-        int ByteCount;
-        unsigned char * DirEntry;
-        DirEntry = DIR_ENTRY_ADDR(DirStart, de);
+    if (*lastExifRefdP < valuePtr + byteCount){
+        /* Keep track of last byte in the exif header that was actually
+           referenced.  That way, we know where the discardable thumbnail data
+           begins.
+        */
+        *lastExifRefdP = valuePtr + byteCount;
+    }
 
-        Tag = Get16u(DirEntry);
-        Format = Get16u(DirEntry+2);
-        Components = Get32u(DirEntry+4);
+    if (wantTagTrace)
+        traceTag(tag, format, valuePtr, byteCount, byteOrder);
 
-        if ((Format-1) >= NUM_FORMATS) {
-            /* (-1) catches illegal zero case as unsigned underflows
-               to positive large.  
-            */
-            pm_message("Illegal number format %d for tag %04x", Format, Tag);
-            continue;
+    *haveThumbnailP = (tag == TAG_THUMBNAIL_OFFSET);
+
+    /* Extract useful components of tag */
+    switch (tag){
+
+    case TAG_MAKE:
+        STRSCPY(imageInfoP->CameraMake, (const char*)valuePtr);
+        break;
+
+    case TAG_MODEL:
+        STRSCPY(imageInfoP->CameraModel, (const char*)valuePtr);
+        break;
+
+    case TAG_XRESOLUTION:
+        imageInfoP->XResolution = 
+            convertAnyFormat(valuePtr, format, byteOrder);
+        break;
+
+    case TAG_YRESOLUTION:
+        imageInfoP->YResolution = 
+            convertAnyFormat(valuePtr, format, byteOrder);
+        break;
+
+    case TAG_DATETIME_ORIGINAL:
+        STRSCPY(imageInfoP->DateTime, (const char*)valuePtr);
+        imageInfoP->DatePointer = (const char*)valuePtr;
+        break;
+
+    case TAG_USERCOMMENT: {
+        /* Olympus has this padded with trailing spaces.  We stop the copy
+           where those start.
+        */
+        const char * const value = (const char *)valuePtr;
+
+        unsigned int cursor;
+        unsigned int outCursor;
+        unsigned int end;
+
+        for (end = byteCount; end > 0 && value[end] == ' '; --end);
+
+        /* Skip "ASCII" if it is there */
+        if (end >= 5 && MEMEQ(value, "ASCII", 5))
+            cursor = 5;
+        else
+            cursor = 0;
+
+        /* Skip consecutive blanks and NULs */
+
+        for (;
+             cursor < byteCount && 
+                 (value[cursor] == '\0' || value[cursor] == ' ');
+             ++cursor);
+
+        /* Copy the rest as the comment */
+
+        for (outCursor = 0;
+             cursor < end && outCursor < MAX_COMMENT-1;
+             ++cursor)
+            imageInfoP->Comments[outCursor++] = value[cursor];
+
+        imageInfoP->Comments[outCursor++] = '\0';
+    } break;
+
+    case TAG_FNUMBER:
+        /* Simplest way of expressing aperture, so I trust it the most.
+           (overwrite previously computd value if there is one)
+        */
+        imageInfoP->ApertureFNumber = 
+            (float)convertAnyFormat(valuePtr, format, byteOrder);
+        break;
+
+    case TAG_APERTURE:
+    case TAG_MAXAPERTURE:
+        /* More relevant info always comes earlier, so only use this field if
+           we don't have appropriate aperture information yet.
+        */
+        if (imageInfoP->ApertureFNumber == 0){
+            imageInfoP->ApertureFNumber = (float)
+                exp(convertAnyFormat(valuePtr, format, byteOrder)
+                    * log(2) * 0.5);
         }
-        
-        ByteCount = Components * BytesPerFormat[Format];
-
-        if (ByteCount > 4){
-            unsigned OffsetVal;
-            OffsetVal = Get32u(DirEntry+8);
-            /* If its bigger than 4 bytes, the dir entry contains an offset.*/
-            if (OffsetVal+ByteCount > ExifLength){
-                /* Bogus pointer offset and / or bytecount value */
-                pm_message("Illegal pointer offset value in EXIF "
-                           "for tag %04x.  "
-                           "Offset %d bytes %d ExifLen %d\n",
-                           Tag, OffsetVal, ByteCount, ExifLength);
-                continue;
-            }
-            ValuePtr = ExifData+OffsetVal;
+        break;
+
+    case TAG_FOCALLENGTH:
+        /* Nice digital cameras actually save the focal length
+           as a function of how farthey are zoomed in. 
+        */
+
+        imageInfoP->FocalLength = 
+            (float)convertAnyFormat(valuePtr, format, byteOrder);
+        break;
+
+    case TAG_SUBJECT_DISTANCE:
+        /* Inidcates the distacne the autofocus camera is focused to.
+           Tends to be less accurate as distance increases.
+        */
+        imageInfoP->Distance = 
+            (float)convertAnyFormat(valuePtr, format, byteOrder);
+        break;
+
+    case TAG_EXPOSURETIME:
+        /* Simplest way of expressing exposure time, so I
+           trust it most.  (overwrite previously computd value
+           if there is one) 
+        */
+        imageInfoP->ExposureTime = 
+            (float)convertAnyFormat(valuePtr, format, byteOrder);
+        break;
+
+    case TAG_SHUTTERSPEED:
+        /* More complicated way of expressing exposure time,
+           so only use this value if we don't already have it
+           from somewhere else.  
+        */
+        if (imageInfoP->ExposureTime == 0){
+            imageInfoP->ExposureTime = (float)
+                (1/exp(convertAnyFormat(valuePtr, format, byteOrder)
+                       * log(2)));
+        }
+        break;
+
+    case TAG_FLASH:
+        if ((int)convertAnyFormat(valuePtr, format, byteOrder) & 0x7){
+            imageInfoP->FlashUsed = TRUE;
         }else{
-            /* 4 bytes or less and value is in the dir entry itself */
-            ValuePtr = DirEntry+8;
+            imageInfoP->FlashUsed = FALSE;
         }
+        break;
 
-        if (*LastExifRefdP < ValuePtr+ByteCount){
-            /* Keep track of last byte in the exif header that was
-               actually referenced.  That way, we know where the
-               discardable thumbnail data begins.
-            */
-            *LastExifRefdP = ValuePtr+ByteCount;
+    case TAG_ORIENTATION:
+        imageInfoP->Orientation = 
+            (int)convertAnyFormat(valuePtr, format, byteOrder);
+        if (imageInfoP->Orientation < 1 || 
+            imageInfoP->Orientation > 8){
+            pm_message("Undefined rotation value %d",
+                       imageInfoP->Orientation);
+            imageInfoP->Orientation = 0;
         }
+        break;
 
-        if (ShowTags){
-            /* Show tag name */
-            for (a=0;;a++){
-                if (TagTable[a].Tag == 0){
-                    fprintf(stderr, "  Unknown Tag %04x Value = ", Tag);
-                    break;
-                }
-                if (TagTable[a].Tag == Tag){
-                    fprintf(stderr, "    %s = ",TagTable[a].Desc);
-                    break;
-                }
-            }
+    case TAG_EXIF_IMAGELENGTH:
+    case TAG_EXIF_IMAGEWIDTH:
+        /* Use largest of height and width to deal with images
+           that have been rotated to portrait format.  
+        */
+        ExifImageWidth =
+            MIN(ExifImageWidth,
+                (int)convertAnyFormat(valuePtr, format, byteOrder));
+        break;
 
-            /* Show tag value. */
-            switch(Format){
-
-                case FMT_UNDEFINED:
-                    /* Undefined is typically an ascii string. */
-
-                case FMT_STRING:
-                    /* String arrays printed without function call
-                       (different from int arrays)
-                    */
-                    {
-                        int NoPrint = 0;
-                        printf("\"");
-                        for (a=0;a<ByteCount;a++){
-                            if (ISPRINT((ValuePtr)[a])){
-                                fprintf(stderr, "%c", (ValuePtr)[a]);
-                                NoPrint = 0;
-                            }else{
-
-                                /* Avoiding indicating too many
-                                   unprintable characters of proprietary
-                                   bits of binary information this
-                                   program may not know how to parse.  
-                                */
-                                if (!NoPrint){
-                                    fprintf(stderr, "?");
-                                    NoPrint = 1;
-                                }
-                            }
-                        }
-                        fprintf(stderr, "\"\n");
-                    }
-                    break;
+    case TAG_FOCALPLANEXRES:
+        HaveXRes = TRUE;
+        FocalplaneXRes = convertAnyFormat(valuePtr, format, byteOrder);
+        break;
 
-                default:
-                    /* Handle arrays of numbers later (will there ever be?)*/
-                    PrintFormatNumber(stderr, ValuePtr, Format, ByteCount);
-            }
+    case TAG_FOCALPLANEUNITS:
+        switch((int)convertAnyFormat(valuePtr, format, byteOrder)){
+        case 1: FocalplaneUnits = 25.4; break; /* 1 inch */
+        case 2: 
+            /* According to the information I was using, 2
+               means meters.  But looking at the Cannon
+               powershot's files, inches is the only
+               sensible value.  
+            */
+            FocalplaneUnits = 25.4;
+            break;
+
+        case 3: FocalplaneUnits = 10;   break;  /* 1 centimeter*/
+        case 4: FocalplaneUnits = 1;    break;  /* 1 millimeter*/
+        case 5: FocalplaneUnits = .001; break;  /* 1 micrometer*/
         }
+        break;
 
-        /* Extract useful components of tag */
-        switch(Tag){
-
-            case TAG_MAKE:
-                STRSCPY(ImageInfoP->CameraMake, (char*)ValuePtr);
-                break;
-
-            case TAG_MODEL:
-                STRSCPY(ImageInfoP->CameraModel, (char*)ValuePtr);
-                break;
-
-            case TAG_XRESOLUTION:
-                ImageInfoP->XResolution = 
-                    ConvertAnyFormat(ValuePtr, Format);
-                break;
-    
-            case TAG_YRESOLUTION:
-                ImageInfoP->YResolution = 
-                    ConvertAnyFormat(ValuePtr, Format);
-                break;
-    
-            case TAG_DATETIME_ORIGINAL:
-                STRSCPY(ImageInfoP->DateTime, (char*)ValuePtr);
-                ImageInfoP->DatePointer = (char*)ValuePtr;
-                break;
-
-            case TAG_USERCOMMENT:
-                /* Olympus has this padded with trailing spaces.
-                   Remove these first. 
-                */
-                for (a=ByteCount;;){
-                    a--;
-                    if (((char*)ValuePtr)[a] == ' '){
-                        ((char*)ValuePtr)[a] = '\0';
-                    }else{
-                        break;
-                    }
-                    if (a == 0) break;
-                }
+        /* Remaining cases contributed by: Volker C. Schoech
+           (schoech@gmx.de)
+        */
 
-                /* Copy the comment */
-                if (memcmp(ValuePtr, "ASCII",5) == 0){
-                    for (a=5;a<10;a++){
-                        char c;
-                        c = ((char*)ValuePtr)[a];
-                        if (c != '\0' && c != ' '){
-                            strncpy(ImageInfoP->Comments, (char*)ValuePtr+a, 
-                                    199);
-                            break;
-                        }
-                    }
-                    
-                }else{
-                    strncpy(ImageInfoP->Comments, (char*)ValuePtr, 199);
-                }
-                break;
-
-            case TAG_FNUMBER:
-                /* Simplest way of expressing aperture, so I trust it the most.
-                   (overwrite previously computd value if there is one)
-                   */
-                ImageInfoP->ApertureFNumber = 
-                    (float)ConvertAnyFormat(ValuePtr, Format);
-                break;
-
-            case TAG_APERTURE:
-            case TAG_MAXAPERTURE:
-                /* More relevant info always comes earlier, so only
-                 use this field if we don't have appropriate aperture
-                 information yet. 
-                */
-                if (ImageInfoP->ApertureFNumber == 0){
-                    ImageInfoP->ApertureFNumber = (float)
-                        exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5);
-                }
-                break;
+    case TAG_EXPOSURE_BIAS:
+        imageInfoP->ExposureBias = 
+            (float) convertAnyFormat(valuePtr, format, byteOrder);
+        break;
 
-            case TAG_FOCALLENGTH:
-                /* Nice digital cameras actually save the focal length
-                   as a function of how farthey are zoomed in. 
-                */
+    case TAG_WHITEBALANCE:
+        imageInfoP->Whitebalance = 
+            (int)convertAnyFormat(valuePtr, format, byteOrder);
+        break;
 
-                ImageInfoP->FocalLength = 
-                    (float)ConvertAnyFormat(ValuePtr, Format);
-                break;
+    case TAG_METERING_MODE:
+        imageInfoP->MeteringMode = 
+            (int)convertAnyFormat(valuePtr, format, byteOrder);
+        break;
 
-            case TAG_SUBJECT_DISTANCE:
-                /* Inidcates the distacne the autofocus camera is focused to.
-                   Tends to be less accurate as distance increases.
-                */
-                ImageInfoP->Distance = 
-                    (float)ConvertAnyFormat(ValuePtr, Format);
-                break;
-
-            case TAG_EXPOSURETIME:
-                /* Simplest way of expressing exposure time, so I
-                   trust it most.  (overwrite previously computd value
-                   if there is one) 
-                */
-                ImageInfoP->ExposureTime = 
-                    (float)ConvertAnyFormat(ValuePtr, Format);
-                break;
-
-            case TAG_SHUTTERSPEED:
-                /* More complicated way of expressing exposure time,
-                   so only use this value if we don't already have it
-                   from somewhere else.  
-                */
-                if (ImageInfoP->ExposureTime == 0){
-                    ImageInfoP->ExposureTime = (float)
-                        (1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2)));
-                }
-                break;
+    case TAG_EXPOSURE_PROGRAM:
+        imageInfoP->ExposureProgram = 
+            (int)convertAnyFormat(valuePtr, format, byteOrder);
+        break;
 
-            case TAG_FLASH:
-                if ((int)ConvertAnyFormat(ValuePtr, Format) & 7){
-                    ImageInfoP->FlashUsed = TRUE;
-                }else{
-                    ImageInfoP->FlashUsed = FALSE;
-                }
-                break;
-
-            case TAG_ORIENTATION:
-                ImageInfoP->Orientation = 
-                    (int)ConvertAnyFormat(ValuePtr, Format);
-                if (ImageInfoP->Orientation < 1 || 
-                    ImageInfoP->Orientation > 8){
-                    pm_message("Undefined rotation value %d", 
-                               ImageInfoP->Orientation);
-                    ImageInfoP->Orientation = 0;
-                }
-                break;
+    case TAG_ISO_EQUIVALENT:
+        imageInfoP->ISOequivalent = 
+            (int)convertAnyFormat(valuePtr, format, byteOrder);
+        if ( imageInfoP->ISOequivalent < 50 ) 
+            imageInfoP->ISOequivalent *= 200;
+        break;
 
-            case TAG_EXIF_IMAGELENGTH:
-            case TAG_EXIF_IMAGEWIDTH:
-                /* Use largest of height and width to deal with images
-                   that have been rotated to portrait format.  
-                */
-                a = (int)ConvertAnyFormat(ValuePtr, Format);
-                if (ExifImageWidth < a) ExifImageWidth = a;
-                break;
-
-            case TAG_FOCALPLANEXRES:
-                HaveXRes = TRUE;
-                FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format);
-                break;
-
-            case TAG_FOCALPLANEUNITS:
-                switch((int)ConvertAnyFormat(ValuePtr, Format)){
-                    case 1: FocalplaneUnits = 25.4; break; /* 1 inch */
-                    case 2: 
-                        /* According to the information I was using, 2
-                           means meters.  But looking at the Cannon
-                           powershot's files, inches is the only
-                           sensible value.  
-                        */
-                        FocalplaneUnits = 25.4;
-                        break;
+    case TAG_COMPRESSION_LEVEL:
+        imageInfoP->CompressionLevel = 
+            (int)convertAnyFormat(valuePtr, format, byteOrder);
+        break;
 
-                    case 3: FocalplaneUnits = 10;   break;  /* 1 centimeter*/
-                    case 4: FocalplaneUnits = 1;    break;  /* 1 millimeter*/
-                    case 5: FocalplaneUnits = .001; break;  /* 1 micrometer*/
-                }
-                break;
+    case TAG_THUMBNAIL_OFFSET:
+        *thumbnailOffsetP = (unsigned int)
+            convertAnyFormat(valuePtr, format, byteOrder);
+        break;
 
-                /* Remaining cases contributed by: Volker C. Schoech
-                   (schoech@gmx.de)
-                */
+    case TAG_THUMBNAIL_LENGTH:
+        *thumbnailSizeP = (unsigned int)
+            convertAnyFormat(valuePtr, format, byteOrder);
+        break;
 
-            case TAG_EXPOSURE_BIAS:
-                ImageInfoP->ExposureBias = 
-                    (float) ConvertAnyFormat(ValuePtr, Format);
-                break;
-
-            case TAG_WHITEBALANCE:
-                ImageInfoP->Whitebalance = 
-                    (int)ConvertAnyFormat(ValuePtr, Format);
-                break;
-
-            case TAG_METERING_MODE:
-                ImageInfoP->MeteringMode = 
-                    (int)ConvertAnyFormat(ValuePtr, Format);
-                break;
-
-            case TAG_EXPOSURE_PROGRAM:
-                ImageInfoP->ExposureProgram = 
-                    (int)ConvertAnyFormat(ValuePtr, Format);
-                break;
-
-            case TAG_ISO_EQUIVALENT:
-                ImageInfoP->ISOequivalent = 
-                    (int)ConvertAnyFormat(ValuePtr, Format);
-                if ( ImageInfoP->ISOequivalent < 50 ) 
-                    ImageInfoP->ISOequivalent *= 200;
-                break;
-
-            case TAG_COMPRESSION_LEVEL:
-                ImageInfoP->CompressionLevel = 
-                    (int)ConvertAnyFormat(ValuePtr, Format);
-                break;
-
-            case TAG_THUMBNAIL_OFFSET:
-                ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
-                DirWithThumbnailPtrs = DirStart;
-                break;
-
-            case TAG_THUMBNAIL_LENGTH:
-                ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
-                break;
-
-            case TAG_EXIF_OFFSET:
-            case TAG_INTEROP_OFFSET:
-                {
-                    unsigned int const SubdirOffset  = Get32u(ValuePtr);
-                    if (SubdirOffset >= ExifLength)
-                        pm_message("Illegal exif or interop offset "
-                                   "directory link.  Offset is %u, "
-                                   "but Exif data is only %u bytes.",
-                                   SubdirOffset, ExifLength);
-                    else
-                        ProcessExifDir(ExifData, ExifLength, SubdirOffset, 
-                                       ImageInfoP, ShowTags, LastExifRefdP);
-                    continue;
-                }
-        }
+    case TAG_EXIF_OFFSET:
+    case TAG_INTEROP_OFFSET: {
+        unsigned int const subdirOffset = get32u(valuePtr, byteOrder);
+        if (subdirOffset >= exifLength)
+            pm_message("Illegal exif or interop offset "
+                       "directory link.  Offset is %u, "
+                       "but Exif data is only %u bytes.",
+                       subdirOffset, exifLength);
+        else
+            processExifDir(exifData, exifLength, subdirOffset, 
+                           imageInfoP, byteOrder, wantTagTrace,
+                           lastExifRefdP);
+    } break;
+    }
+}
+
+
+
+static void 
+processExifDir(const unsigned char *  const exifData, 
+               unsigned int           const exifLength,
+               unsigned int           const dirOffset,
+               exif_ImageInfo *       const imageInfoP, 
+               ByteOrder              const byteOrder,
+               bool                   const wantTagTrace,
+               const unsigned char ** const lastExifRefdP) {
+/*--------------------------------------------------------------------------
+   Process one of the nested EXIF directories.
+--------------------------------------------------------------------------*/
+    const unsigned char * const dirStart = exifData + dirOffset;
+    unsigned int const numDirEntries = get16u(&dirStart[0], byteOrder);
+    unsigned int de;
+    bool haveThumbnail;
+    unsigned int thumbnailOffset;
+    unsigned int thumbnailSize;
+
+    #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
 
+    {
+        const unsigned char * const dirEnd =
+            DIR_ENTRY_ADDR(dirStart, numDirEntries);
+        if (dirEnd + 4 > (exifData + exifLength)){
+            if (dirEnd + 2 == exifData + exifLength || 
+                dirEnd == exifData + exifLength){
+                /* Version 1.3 of jhead would truncate a bit too much.
+                   This also caught later on as well.
+                */
+            }else{
+                /* Note: Files that had thumbnails trimmed with jhead
+                   1.3 or earlier might trigger this.
+                */
+                pm_message("Illegal directory entry size");
+                return;
+            }
+        }
+        *lastExifRefdP = MAX(*lastExifRefdP, dirEnd);
     }
 
+    if (wantTagTrace)
+        pm_message("Directory with %d entries", numDirEntries);
+
+    haveThumbnail   = false;  /* initial value */
+    thumbnailOffset = 0;      /* initial value */
+    thumbnailSize   = 0;      /* initial value */
+
+    for (de = 0; de < numDirEntries; ++de)
+        processDirEntry(DIR_ENTRY_ADDR(dirStart, de), exifData, exifLength,
+                        byteOrder, wantTagTrace, imageInfoP,
+                        &thumbnailOffset, &thumbnailSize, &haveThumbnail,
+                        lastExifRefdP);
+
+    if (haveThumbnail)
+        DirWithThumbnailPtrs = dirStart;
 
     {
         /* In addition to linking to subdirectories via exif tags,
@@ -706,28 +791,30 @@ ProcessExifDir(unsigned char *  const ExifData,
            of each directory.  This has got to be the result of a
            committee!  
         */
-        if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= 
-            ExifData+ExifLength){
-            unsigned int const SubdirOffset =
-                Get32u(DirStart+2+12*NumDirEntries);
-            if (SubdirOffset){
-                unsigned char * const SubdirStart = ExifData + SubdirOffset;
-                if (SubdirStart > ExifData+ExifLength){
-                    if (SubdirStart < ExifData+ExifLength+20){
+        if (DIR_ENTRY_ADDR(dirStart, numDirEntries) + 4 <= 
+            exifData + exifLength){
+            unsigned int const subdirOffset =
+                get32u(dirStart + 2 + 12*numDirEntries, byteOrder);
+            if (subdirOffset){
+                const unsigned char * const subdirStart =
+                    exifData + subdirOffset;
+                if (subdirStart > exifData + exifLength){
+                    if (subdirStart < exifData + exifLength + 20){
                         /* Jhead 1.3 or earlier would crop the whole directory!
                            As Jhead produces this form of format incorrectness,
                            I'll just let it pass silently.
                         */
-                        if (ShowTags) 
+                        if (wantTagTrace) 
                             printf("Thumbnail removed with "
                                    "Jhead 1.3 or earlier\n");
                     }else{
                         pm_message("Illegal subdirectory link");
                     }
                 }else{
-                    if (SubdirOffset <= ExifLength)
-                        ProcessExifDir(ExifData, ExifLength, SubdirOffset,
-                                       ImageInfoP, ShowTags, LastExifRefdP);
+                    if (subdirOffset <= exifLength)
+                        processExifDir(exifData, exifLength, subdirOffset,
+                                       imageInfoP, byteOrder, wantTagTrace,
+                                       lastExifRefdP);
                 }
             }
         }else{
@@ -735,14 +822,14 @@ ProcessExifDir(unsigned char *  const ExifData,
         }
     }
 
-    if (ThumbnailSize && ThumbnailOffset){
-        if (ThumbnailSize + ThumbnailOffset <= ExifLength){
+    if (thumbnailSize && thumbnailOffset){
+        if (thumbnailSize + thumbnailOffset <= exifLength){
             /* The thumbnail pointer appears to be valid.  Store it. */
-            ImageInfoP->ThumbnailPointer = ExifData + ThumbnailOffset;
-            ImageInfoP->ThumbnailSize = ThumbnailSize;
+            imageInfoP->ThumbnailPointer = exifData + thumbnailOffset;
+            imageInfoP->ThumbnailSize = thumbnailSize;
 
-            if (ShowTags){
-                fprintf(stderr, "Thumbnail size: %d bytes\n",ThumbnailSize);
+            if (wantTagTrace){
+                fprintf(stderr, "Thumbnail size: %u bytes\n", thumbnailSize);
             }
         }
     }
@@ -751,56 +838,56 @@ ProcessExifDir(unsigned char *  const ExifData,
 
 
 void 
-process_EXIF(unsigned char * const ExifData,
-             unsigned int    const length,
-             ImageInfo_t *   const ImageInfoP, 
-             int             const ShowTags,
-             const char **   const errorP) {
+exif_parse(const unsigned char * const exifData,
+           unsigned int          const length,
+           exif_ImageInfo *      const imageInfoP, 
+           bool                  const wantTagTrace,
+           const char **         const errorP) {
 /*--------------------------------------------------------------------------
   Interpret an EXIF APP1 marker
 
-  'ExifData' is the actual Exif data; it does not include the
+  'exifData' is the actual Exif data; it does not include the
   "Exif" identifier and length field that often prefix Exif data.
 
   'length' is the length of the Exif section.
 --------------------------------------------------------------------------*/
+    ByteOrder byteOrder;
     int FirstOffset;
-    unsigned char * LastExifRefd;
+    const unsigned char * lastExifRefd;
 
     *errorP = NULL;  /* initial assumption */
 
-    if (ShowTags){
+    if (wantTagTrace)
         fprintf(stderr, "Exif header %d bytes long\n",length);
-    }
 
-    if (memcmp(ExifData+0,"II",2) == 0) {
-        if (ShowTags) 
+    if (MEMEQ(exifData + 0, "II" , 2)) {
+        if (wantTagTrace) 
             fprintf(stderr, "Exif header in Intel order\n");
-        MotorolaOrder = 0;
+        byteOrder = NORMAL;
     } else {
-        if (memcmp(ExifData+0, "MM", 2) == 0) {
-            if (ShowTags) 
+        if (MEMEQ(exifData + 0, "MM", 2)) {
+            if (wantTagTrace) 
                 fprintf(stderr, "Exif header in Motorola order\n");
-            MotorolaOrder = 1;
+            byteOrder = MOTOROLA;
         } else {
-            asprintfN(errorP, "Invalid alignment marker in Exif "
-                      "data.  First two bytes are '%c%c' (0x%02x%02x) "
-                      "instead of 'II' or 'MM'.", 
-                      ExifData[0], ExifData[1], ExifData[0], ExifData[1]);
+            pm_asprintf(errorP, "Invalid alignment marker in Exif "
+                        "data.  First two bytes are '%c%c' (0x%02x%02x) "
+                        "instead of 'II' or 'MM'.", 
+                        exifData[0], exifData[1], exifData[0], exifData[1]);
         }
     }
     if (!*errorP) {
-        unsigned short const start = Get16u(ExifData + 2);
+        unsigned short const start = get16u(exifData + 2, byteOrder);
         /* Check the next value for correctness. */
         if (start != 0x002a){
-            asprintfN(errorP, "Invalid Exif header start.  "
-                      "two bytes after the alignment marker "
-                      "should be 0x002a, but is 0x%04x",
-                      start);
+            pm_asprintf(errorP, "Invalid Exif header start.  "
+                        "two bytes after the alignment marker "
+                        "should be 0x002a, but is 0x%04x",
+                        start);
         }
     }
     if (!*errorP) {
-        FirstOffset = Get32u(ExifData + 4);
+        FirstOffset = get32u(exifData + 4, byteOrder);
         if (FirstOffset < 8 || FirstOffset > 16){
             /* I used to ensure this was set to 8 (website I used
                indicated its 8) but PENTAX Optio 230 has it set
@@ -809,51 +896,54 @@ process_EXIF(unsigned char * const ExifData,
             pm_message("Suspicious offset of first IFD value in Exif header");
         }
         
-        ImageInfoP->Comments[0] = '\0';  /* Initial value - null string */
+        imageInfoP->Comments[0] = '\0';  /* Initial value - null string */
         
         HaveXRes = FALSE;  /* Initial assumption */
         FocalplaneUnits = 0;
         ExifImageWidth = 0;
         
-        LastExifRefd = ExifData;
+        lastExifRefd = exifData;
         DirWithThumbnailPtrs = NULL;
         
-        ProcessExifDir(ExifData, length, FirstOffset, 
-                       ImageInfoP, ShowTags, &LastExifRefd);
+        processExifDir(exifData, length, FirstOffset, 
+                       imageInfoP, byteOrder, wantTagTrace, &lastExifRefd);
         
         /* Compute the CCD width, in millimeters. */
         if (HaveXRes){
-            ImageInfoP->HaveCCDWidth = 1;
-            ImageInfoP->CCDWidth = 
+            imageInfoP->HaveCCDWidth = 1;
+            imageInfoP->CCDWidth = 
                     (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes);
         } else
-            ImageInfoP->HaveCCDWidth = 0;
+            imageInfoP->HaveCCDWidth = 0;
             
-        if (ShowTags){
+        if (wantTagTrace){
             fprintf(stderr, 
-                    "Non-settings part of Exif header: %d bytes\n",
-                    ExifData+length-LastExifRefd);
+                    "Non-settings part of Exif header: %lu bytes\n",
+                    (unsigned long)(exifData + length - lastExifRefd));
         }
     }
 }
 
+
+
+void 
+exif_showImageInfo(const exif_ImageInfo * const imageInfoP,
+                   FILE *                 const fileP) {
 /*--------------------------------------------------------------------------
    Show the collected image info, displaying camera F-stop and shutter
    speed in a consistent and legible fashion.
 --------------------------------------------------------------------------*/
-void 
-ShowImageInfo(ImageInfo_t * const ImageInfoP)
-{
-    if (ImageInfoP->CameraMake[0]){
-        fprintf(stderr, "Camera make  : %s\n",ImageInfoP->CameraMake);
-        fprintf(stderr, "Camera model : %s\n",ImageInfoP->CameraModel);
+    if (imageInfoP->CameraMake[0]) {
+        fprintf(fileP, "Camera make  : %s\n", imageInfoP->CameraMake);
+        fprintf(fileP, "Camera model : %s\n", imageInfoP->CameraModel);
     }
-    if (ImageInfoP->DateTime[0]){
-        fprintf(stderr, "Date/Time    : %s\n",ImageInfoP->DateTime);
-    }
-    fprintf(stderr, "Resolution   : %f x %f\n",
-            ImageInfoP->XResolution, ImageInfoP->YResolution);
-    if (ImageInfoP->Orientation > 1){
+    if (imageInfoP->DateTime[0])
+        fprintf(fileP, "Date/Time    : %s\n", imageInfoP->DateTime);
+
+    fprintf(fileP, "Resolution   : %f x %f\n",
+            imageInfoP->XResolution, imageInfoP->YResolution);
+
+    if (imageInfoP->Orientation > 1) {
 
         /* Only print orientation if one was supplied, and if its not
            1 (normal orientation)
@@ -890,154 +980,144 @@ ShowImageInfo(ImageInfo_t * const ImageInfoP)
             "rotate 270",       /* rotate 270 to right it. */
         };
 
-        fprintf(stderr, "Orientation  : %s\n", 
-                OrientTab[ImageInfoP->Orientation]);
+        fprintf(fileP, "Orientation  : %s\n", 
+                OrientTab[imageInfoP->Orientation]);
     }
 
-    if (ImageInfoP->IsColor == 0){
-        fprintf(stderr, "Color/bw     : Black and white\n");
-    }
-    if (ImageInfoP->FlashUsed >= 0){
-        fprintf(stderr, "Flash used   : %s\n",
-                ImageInfoP->FlashUsed ? "Yes" :"No");
-    }
-    if (ImageInfoP->FocalLength){
-        fprintf(stderr, "Focal length : %4.1fmm",
-                (double)ImageInfoP->FocalLength);
-        if (ImageInfoP->HaveCCDWidth){
-            fprintf(stderr, "  (35mm equivalent: %dmm)",
+    if (imageInfoP->IsColor == 0)
+        fprintf(fileP, "Color/bw     : Black and white\n");
+
+    if (imageInfoP->FlashUsed >= 0)
+        fprintf(fileP, "Flash used   : %s\n",
+                imageInfoP->FlashUsed ? "Yes" :"No");
+
+    if (imageInfoP->FocalLength) {
+        fprintf(fileP, "Focal length : %4.1fmm",
+                (double)imageInfoP->FocalLength);
+        if (imageInfoP->HaveCCDWidth){
+            fprintf(fileP, "  (35mm equivalent: %dmm)",
                     (int)
-                    (ImageInfoP->FocalLength/ImageInfoP->CCDWidth*36 + 0.5));
+                    (imageInfoP->FocalLength/imageInfoP->CCDWidth*36 + 0.5));
         }
-        fprintf(stderr, "\n");
+        fprintf(fileP, "\n");
     }
 
-    if (ImageInfoP->HaveCCDWidth){
-        fprintf(stderr, "CCD width    : %2.4fmm\n",
-                (double)ImageInfoP->CCDWidth);
-    }
+    if (imageInfoP->HaveCCDWidth)
+        fprintf(fileP, "CCD width    : %2.4fmm\n",
+                (double)imageInfoP->CCDWidth);
 
-    if (ImageInfoP->ExposureTime){ 
-        if (ImageInfoP->ExposureTime < 0.010){
-            fprintf(stderr, 
+    if (imageInfoP->ExposureTime) {
+        if (imageInfoP->ExposureTime < 0.010){
+            fprintf(fileP, 
                     "Exposure time: %6.4f s ",
-                    (double)ImageInfoP->ExposureTime);
+                    (double)imageInfoP->ExposureTime);
         }else{
-            fprintf(stderr, 
+            fprintf(fileP, 
                     "Exposure time: %5.3f s ",
-                    (double)ImageInfoP->ExposureTime);
+                    (double)imageInfoP->ExposureTime);
         }
-        if (ImageInfoP->ExposureTime <= 0.5){
-            fprintf(stderr, " (1/%d)",(int)(0.5 + 1/ImageInfoP->ExposureTime));
+        if (imageInfoP->ExposureTime <= 0.5){
+            fprintf(fileP, " (1/%d)",(int)(0.5 + 1/imageInfoP->ExposureTime));
         }
-        fprintf(stderr, "\n");
+        fprintf(fileP, "\n");
     }
-    if (ImageInfoP->ApertureFNumber){
-        fprintf(stderr, "Aperture     : f/%3.1f\n",
-                (double)ImageInfoP->ApertureFNumber);
+    if (imageInfoP->ApertureFNumber){
+        fprintf(fileP, "Aperture     : f/%3.1f\n",
+                (double)imageInfoP->ApertureFNumber);
     }
-    if (ImageInfoP->Distance){
-        if (ImageInfoP->Distance < 0){
-            fprintf(stderr, "Focus dist.  : Infinite\n");
+    if (imageInfoP->Distance){
+        if (imageInfoP->Distance < 0){
+            fprintf(fileP, "Focus dist.  : Infinite\n");
         }else{
-            fprintf(stderr, "Focus dist.  :%5.2fm\n",
-                    (double)ImageInfoP->Distance);
+            fprintf(fileP, "Focus dist.  :%5.2fm\n",
+                    (double)imageInfoP->Distance);
         }
     }
 
-
-
-
-
-    if (ImageInfoP->ISOequivalent){ /* 05-jan-2001 vcs */
-        fprintf(stderr, "ISO equiv.   : %2d\n",(int)ImageInfoP->ISOequivalent);
+    if (imageInfoP->ISOequivalent){ /* 05-jan-2001 vcs */
+        fprintf(fileP, "ISO equiv.   : %2d\n",(int)imageInfoP->ISOequivalent);
     }
-    if (ImageInfoP->ExposureBias){ /* 05-jan-2001 vcs */
-        fprintf(stderr, "Exposure bias:%4.2f\n",
-                (double)ImageInfoP->ExposureBias);
+    if (imageInfoP->ExposureBias){ /* 05-jan-2001 vcs */
+        fprintf(fileP, "Exposure bias:%4.2f\n",
+                (double)imageInfoP->ExposureBias);
     }
         
-    if (ImageInfoP->Whitebalance){ /* 05-jan-2001 vcs */
-        switch(ImageInfoP->Whitebalance) {
+    if (imageInfoP->Whitebalance){ /* 05-jan-2001 vcs */
+        switch(imageInfoP->Whitebalance) {
         case 1:
-            fprintf(stderr, "Whitebalance : sunny\n");
+            fprintf(fileP, "Whitebalance : sunny\n");
             break;
         case 2:
-            fprintf(stderr, "Whitebalance : fluorescent\n");
+            fprintf(fileP, "Whitebalance : fluorescent\n");
             break;
         case 3:
-            fprintf(stderr, "Whitebalance : incandescent\n");
+            fprintf(fileP, "Whitebalance : incandescent\n");
             break;
         default:
-            fprintf(stderr, "Whitebalance : cloudy\n");
+            fprintf(fileP, "Whitebalance : cloudy\n");
         }
     }
-    if (ImageInfoP->MeteringMode){ /* 05-jan-2001 vcs */
-        switch(ImageInfoP->MeteringMode) {
+    if (imageInfoP->MeteringMode){ /* 05-jan-2001 vcs */
+        switch(imageInfoP->MeteringMode) {
         case 2:
-            fprintf(stderr, "Metering Mode: center weight\n");
+            fprintf(fileP, "Metering Mode: center weight\n");
             break;
         case 3:
-            fprintf(stderr, "Metering Mode: spot\n");
+            fprintf(fileP, "Metering Mode: spot\n");
             break;
         case 5:
-            fprintf(stderr, "Metering Mode: matrix\n");
+            fprintf(fileP, "Metering Mode: matrix\n");
             break;
         }
     }
-    if (ImageInfoP->ExposureProgram){ /* 05-jan-2001 vcs */
-        switch(ImageInfoP->ExposureProgram) {
+    if (imageInfoP->ExposureProgram){ /* 05-jan-2001 vcs */
+        switch(imageInfoP->ExposureProgram) {
         case 2:
-            fprintf(stderr, "Exposure     : program (auto)\n");
+            fprintf(fileP, "Exposure     : program (auto)\n");
             break;
         case 3:
-            fprintf(stderr, "Exposure     : aperture priority (semi-auto)\n");
+            fprintf(fileP, "Exposure     : aperture priority (semi-auto)\n");
             break;
         case 4:
-            fprintf(stderr, "Exposure     : shutter priority (semi-auto)\n");
+            fprintf(fileP, "Exposure     : shutter priority (semi-auto)\n");
             break;
         }
     }
-    if (ImageInfoP->CompressionLevel){ /* 05-jan-2001 vcs */
-        switch(ImageInfoP->CompressionLevel) {
+    if (imageInfoP->CompressionLevel){ /* 05-jan-2001 vcs */
+        switch(imageInfoP->CompressionLevel) {
         case 1:
-            fprintf(stderr, "Jpeg Quality  : basic\n");
+            fprintf(fileP, "Jpeg Quality  : basic\n");
             break;
         case 2:
-            fprintf(stderr, "Jpeg Quality  : normal\n");
+            fprintf(fileP, "Jpeg Quality  : normal\n");
             break;
         case 4:
-            fprintf(stderr, "Jpeg Quality  : fine\n");
+            fprintf(fileP, "Jpeg Quality  : fine\n");
             break;
        }
     }
 
-         
-
     /* Print the comment. Print 'Comment:' for each new line of comment. */
-    if (ImageInfoP->Comments[0]){
-        int a,c;
-        fprintf(stderr, "Comment      : ");
-        for (a=0;a<MAX_COMMENT;a++){
-            c = ImageInfoP->Comments[a];
-            if (c == '\0') break;
+    if (imageInfoP->Comments[0]) {
+        unsigned int a;
+
+        fprintf(fileP, "Comment      : ");
+
+        for (a = 0; a < MAX_COMMENT && imageInfoP->Comments[a]; ++a) {
+            char const c = imageInfoP->Comments[a];
             if (c == '\n'){
                 /* Do not start a new line if the string ends with a cr */
-                if (ImageInfoP->Comments[a+1] != '\0'){
-                    fprintf(stderr, "\nComment      : ");
-                }else{
-                    fprintf(stderr, "\n");
-                }
-            }else{
-                putc(c, stderr);
-            }
+                if (imageInfoP->Comments[a+1] != '\0')
+                    fprintf(fileP, "\nComment      : ");
+                else
+                    fprintf(fileP, "\n");
+            } else
+                putc(c, fileP);
         }
-        fprintf(stderr, "\n");
+        fprintf(fileP, "\n");
     }
 
-    fprintf(stderr, "\n");
+    fprintf(fileP, "\n");
 }
 
 
-
-
diff --git a/converter/other/exif.h b/converter/other/exif.h
index 4630988f..57eb745b 100644
--- a/converter/other/exif.h
+++ b/converter/other/exif.h
@@ -1,9 +1,12 @@
 #ifndef EXIF_H_INCLUDED
 #define EXIF_H_INCLUDED
 
+#include <stdio.h>
+#include "netpbm/pm_c_util.h"
+
 #define MAX_COMMENT 2000
 
-#ifdef _WIN32
+#if MSVCRT
     #define PATH_MAX _MAX_PATH
 #endif
 
@@ -35,23 +38,24 @@ typedef struct {
     int   CompressionLevel;
     char  Comments[MAX_COMMENT];
 
-    unsigned char * ThumbnailPointer;  /* Pointer at the thumbnail */
+    const unsigned char * ThumbnailPointer;  /* Pointer at the thumbnail */
     unsigned ThumbnailSize;     /* Size of thumbnail. */
 
-    char * DatePointer;
-}ImageInfo_t;
+    const char * DatePointer;
+} exif_ImageInfo;
 
 
 /* Prototypes for exif.c functions. */
 
 void 
-process_EXIF(unsigned char * const ExifSection, 
-             unsigned int    const length,
-             ImageInfo_t *   const ImageInfoP, 
-             int             const ShowTags,
-             const char **   const errorP);
+exif_parse(const unsigned char * const exifSection, 
+           unsigned int          const length,
+           exif_ImageInfo *      const imageInfoP, 
+           bool                  const wantTagTrace,
+           const char **         const errorP);
 
 void 
-ShowImageInfo(ImageInfo_t * const ImageInfoP);
+exif_showImageInfo(const exif_ImageInfo * const imageInfoP,
+                   FILE *                 const fileP);
 
 #endif
diff --git a/converter/other/fiasco/Makefile b/converter/other/fiasco/Makefile
index 16221d77..392e843c 100644
--- a/converter/other/fiasco/Makefile
+++ b/converter/other/fiasco/Makefile
@@ -11,8 +11,9 @@ COMP_INCLUDES = \
 	-I$(SRCDIR)/$(SUBDIR)/codec -I$(SRCDIR)/$(SUBDIR)/input \
 	-I$(SRCDIR)/$(SUBDIR)/output -I$(SRCDIR)/$(SUBDIR)/lib \
 
-BINARIES = pnmtofiasco fiascotopnm
+PORTBINARIES = pnmtofiasco fiascotopnm
 
+BINARIES = $(PORTBINARIES)
 MERGEBINARIES = $(BINARIES)
 
 SCRIPTS =
@@ -24,21 +25,18 @@ FIASCOLIBS = codec/libfiasco_codec.a \
 	     output/libfiasco_output.a \
 	     lib/libfiasco_lib.a 
 
-COMMON_OBJECTS = binerror.o getopt.o getopt1.o params.o
+ADDL_OBJECTS = binerror.o getopt.o getopt1.o params.o
 
-OBJECTS = $(BINARIES:%=%.o) $(COMMON_OBJECTS)
+OBJECTS = $(BINARIES:%=%.o) $(ADDL_OBJECTS)
 
-MERGE_OBJECTS = $(BINARIES:%=%.o2) $(COMMON_OBJECTS)  $(FIASCOLIBS)
+MERGE_OBJECTS = $(BINARIES:%=%.o2) $(ADDL_OBJECTS) $(FIASCOLIBS)
 
 SUBDIRS = codec input output lib
 
 include $(SRCDIR)/common.mk
 
-$(BINARIES):%:%.o $(COMMON_OBJECTS) $(FIASCOLIBS) $(NETPBMLIB) \
-   $(LIBOPT)
-	$(LD) -o $@ $< $(COMMON_OBJECTS) \
-	$(shell $(LIBOPT) $(FIASCOLIBS) $(NETPBMLIB)) $(MATHLIB) \
-	$(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
+$(BINARIES):%:%.o $(ADDL_OBJECTS) $(FIASCOLIBS)
+$(BINARIES): LDFLAGS_TARGET = $(shell $(LIBOPT) $(FIASCOLIBS))
 
 codec/libfiasco_codec.a: $(BUILDDIR)/$(SUBDIR)/codec FORCE
 	$(MAKE) -C codec -f $(SRCDIR)/$(SUBDIR)/codec/Makefile \
diff --git a/converter/other/fiasco/binerror.c b/converter/other/fiasco/binerror.c
index 8a41a214..77243c64 100644
--- a/converter/other/fiasco/binerror.c
+++ b/converter/other/fiasco/binerror.c
@@ -92,11 +92,7 @@ _error (const char *format, ...)
 
    fprintf (stderr, "%s: %s: line %d:\nError: ",
 	    executable, error_file, error_line);
-#if HAVE_VPRINTF
    vfprintf (stderr, format, args);
-#elif HAVE_DOPRNT
-   _doprnt (format, args, stderr);
-#endif /* HAVE_DOPRNT */
    fputc ('\n', stderr);
    va_end(args);
 
@@ -132,11 +128,7 @@ _warning (const char *format, ...)
 
    fprintf (stderr, "%s: %s: line %d:\nWarning: ",
 	    executable, error_file, error_line);
-#if HAVE_VPRINTF
    vfprintf (stderr, format, args);
-#elif HAVE_DOPRNT
-   _doprnt (format, args, stderr);
-#endif /* HAVE_DOPRNT */
    fputc ('\n', stderr);
 
    va_end (args);
diff --git a/converter/other/fiasco/codec/approx.c b/converter/other/fiasco/codec/approx.c
index 72e38cbf..5072fae3 100644
--- a/converter/other/fiasco/codec/approx.c
+++ b/converter/other/fiasco/codec/approx.c
@@ -294,7 +294,7 @@ static real_t ip_domain_ortho_vector [MAXSTATES][MAXEDGES];
 static real_t rem_denominator [MAXSTATES];     
 static real_t rem_numerator [MAXSTATES];
 /*
- *  At step n of the orthogonalization the comparitive value
+ *  At step n of the orthogonalization the comparative value
  *  (numerator_i / denominator_i):= <b, o_n>^2 / ||o_n|| ,
  *  is computed for every domain i,
  *  where o_n := s_i - \sum(k = 0, ... , n-1) {(<s_i, o_k> / ||o_k||^2) o_k}
@@ -670,7 +670,7 @@ orthogonalize (unsigned index, unsigned n, unsigned level, real_t min_norm,
     *  for (i = 0, ... , wfa->states)  
     *  <s_i, o_n> := <s_i, v_n> -
     *      \sum (k = 0, ... , n - 1){ <v_n, o_k> <s_i, o_k> / ||o_k||^2}
-    *  Moreover the denominator and numerator parts of the comparitive
+    *  Moreover the denominator and numerator parts of the comparative
     *  value are updated.
     */
    for (domain = 0; domain_blocks [domain] >= 0; domain++) 
diff --git a/converter/other/fiasco/codec/coder.c b/converter/other/fiasco/codec/coder.c
index 927ebbda..94e367dd 100644
--- a/converter/other/fiasco/codec/coder.c
+++ b/converter/other/fiasco/codec/coder.c
@@ -252,14 +252,14 @@ alloc_coder (char const * const * const inputname,
         lx = (unsigned) (log2 (wi->width - 1) + 1);
         ly = (unsigned) (log2 (wi->height - 1) + 1);
       
-        wi->level = max (lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
+        wi->level = MAX(lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
     }
    
     c = Calloc (1, sizeof (coding_t));
 
     c->options             = *options;
-    c->options.lc_min_level = max (options->lc_min_level, 3);
-    c->options.lc_max_level = min (options->lc_max_level, wi->level - 1);
+    c->options.lc_min_level = MAX(options->lc_min_level, 3);
+    c->options.lc_max_level = MIN(options->lc_max_level, wi->level - 1);
 
     c->tiling = alloc_tiling (options->tiling_method,
                               options->tiling_exponent, wi->level);
@@ -273,7 +273,7 @@ alloc_coder (char const * const * const inputname,
     if (c->options.lc_max_level >= wi->level - c->tiling->exponent)
     {
         message ("'max_level' changed from %d to %d "
-                 "due to image tiling level.",
+                 "because of image tiling level.",
                  c->options.lc_max_level, wi->level - c->tiling->exponent - 1);
         c->options.lc_max_level = wi->level - c->tiling->exponent - 1;
     }
@@ -285,16 +285,16 @@ alloc_coder (char const * const * const inputname,
      *  p_min_level, p_max_level min and max level for ND/MC prediction
      *  [p_min_level, p_max_level] must be a subset of [min_level, max_level] !
      */
-    wi->p_min_level = max (options->p_min_level, c->options.lc_min_level);
-    wi->p_max_level = min (options->p_max_level, c->options.lc_max_level);
+    wi->p_min_level = MAX(options->p_min_level, c->options.lc_min_level);
+    wi->p_max_level = MIN(options->p_max_level, c->options.lc_max_level);
     if (wi->p_min_level > wi->p_max_level)
         wi->p_min_level = wi->p_max_level;
 
-    c->options.images_level = min (c->options.images_level,
-                                   c->options.lc_max_level - 1);
+    c->options.images_level = MIN(c->options.images_level,
+                                  c->options.lc_max_level - 1);
    
-    c->products_level  = max (0, ((signed int) c->options.lc_max_level
-                                  - (signed int) c->options.images_level - 1));
+    c->products_level  = MAX(0, ((signed int) c->options.lc_max_level
+                                 - (signed int) c->options.images_level - 1));
     c->pixels         = Calloc (size_of_level (c->options.lc_max_level),
                                 sizeof (real_t));
     c->images_of_state = Calloc (MAXSTATES, sizeof (real_t *));
@@ -324,8 +324,8 @@ alloc_coder (char const * const * const inputname,
     /*
      *  Max. number of states and edges
      */
-    wi->max_states         = max (min (options->max_states, MAXSTATES), 1);
-    c->options.max_elements = max (min (options->max_elements, MAXEDGES), 1);
+    wi->max_states          = MAX(MIN(options->max_states, MAXSTATES), 1);
+    c->options.max_elements = MAX(MIN(options->max_elements, MAXEDGES), 1);
 
     /*
      *  Title and comment strings
@@ -348,7 +348,7 @@ alloc_coder (char const * const * const inputname,
     /*
      *  Color image options ...
      */
-    wi->chroma_max_states = max (1, options->chroma_max_states);
+    wi->chroma_max_states = MAX(1, options->chroma_max_states);
 
     /*
     *  Set up motion compensation struct.
@@ -432,9 +432,9 @@ print_statistics (char c, real_t costs, const wfa_t *wfa, const image_t *image,
      
       if (lincomb)
       {
-     max_level = max (max_level,
+     max_level = MAX(max_level,
               (unsigned) (wfa->level_of_state [state] - 1));
-     min_level = min (min_level,
+     min_level = MIN(min_level,
               (unsigned) (wfa->level_of_state [state] - 1));
       }
    }
@@ -548,77 +548,78 @@ frame_coder (wfa_t *wfa, coding_t *c, bitfile_t *output)
    }
    else
    {
-      int     YCb_node = -1;
-      int     tree [3];         /* 3 root states of each color comp. */
-      color_e band;
+       int     YCb_node = -1;
+       int     tree [3];         /* 3 root states of each color comp. */
+       color_e band;
       
-      /*
-       *  When compressing color images, the three color components (YCbCr) 
-       *  are copied into a large image:
-       *  [  Y  Cr ]
-       *  [  Cb 0  ]
-       *  I.e. the color components of an image are processed in a row.
-       *  After all components are compressed, virtual states are generated
-       *  to describe the large image.
-       */
-      for (band = first_band (YES); band <= last_band (YES) ; band++)
-      {
-     debug_message ("Encoding color component %d", band);
-     tree [band] = RANGE;
-     if (band == Cb)
-     {
-        unsigned min_level;
-
-        c->domain_pool->chroma (wfa->wfainfo->chroma_max_states, wfa,
-                    c->domain_pool->model);
-        /*
-         *  Don't use a finer partioning for the chrominancy bands than for
-         *  the luminancy band.
-         */
-        for (min_level = MAXLEVEL, state = wfa->basis_states;
-         state < wfa->states; state++)
-        {
-           unsigned lincomb, label;
+       /*
+        *  When compressing color images, the three color components (YCbCr) 
+        *  are copied into a large image:
+        *  [  Y  Cr ]
+        *  [  Cb 0  ]
+        *  I.e. the color components of an image are processed in a row.
+        *  After all components are compressed, virtual states are generated
+        *  to describe the large image.
+        */
+       for (band = first_band (YES); band <= last_band (YES) ; band++)
+       {
+           debug_message ("Encoding color component %d", band);
+           tree [band] = RANGE;
+           if (band == Cb)
+           {
+               unsigned min_level;
+
+               c->domain_pool->chroma (wfa->wfainfo->chroma_max_states, wfa,
+                                       c->domain_pool->model);
+               /*
+                *  Don't use a finer partioning for the chrominancy bands than
+                *  for the luminancy band.
+                */
+               for (min_level = MAXLEVEL, state = wfa->basis_states;
+                    state < wfa->states; state++)
+               {
+                   unsigned lincomb, label;
            
-           for (lincomb = 0, label = 0; label < MAXLABELS; label++)
-          lincomb += isrange (wfa->tree [state][label]) ? 1 : 0;
-           if (lincomb)
-          min_level = min (min_level,
-                   (unsigned) (wfa->level_of_state [state]
-                           - 1));
-        }
-        c->options.lc_min_level = min_level;
-        if (c->mt->frame_type != I_FRAME) /* subtract mc of luminance */
-           subtract_mc (c->mt->original, c->mt->past, c->mt->future, wfa);
-     }
-
-     memset (&range, 0, sizeof (range_t));
-     range.level = wfa->wfainfo->level;
+                   for (lincomb = 0, label = 0; label < MAXLABELS; label++)
+                       lincomb += isrange (wfa->tree [state][label]) ? 1 : 0;
+                   if (lincomb)
+                       min_level = MIN(min_level,
+                                       (unsigned) (wfa->level_of_state [state]
+                                                   - 1));
+               }
+               c->options.lc_min_level = min_level;
+               if (c->mt->frame_type != I_FRAME) /* subtract mc of luminance */
+                   subtract_mc (c->mt->original, c->mt->past, c->mt->future,
+                                wfa);
+           }
+
+           memset (&range, 0, sizeof (range_t));
+           range.level = wfa->wfainfo->level;
      
-     costs = subdivide (MAXCOSTS, band, tree [Y], &range, wfa, c,
-                c->mt->frame_type != I_FRAME && band == Y, NO);
-     if (c->options.progress_meter != FIASCO_PROGRESS_NONE)
-        message ("");
-     {
-        char colors [] = {'Y', 'B', 'R'};
+           costs = subdivide (MAXCOSTS, band, tree [Y], &range, wfa, c,
+                              c->mt->frame_type != I_FRAME && band == Y, NO);
+           if (c->options.progress_meter != FIASCO_PROGRESS_NONE)
+               message ("");
+           {
+               char colors [] = {'Y', 'B', 'R'};
         
-        print_statistics (colors [band], costs, wfa,
-                  c->mt->original, &range);
-     }
+               print_statistics (colors [band], costs, wfa,
+                                 c->mt->original, &range);
+           }
      
-     if (isrange (range.tree))  /* whole image is approx. by a l.c. */
-        error ("No root state generated for color component %d!", band);
-     else
-        tree[band] = range.tree;
+           if (isrange (range.tree))  /* whole image is approx. by a l.c. */
+               error ("No root state generated for color component %d!", band);
+           else
+               tree[band] = range.tree;
      
-     if (band == Cb)
-     {
-        wfa->tree [wfa->states][0] = tree[Y];
-        wfa->tree [wfa->states][1] = tree[Cb];
-        YCb_node = wfa->states;
-        append_state (YES, compute_final_distribution (wfa->states, wfa),
-              wfa->wfainfo->level + 1, wfa, c);
-     }
+           if (band == Cb)
+           {
+               wfa->tree [wfa->states][0] = tree[Y];
+               wfa->tree [wfa->states][1] = tree[Cb];
+               YCb_node = wfa->states;
+               append_state (YES, compute_final_distribution(wfa->states, wfa),
+                             wfa->wfainfo->level + 1, wfa, c);
+           }
       }
       /*
        *  generate two virtual states (*) 
diff --git a/converter/other/fiasco/codec/control.c b/converter/other/fiasco/codec/control.c
index 9af9928b..94c23c83 100644
--- a/converter/other/fiasco/codec/control.c
+++ b/converter/other/fiasco/codec/control.c
@@ -16,11 +16,7 @@
 
 #include "config.h"
 
-#if HAVE_STRING_H
-#	include <string.h>
-#else /* not HAVE_STRING_H */
-#	include <strings.h>
-#endif /* not HAVE_STRING_H */
+#include <string.h>
  
 #include "types.h"
 #include "macros.h"
diff --git a/converter/other/fiasco/codec/decoder.c b/converter/other/fiasco/codec/decoder.c
index 77d5340f..26284596 100644
--- a/converter/other/fiasco/codec/decoder.c
+++ b/converter/other/fiasco/codec/decoder.c
@@ -1,9 +1,9 @@
 /*
- *  decode.c:		Decoding of an image represented by a WFA
+ *  decode.c:       Decoding of an image represented by a WFA
  *
- *  Written by:		Ullrich Hafner
- *			Michael Unger
- *		
+ *  Written by:     Ullrich Hafner
+ *          Michael Unger
+ *      
  *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
  *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
  */
@@ -15,13 +15,12 @@
  *  $State: Exp $
  */
 
+#include "pm_config.h"
 #include "config.h"
 
-#if HAVE_STRING_H
-#	include <string.h>
-#else /* not HAVE_STRING_H */
-#	include <strings.h>
-#endif /* not HAVE_STRING_H */
+#include <string.h>
+
+#include "pm_c_util.h"
 
 #include "types.h"
 #include "macros.h"
@@ -37,33 +36,33 @@
 
 /*****************************************************************************
 
-				prototypes
+                prototypes
   
 *****************************************************************************/
 
 static void
 compute_state_images (unsigned frame_level, word_t **simg,
-		      const u_word_t *offset, const wfa_t *wfa);
+              const u_word_t *offset, const wfa_t *wfa);
 static void
 free_state_images (unsigned max_level, bool_t color, word_t **state_image,
-		   u_word_t *offset, const unsigned *root_state,
-		   unsigned range_state, format_e format, const wfa_t *wfa);
+           u_word_t *offset, const unsigned *root_state,
+           unsigned range_state, format_e format, const wfa_t *wfa);
 static void
 alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
-		    const unsigned *root_state, unsigned range_state,
-		    unsigned max_level, format_e format, const wfa_t *wfa);
+            const unsigned *root_state, unsigned range_state,
+            unsigned max_level, format_e format, const wfa_t *wfa);
 static void
 compute_actual_size (unsigned luminance_root,
-		     unsigned *width, unsigned *height, const wfa_t *wfa);
+             unsigned *width, unsigned *height, const wfa_t *wfa);
 static void
 enlarge_image (int enlarge_factor, format_e format, unsigned y_root,
-	       wfa_t *wfa);
+           wfa_t *wfa);
 static word_t *
 duplicate_state_image (const word_t *domain, unsigned offset, unsigned level);
 
 /*****************************************************************************
 
-				public code
+                public code
   
 *****************************************************************************/
 
@@ -75,7 +74,7 @@ alloc_video (bool_t store_wfa)
  *  and future WFA if flag 'store_wfa' is TRUE.
  *
  *  Return value:
- *	pointer to the new video structure
+ *  pointer to the new video structure
  */
 {
    video_t *video = Calloc (1, sizeof (video_t));
@@ -84,7 +83,7 @@ alloc_video (bool_t store_wfa)
    video->display        = 0;
 
    video->future = video->sfuture = video->past
-		 = video->frame   = video->sframe = NULL;
+         = video->frame   = video->sframe = NULL;
 
    if (store_wfa)
    {
@@ -107,7 +106,7 @@ free_video (video_t *video)
  *  No return value.
  *
  *  Side effects:
- *	'video' struct is discarded.
+ *  'video' struct is discarded.
  */
 {
    if (video->past)
@@ -132,9 +131,9 @@ free_video (video_t *video)
 
 image_t *
 get_next_frame (bool_t store_wfa, int enlarge_factor,
-		int smoothing, const char *reference_frame,
-		format_e format, video_t *video, dectimer_t *timer,
-		wfa_t *orig_wfa, bitfile_t *input)
+        int smoothing, const char *reference_frame,
+        format_e format, video_t *video, dectimer_t *timer,
+        wfa_t *orig_wfa, bitfile_t *input)
 /*
  *  Get next frame of the WFA 'video' from stream 'input'.
  *  'orig_wfa' is the constant part of the WFA used by all frames.
@@ -148,265 +147,265 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
  *  If 'timer' is not NULL, then accumulate running time statistics. 
  *
  *  Return value:
- *	pointer to decoded frame
+ *  pointer to decoded frame
  *
  *  Side effects:
- *	'video' and 'timer' struct are modified.
+ *  'video' and 'timer' struct are modified.
  */
 {
-   image_t *frame 			  = NULL; /* current frame */
-   image_t *sframe 			  = NULL; /* current smoothed frame */
+   image_t *frame             = NULL; /* current frame */
+   image_t *sframe            = NULL; /* current smoothed frame */
    bool_t   current_frame_is_future_frame = NO;
 
-   if (video->future_display == video->display)	 
+   if (video->future_display == video->display)  
    {
       /*
        *  Future frame is already computed since it has been used
        *  as reference frame. So just return the stored frame.
        */
       if (video->frame) /* discard current frame */
-	 free_image (video->frame);
+     free_image (video->frame);
       video->frame  = video->future;
       video->future = NULL;
 
       if (video->sframe) /* discard current (smoothed) frame */
-	 free_image (video->sframe);
+     free_image (video->sframe);
       video->sframe  = video->sfuture;
       video->sfuture = NULL;
 
       if (store_wfa)
-	 copy_wfa (video->wfa, video->wfa_future);
+     copy_wfa (video->wfa, video->wfa_future);
 
       video->display++;
 
       if (!store_wfa)
-	 video->wfa = NULL;
+     video->wfa = NULL;
    }
    else
    {
-      do				/* compute next frame(s) */
+      do                /* compute next frame(s) */
       {
-	 unsigned      frame_number;	/* current frame number */
-	 clock_t       ptimer;
-	 unsigned int  stop_timer [3];
-	 wfa_t	      *tmp_wfa = NULL;
-	 
-	 if (!store_wfa)
-	    video->wfa = orig_wfa;
-	 else
-	 {
-	    tmp_wfa = alloc_wfa (NO);
-	    copy_wfa (tmp_wfa, video->wfa);
-	    copy_wfa (video->wfa, orig_wfa);
-	 }
+     unsigned      frame_number;    /* current frame number */
+     clock_t       ptimer;
+     unsigned int  stop_timer [3];
+     wfa_t        *tmp_wfa = NULL;
+     
+     if (!store_wfa)
+        video->wfa = orig_wfa;
+     else
+     {
+        tmp_wfa = alloc_wfa (NO);
+        copy_wfa (tmp_wfa, video->wfa);
+        copy_wfa (video->wfa, orig_wfa);
+     }
    
-	 /*
-	  *  First step: read WFA from disk
-	  */
-	 prg_timer (&ptimer, START);
-	 frame_number = read_next_wfa (video->wfa, input);
-	 stop_timer [0] = prg_timer (&ptimer, STOP);
-	 if (timer)
-	 {
-	    timer->input [video->wfa->frame_type] += stop_timer [0];
-	    timer->frames [video->wfa->frame_type]++;
-	 }
+     /*
+      *  First step: read WFA from disk
+      */
+     prg_timer (&ptimer, START);
+     frame_number = read_next_wfa (video->wfa, input);
+     stop_timer [0] = prg_timer (&ptimer, STOP);
+     if (timer)
+     {
+        timer->input [video->wfa->frame_type] += stop_timer [0];
+        timer->frames [video->wfa->frame_type]++;
+     }
       
-	 /*
-	  *  Read reference frame from disk if required
-	  *  (i.e., 1st frame is of type B or P)
-	  */
-	 if (video->display == 0 && video->wfa->frame_type != I_FRAME)
-	 {
-	    if (!reference_frame)
-	       error ("First frame is %c-frame but no "
-		      "reference frame is given.",
-		      video->wfa->frame_type == B_FRAME ? 'B' : 'P');
-
-	    video->frame  = read_image_file (reference_frame);
-	    video->sframe = NULL;
-	 }
+     /*
+      *  Read reference frame from disk if required
+      *  (i.e., 1st frame is of type B or P)
+      */
+     if (video->display == 0 && video->wfa->frame_type != I_FRAME)
+     {
+        if (!reference_frame)
+           error ("First frame is %c-frame but no "
+              "reference frame is given.",
+              video->wfa->frame_type == B_FRAME ? 'B' : 'P');
+
+        video->frame  = read_image_file (reference_frame);
+        video->sframe = NULL;
+     }
    
-	 /*
-	  *  Depending on current frame type update past and future frames
-	  */
-	 if (video->wfa->frame_type == I_FRAME)
-	 {
-	    if (video->past)		/* discard past frame */
-	       free_image (video->past);
-	    video->past = NULL;
-	    if (video->future)		/* discard future frame */
-	       free_image (video->future);
-	    video->future = NULL;
-	    if (video->sfuture)		/* discard (smoothed) future frame */
-	       free_image (video->sfuture);
-	    video->sfuture = NULL;
-	    if (video->frame)		/* discard current frame */
-	       free_image (video->frame);
-	    video->frame = NULL;
-	    if (video->sframe)		/* discard current (smoothed) frame */
-	       free_image (video->sframe);
-	    video->sframe = NULL;
-	 }
-	 else if (video->wfa->frame_type == P_FRAME)
-	 {
-	    if (video->past)		/* discard past frame */
-	       free_image (video->past);
-	    video->past = video->frame;	/* past <- current frame */
-	    video->frame = NULL;
-	    if (video->sframe)		/* discard current (smoothed) frame */
-	       free_image (video->sframe);
-	    video->sframe = NULL;
-	    if (store_wfa)
-	       copy_wfa (video->wfa_past, tmp_wfa);
-	    if (video->future)		/* discard future frame */
-	       free_image (video->future);
-	    video->future = NULL;
-	    if (video->sfuture)		/* discard (smoothed) future frame */
-	       free_image (video->sfuture);
-	    video->sfuture = NULL;
-	 }
-	 else				/* B_FRAME */
-	 {
-	    if (current_frame_is_future_frame)
-	    {
-	       if (video->future)	/* discard future frame */
-		  free_image (video->future);
-	       video->future = frame;	/* future <- current frame */
-	       if (video->sfuture)	/* discard (smoothed) future frame */
-		  free_image (video->sfuture);
-	       video->sfuture = sframe;	/* future <- current (smoothed) */
-	       if (store_wfa)
-		  copy_wfa (video->wfa_future, tmp_wfa);
-	       if (video->frame)	/* discard current frame */
-		  free_image (video->frame);
-	       video->frame = NULL;
-	       if (video->sframe)	/* discard current (smoothed) frame */
-		  free_image (video->sframe);
-	       video->sframe = NULL;
-	       frame  = NULL;
-	       sframe = NULL;
-	    }
-	    else
-	    {
-	       if (video->wfa->wfainfo->B_as_past_ref == YES)
-	       {
-		  if (video->past)	/* discard past frame */
-		     free_image (video->past);
-		  video->past  = video->frame; /* past <- current frame */
-		  video->frame = NULL;
-		  if (video->sframe)	/* discard current (smoothed) frame */
-		     free_image (video->sframe);
-		  video->sframe = NULL;
-		  if (store_wfa)
-		     copy_wfa (video->wfa_past, tmp_wfa);
-	       }
-	       else
-	       {
-		  if (video->frame)	/* discard current */
-		     free_image (video->frame);
-		  video->frame = NULL;
-		  if (video->sframe)	/* discard current (smoothed) frame */
-		     free_image (video->sframe);
-		  video->sframe = NULL;
-	       }
-	    }
-	 }
-	 if (tmp_wfa)
-	    free_wfa (tmp_wfa);
-	 
-	 current_frame_is_future_frame = NO;
-	 /*
-	  *  Second step: decode image
-	  *  Optionally enlarge image if specified by option 'enlarge_factor'.
-	  */
-	 {
-	    unsigned orig_width, orig_height;
-
-	    stop_timer [0] = stop_timer [1] = stop_timer [2] = 0;
-	 
-	    enlarge_image (enlarge_factor, format,
-			   (video->wfa->wfainfo->color
-			    && format == FORMAT_4_2_0)
-			   ? video->wfa->tree [video->wfa->tree [video->wfa->root_state][0]][0] : -1, video->wfa);
-
-	    if (enlarge_factor > 0)
-	    {
-	       orig_width  = video->wfa->wfainfo->width  << enlarge_factor;
-	       orig_height = video->wfa->wfainfo->height << enlarge_factor; 
-	    }
-	    else
-	    { 
-	       orig_width  = video->wfa->wfainfo->width  >> - enlarge_factor;
-	       orig_height = video->wfa->wfainfo->height >> - enlarge_factor;
-	       if (orig_width & 1)
-		  orig_width++;
-	       if (orig_height & 1)
-		  orig_height++;
-	    }
-	 
-	    frame = decode_image (orig_width, orig_height, format,
-				  timer != NULL ? stop_timer : NULL,
-				  video->wfa);
-	    if (timer)
-	    {
-	       timer->preprocessing [video->wfa->frame_type] += stop_timer [0];
-	       timer->decoder [video->wfa->frame_type]       += stop_timer [1];
-	       timer->cleanup [video->wfa->frame_type]       += stop_timer [2];
-	    }
-	 }
-
-	 /*
-	  *  Third step: restore motion compensation
-	  */
-	 if (video->wfa->frame_type != I_FRAME)
-	 {
-	    prg_timer (&ptimer, START);
-	    restore_mc (enlarge_factor, frame, video->past, video->future,
-			video->wfa);
-	    stop_timer [0] = prg_timer (&ptimer, STOP);
-	    if (timer)
-	       timer->motion [video->wfa->frame_type] += stop_timer [0];
-	 }
-
-	 /*
-	  *  Fourth step: smooth image along partitioning borders
-	  */
-	 prg_timer (&ptimer, START);
-	 if (smoothing < 0)	/* smoothing not changed by user */
-	    smoothing = video->wfa->wfainfo->smoothing;
-	 if (smoothing > 0 && smoothing <= 100)
-	 {
-	    sframe = clone_image (frame);
-	    smooth_image (smoothing, video->wfa, sframe);
-	 }
-	 else
-	    sframe = NULL;
-	 
-	 stop_timer [0] = prg_timer (&ptimer, STOP);
-	 if (timer)
-	    timer->smooth [video->wfa->frame_type] += stop_timer [0];
-
-	 if (frame_number == video->display)
-	 {
-	    video->display++;
-	    video->frame  = frame;
-	    video->sframe = sframe;
-	    frame         = NULL;
-	    sframe        = NULL;
-	 }
-	 else if (frame_number > video->display)
-	 {
-	    video->future_display 	  = frame_number;
-	    current_frame_is_future_frame = YES;
-	 }
+     /*
+      *  Depending on current frame type update past and future frames
+      */
+     if (video->wfa->frame_type == I_FRAME)
+     {
+        if (video->past)        /* discard past frame */
+           free_image (video->past);
+        video->past = NULL;
+        if (video->future)      /* discard future frame */
+           free_image (video->future);
+        video->future = NULL;
+        if (video->sfuture)     /* discard (smoothed) future frame */
+           free_image (video->sfuture);
+        video->sfuture = NULL;
+        if (video->frame)       /* discard current frame */
+           free_image (video->frame);
+        video->frame = NULL;
+        if (video->sframe)      /* discard current (smoothed) frame */
+           free_image (video->sframe);
+        video->sframe = NULL;
+     }
+     else if (video->wfa->frame_type == P_FRAME)
+     {
+        if (video->past)        /* discard past frame */
+           free_image (video->past);
+        video->past = video->frame; /* past <- current frame */
+        video->frame = NULL;
+        if (video->sframe)      /* discard current (smoothed) frame */
+           free_image (video->sframe);
+        video->sframe = NULL;
+        if (store_wfa)
+           copy_wfa (video->wfa_past, tmp_wfa);
+        if (video->future)      /* discard future frame */
+           free_image (video->future);
+        video->future = NULL;
+        if (video->sfuture)     /* discard (smoothed) future frame */
+           free_image (video->sfuture);
+        video->sfuture = NULL;
+     }
+     else               /* B_FRAME */
+     {
+        if (current_frame_is_future_frame)
+        {
+           if (video->future)   /* discard future frame */
+          free_image (video->future);
+           video->future = frame;   /* future <- current frame */
+           if (video->sfuture)  /* discard (smoothed) future frame */
+          free_image (video->sfuture);
+           video->sfuture = sframe; /* future <- current (smoothed) */
+           if (store_wfa)
+          copy_wfa (video->wfa_future, tmp_wfa);
+           if (video->frame)    /* discard current frame */
+          free_image (video->frame);
+           video->frame = NULL;
+           if (video->sframe)   /* discard current (smoothed) frame */
+          free_image (video->sframe);
+           video->sframe = NULL;
+           frame  = NULL;
+           sframe = NULL;
+        }
+        else
+        {
+           if (video->wfa->wfainfo->B_as_past_ref == YES)
+           {
+          if (video->past)  /* discard past frame */
+             free_image (video->past);
+          video->past  = video->frame; /* past <- current frame */
+          video->frame = NULL;
+          if (video->sframe)    /* discard current (smoothed) frame */
+             free_image (video->sframe);
+          video->sframe = NULL;
+          if (store_wfa)
+             copy_wfa (video->wfa_past, tmp_wfa);
+           }
+           else
+           {
+          if (video->frame) /* discard current */
+             free_image (video->frame);
+          video->frame = NULL;
+          if (video->sframe)    /* discard current (smoothed) frame */
+             free_image (video->sframe);
+          video->sframe = NULL;
+           }
+        }
+     }
+     if (tmp_wfa)
+        free_wfa (tmp_wfa);
+     
+     current_frame_is_future_frame = NO;
+     /*
+      *  Second step: decode image
+      *  Optionally enlarge image if specified by option 'enlarge_factor'.
+      */
+     {
+        unsigned orig_width, orig_height;
+
+        stop_timer [0] = stop_timer [1] = stop_timer [2] = 0;
+     
+        enlarge_image (enlarge_factor, format,
+               (video->wfa->wfainfo->color
+                && format == FORMAT_4_2_0)
+               ? video->wfa->tree [video->wfa->tree [video->wfa->root_state][0]][0] : -1, video->wfa);
+
+        if (enlarge_factor > 0)
+        {
+           orig_width  = video->wfa->wfainfo->width  << enlarge_factor;
+           orig_height = video->wfa->wfainfo->height << enlarge_factor; 
+        }
+        else
+        { 
+           orig_width  = video->wfa->wfainfo->width  >> - enlarge_factor;
+           orig_height = video->wfa->wfainfo->height >> - enlarge_factor;
+           if (orig_width & 1)
+          orig_width++;
+           if (orig_height & 1)
+          orig_height++;
+        }
+     
+        frame = decode_image (orig_width, orig_height, format,
+                  timer != NULL ? stop_timer : NULL,
+                  video->wfa);
+        if (timer)
+        {
+           timer->preprocessing [video->wfa->frame_type] += stop_timer [0];
+           timer->decoder [video->wfa->frame_type]       += stop_timer [1];
+           timer->cleanup [video->wfa->frame_type]       += stop_timer [2];
+        }
+     }
+
+     /*
+      *  Third step: restore motion compensation
+      */
+     if (video->wfa->frame_type != I_FRAME)
+     {
+        prg_timer (&ptimer, START);
+        restore_mc (enlarge_factor, frame, video->past, video->future,
+            video->wfa);
+        stop_timer [0] = prg_timer (&ptimer, STOP);
+        if (timer)
+           timer->motion [video->wfa->frame_type] += stop_timer [0];
+     }
+
+     /*
+      *  Fourth step: smooth image along partitioning borders
+      */
+     prg_timer (&ptimer, START);
+     if (smoothing < 0) /* smoothing not changed by user */
+        smoothing = video->wfa->wfainfo->smoothing;
+     if (smoothing > 0 && smoothing <= 100)
+     {
+        sframe = clone_image (frame);
+        smooth_image (smoothing, video->wfa, sframe);
+     }
+     else
+        sframe = NULL;
+     
+     stop_timer [0] = prg_timer (&ptimer, STOP);
+     if (timer)
+        timer->smooth [video->wfa->frame_type] += stop_timer [0];
+
+     if (frame_number == video->display)
+     {
+        video->display++;
+        video->frame  = frame;
+        video->sframe = sframe;
+        frame         = NULL;
+        sframe        = NULL;
+     }
+     else if (frame_number > video->display)
+     {
+        video->future_display     = frame_number;
+        current_frame_is_future_frame = YES;
+     }
       
-	 if (!store_wfa)
-	    remove_states (video->wfa->basis_states, video->wfa);
+     if (!store_wfa)
+        remove_states (video->wfa->basis_states, video->wfa);
       } while (!video->frame);
 
       if (!store_wfa)
-	 video->wfa = NULL;
+     video->wfa = NULL;
    }
    
    return video->sframe ? video->sframe : video->frame;
@@ -414,7 +413,7 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
 
 image_t *
 decode_image (unsigned orig_width, unsigned orig_height, format_e format,
-	      unsigned *dec_timer, const wfa_t *wfa)
+          unsigned *dec_timer, const wfa_t *wfa)
 /*
  *  Compute image which is represented by the given 'wfa'.
  *  'orig_width'x'orig_height' gives the resolution of the image at
@@ -422,20 +421,20 @@ decode_image (unsigned orig_width, unsigned orig_height, format_e format,
  *  If 'dec_timer' is given, accumulate running time statistics. 
  *  
  *  Return value:
- *	pointer to decoded image
+ *  pointer to decoded image
  *
  *  Side effects:
- *	'*dectimer' is changed if 'dectimer' != NULL.
+ *  '*dectimer' is changed if 'dectimer' != NULL.
  */
 {
-   unsigned   root_state [3];		/* root of bintree for each band */
-   unsigned   width, height;		/* computed image size */
-   image_t   *frame;			/* regenerated frame */
-   word_t   **images;			/* pointer to array of pointers
-					   to state images */
-   u_word_t  *offsets;			/* pointer to array of state image
-					   offsets */
-   unsigned   max_level;		/* max. level of state with approx. */
+   unsigned   root_state [3];       /* root of bintree for each band */
+   unsigned   width, height;        /* computed image size */
+   image_t   *frame;            /* regenerated frame */
+   word_t   **images;           /* pointer to array of pointers
+                       to state images */
+   u_word_t  *offsets;          /* pointer to array of state image
+                       offsets */
+   unsigned   max_level;        /* max. level of state with approx. */
    unsigned   state;
    clock_t    ptimer;
 
@@ -458,16 +457,16 @@ decode_image (unsigned orig_width, unsigned orig_height, format_e format,
     */
    for (max_level = 0, state = wfa->basis_states; state < wfa->states; state++)
       if (isedge (wfa->into [state][0][0]) || isedge (wfa->into [state][1][0]))
-	 max_level = max (max_level, wfa->level_of_state [state]);
+     max_level = MAX(max_level, wfa->level_of_state [state]);
    
 
    /*
     *  Allocate frame buffer for decoded image
     */
    compute_actual_size (format == FORMAT_4_2_0 ? root_state [Y] : MAXSTATES,
-			&width, &height, wfa);
-   width  = max (width, orig_width);
-   height = max (height, orig_height);
+            &width, &height, wfa);
+   width  = MAX(width, orig_width);
+   height = MAX(height, orig_height);
    frame = alloc_image (width, height, wfa->wfainfo->color, format);
    
    /*
@@ -480,7 +479,7 @@ decode_image (unsigned orig_width, unsigned orig_height, format_e format,
       wfa->level_of_state [wfa->tree[wfa->root_state][1]] = 128;
    }
    alloc_state_images (&images, &offsets, frame, root_state, 0, max_level, 
-		       format, wfa);
+               format, wfa);
 
    if (dec_timer)
       dec_timer [0] += prg_timer (&ptimer, STOP);
@@ -498,38 +497,38 @@ decode_image (unsigned orig_width, unsigned orig_height, format_e format,
     */
    prg_timer (&ptimer, START);
    free_state_images (max_level, frame->color, images, offsets, root_state, 0,
-		      format, wfa);
+              format, wfa);
    
    /*
     *  Crop decoded image if the image size differs.
     */
    if (orig_width != width || orig_height != height)
    {
-      frame->height = orig_height;	
-      frame->width  = orig_width;	
-      if (orig_width != width)		
+      frame->height = orig_height;  
+      frame->width  = orig_width;   
+      if (orig_width != width)      
       {
-	 color_e   band;		/* current color band */
-	 word_t	  *src, *dst;		/* source and destination pointers */
-	 unsigned  y;			/* current row */
-	 
-	 for (band  = first_band (frame->color);
-	      band <= last_band (frame->color); band++)
-	 {
-	    src = dst = frame->pixels [band];
-	    for (y = orig_height; y; y--)
-	    {
-	       memmove (dst, src, orig_width * sizeof (word_t));
-	       dst += orig_width;
-	       src += width;
-	    }
-	    if (format == FORMAT_4_2_0 && band == Y)
-	    {
-	       orig_width  >>= 1;
-	       orig_height >>= 1;
-	       width       >>= 1;
-	    }
-	 }
+     color_e   band;        /* current color band */
+     word_t   *src, *dst;       /* source and destination pointers */
+     unsigned  y;           /* current row */
+     
+     for (band  = first_band (frame->color);
+          band <= last_band (frame->color); band++)
+     {
+        src = dst = frame->pixels [band];
+        for (y = orig_height; y; y--)
+        {
+           memmove (dst, src, orig_width * sizeof (word_t));
+           dst += orig_width;
+           src += width;
+        }
+        if (format == FORMAT_4_2_0 && band == Y)
+        {
+           orig_width  >>= 1;
+           orig_height >>= 1;
+           width       >>= 1;
+        }
+     }
       }
    }
    if (dec_timer)
@@ -544,10 +543,10 @@ decode_state (unsigned state, unsigned level, wfa_t *wfa)
  *  Decode 'state' image of 'wfa' at given 'level'.
  *
  *  Return value.
- *	pointer to decoded state image
+ *  pointer to decoded state image
  *
  *  Side effects:
- *	'wfa' states > 'state' are removed.  
+ *  'wfa' states > 'state' are removed.  
  */
 {
    word_t  *domains [2];
@@ -571,15 +570,15 @@ decode_state (unsigned state, unsigned level, wfa_t *wfa)
     */
    {
       word_t   *src, *dst;
-      unsigned	y;
-	    
+      unsigned  y;
+        
       src = domains [0];
       dst = img->pixels [GRAY];
       for (y = img->height; y; y--)
       {
-	 memcpy (dst, src, width_of_level (level) * sizeof (word_t));
-	 src += width_of_level (level);
-	 dst += img->width;
+     memcpy (dst, src, width_of_level (level) * sizeof (word_t));
+     src += width_of_level (level);
+     dst += img->width;
       }
       Free (domains [0]);
    }
@@ -589,87 +588,87 @@ decode_state (unsigned state, unsigned level, wfa_t *wfa)
 
 word_t *
 decode_range (unsigned range_state, unsigned range_label, unsigned range_level,
-	      word_t **domain, wfa_t *wfa)
+          word_t **domain, wfa_t *wfa)
 /*
  *  Compute 'wfa' image of range (identified by 'state' and 'label')
  *  at 'range_level (works as function decode_image()).
  *
  *  Return value:
- *	pointer to the pixels in SHORT format
+ *  pointer to the pixels in SHORT format
  *
  *  Side effects:
- *	if 'domain' != NULL then also the domain blocks
- *	of the corresponding range blocks are generated
+ *  if 'domain' != NULL then also the domain blocks
+ *  of the corresponding range blocks are generated
  *      and returned in domain[]
- *	'wfa->level_of_state []' is changed
+ *  'wfa->level_of_state []' is changed
  */
 {
-   unsigned   root_state [3];		/* dummy (for alloc_state_images) */
-   image_t   *state_image;		/* regenerated state image */
-   word_t   **images;			/* pointer to array of pointers
-					   to state images */
-   u_word_t  *offsets;			/* pointer to array of state image
-					   offsets */
+   unsigned   root_state [3];       /* dummy (for alloc_state_images) */
+   image_t   *state_image;      /* regenerated state image */
+   word_t   **images;           /* pointer to array of pointers
+                       to state images */
+   u_word_t  *offsets;          /* pointer to array of state image
+                       offsets */
    word_t    *range;
 
    enlarge_image (range_level - (wfa->level_of_state [range_state] - 1),
-		  FORMAT_4_4_4, -1, wfa);
+          FORMAT_4_4_4, -1, wfa);
    root_state [0] = range_state;
    state_image    = alloc_image (width_of_level (range_level + 1),
-				 height_of_level (range_level + 1),
-				 NO, FORMAT_4_4_4);
+                 height_of_level (range_level + 1),
+                 NO, FORMAT_4_4_4);
    alloc_state_images (&images, &offsets, state_image, NULL, range_state,
-		       range_level + 1, NO, wfa);
+               range_level + 1, NO, wfa);
    compute_state_images (range_level + 1, images, offsets, wfa);
 
    range = Calloc (size_of_level (range_level), sizeof (word_t));
 
-   if ((range_level & 1) == 0)		/* square image */
+   if ((range_level & 1) == 0)      /* square image */
    {
       memcpy (range,
-	      images [range_state + (range_level + 1) * wfa->states]
-	      + range_label * size_of_level (range_level),
-	      size_of_level (range_level) * sizeof (word_t));
+          images [range_state + (range_level + 1) * wfa->states]
+          + range_label * size_of_level (range_level),
+          size_of_level (range_level) * sizeof (word_t));
    }
-   else					/* rectangle */
+   else                 /* rectangle */
    {
       word_t   *src, *dst;
       unsigned  y;
       
       src = images [range_state + (range_level + 1) * wfa->states]
-	    + range_label * width_of_level (range_level);
+        + range_label * width_of_level (range_level);
       dst = range;
       for (y = height_of_level (range_level); y; y--)
       {
-	 memcpy (dst, src, width_of_level (range_level) * sizeof (word_t));
-	 dst += width_of_level (range_level);
-	 src += width_of_level (range_level + 1);
+     memcpy (dst, src, width_of_level (range_level) * sizeof (word_t));
+     dst += width_of_level (range_level);
+     src += width_of_level (range_level + 1);
       }
    }
 
-   if (domain != NULL)			/* copy domain images */
+   if (domain != NULL)          /* copy domain images */
    {
-      int      s;			/* domain state */
-      unsigned edge;			/* counter */
-		
+      int      s;           /* domain state */
+      unsigned edge;            /* counter */
+        
       if (ischild (s = wfa->tree [range_state][range_label]))
-	 *domain++ = duplicate_state_image (images [s + (range_level)
-						   * wfa->states],
-					    offsets [s + (range_level)
-						    * wfa->states],
-					    range_level);
+     *domain++ = duplicate_state_image (images [s + (range_level)
+                           * wfa->states],
+                        offsets [s + (range_level)
+                            * wfa->states],
+                        range_level);
       for (edge = 0; isedge (s = wfa->into[range_state][range_label][edge]);
-	   edge++)
-	 *domain++ = duplicate_state_image (images [s + (range_level)
-						   * wfa->states],
-					    offsets [s + (range_level)
-						    * wfa->states],
-					    range_level);
+       edge++)
+     *domain++ = duplicate_state_image (images [s + (range_level)
+                           * wfa->states],
+                        offsets [s + (range_level)
+                            * wfa->states],
+                        range_level);
       *domain = NULL;
    }
    
    free_state_images (range_level + 1, NO, images, offsets, NULL, range_state,
-		      NO, wfa);
+              NO, wfa);
    free_image (state_image);
    
    return range;
@@ -684,102 +683,102 @@ smooth_image (unsigned sf, const wfa_t *wfa, image_t *image)
  *  No return value.
  *
  *  Side effects:
- *	pixel values of the 'image' are modified with respect to 's'
+ *  pixel values of the 'image' are modified with respect to 's'
  */
 {
-   int	    is, inegs;			/* integer factors of s and 1 - s*/
-   unsigned state;			
+   int      is, inegs;          /* integer factors of s and 1 - s*/
+   unsigned state;          
    unsigned img_width  = image->width;
    unsigned img_height = image->height;
-   real_t   s 	       = 1.0 - sf / 200.0;
+   real_t   s          = 1.0 - sf / 200.0;
 
-   if (s < 0.5 || s >= 1)		/* value out of range */
+   if (s < 0.5 || s >= 1)       /* value out of range */
       return;
 
-   is 	 = s * 512 + .5;		/* integer representation of s */
-   inegs = (1 - s) * 512 + .5;		/* integer representation of 1 - s */
+   is    = s * 512 + .5;        /* integer representation of s */
+   inegs = (1 - s) * 512 + .5;      /* integer representation of 1 - s */
    
    for (state = wfa->basis_states;
-	state < (wfa->wfainfo->color
-		 ? wfa->tree [wfa->root_state][0]
-		 : wfa->states); state++)
+    state < (wfa->wfainfo->color
+         ? wfa->tree [wfa->root_state][0]
+         : wfa->states); state++)
    {
       word_t   *bptr   = image->pixels [Y]; /* pointer to right or
-					       lower line */
+                           lower line */
       unsigned  level  = wfa->level_of_state[state]; /* level of state image */
       unsigned  width  = width_of_level (level); /* size of state image */
       unsigned  height = height_of_level (level); /* size of state image */
       
       if (wfa->y [state][1] >= img_height || wfa->x [state][1] >= img_width)
-	 continue;			/* outside visible area */
-	 
-      if (level % 2)			/* horizontal smoothing */
+     continue;          /* outside visible area */
+     
+      if (level % 2)            /* horizontal smoothing */
       {
-	 unsigned  i;			/* line counter */
-	 word_t   *img1;		/* pointer to left or upper line */
-	 word_t   *img2;		/* pointer to right or lower line */
-
-	 img1 = bptr + (wfa->y [state][1] - 1) * img_width
-		+ wfa->x [state][1];
-	 img2 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1];
-	 
-	 for (i = min (width, img_width - wfa->x [state][1]); i;
-	      i--, img1++, img2++)
-	 {
-	    int tmp = *img1;
-	    
+     unsigned  i;           /* line counter */
+     word_t   *img1;        /* pointer to left or upper line */
+     word_t   *img2;        /* pointer to right or lower line */
+
+     img1 = bptr + (wfa->y [state][1] - 1) * img_width
+        + wfa->x [state][1];
+     img2 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1];
+     
+     for (i = MIN(width, img_width - wfa->x [state][1]); i;
+          i--, img1++, img2++)
+     {
+        int tmp = *img1;
+        
 #ifdef HAVE_SIGNED_SHIFT
-	    *img1 = (((is * tmp) >> 10) << 1)
-		    + (((inegs * (int) *img2) >> 10) << 1);
-	    *img2 = (((is * (int) *img2) >> 10) << 1)
-		    + (((inegs * tmp) >> 10) << 1);
+        *img1 = (((is * tmp) >> 10) << 1)
+            + (((inegs * (int) *img2) >> 10) << 1);
+        *img2 = (((is * (int) *img2) >> 10) << 1)
+            + (((inegs * tmp) >> 10) << 1);
 #else /* not HAVE_SIGNED_SHIFT */
-	    *img1 = (((is * tmp) / 1024) * 2)
-		    + (((inegs * (int) *img2) / 1024) * 2);
-	    *img2 = (((is * (int) *img2) / 1024) * 2)
-		    + (((inegs * tmp) / 1024) *2);
+        *img1 = (((is * tmp) / 1024) * 2)
+            + (((inegs * (int) *img2) / 1024) * 2);
+        *img2 = (((is * (int) *img2) / 1024) * 2)
+            + (((inegs * tmp) / 1024) *2);
 #endif /* not HAVE_SIGNED_SHIFT */
-	 }
+     }
       }
-      else				/* vertical smoothing */
+      else              /* vertical smoothing */
       {
-	 unsigned  i;			/* line counter */
-	 word_t   *img1;		/* pointer to left or upper line */
-	 word_t   *img2;		/* pointer to right or lower line */
-
-	 img1 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1] - 1;
-	 img2 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1];
-	 
-	 for (i = min (height, img_height - wfa->y [state][1]); i;
-	      i--, img1 += img_width, img2 += img_width)
-	 {
-	    int tmp = *img1;
-	    
+     unsigned  i;           /* line counter */
+     word_t   *img1;        /* pointer to left or upper line */
+     word_t   *img2;        /* pointer to right or lower line */
+
+     img1 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1] - 1;
+     img2 = bptr + wfa->y [state][1] * img_width + wfa->x [state][1];
+     
+     for (i = MIN(height, img_height - wfa->y [state][1]); i;
+          i--, img1 += img_width, img2 += img_width)
+     {
+        int tmp = *img1;
+        
 #ifdef HAVE_SIGNED_SHIFT
-	    *img1 = (((is * tmp) >> 10) << 1)
-		    + (((inegs * (int) *img2) >> 10) << 1);
-	    *img2 = (((is * (int) *img2) >> 10) << 1)
-		    + (((inegs * tmp) >> 10) << 1);
+        *img1 = (((is * tmp) >> 10) << 1)
+            + (((inegs * (int) *img2) >> 10) << 1);
+        *img2 = (((is * (int) *img2) >> 10) << 1)
+            + (((inegs * tmp) >> 10) << 1);
 #else /* not HAVE_SIGNED_SHIFT */
-	    *img1 = (((is * tmp) / 1024) * 2)
-		    + (((inegs * (int) *img2) / 1024) * 2);
-	    *img2 = (((is * (int) *img2) / 1024) * 2)
-		    + (((inegs * tmp) / 1024) *2);
+        *img1 = (((is * tmp) / 1024) * 2)
+            + (((inegs * (int) *img2) / 1024) * 2);
+        *img2 = (((is * (int) *img2) / 1024) * 2)
+            + (((inegs * tmp) / 1024) *2);
 #endif /* not HAVE_SIGNED_SHIFT */
-	 }
+     }
       }
    }
 }
 
 /*****************************************************************************
 
-				private code
+                private code
   
 *****************************************************************************/
 
 static void
 enlarge_image (int enlarge_factor, format_e format, unsigned y_root,
-	       wfa_t *wfa)
+           wfa_t *wfa)
 /*
  *  Enlarge or reduce size of state images by factor 2^'enlarge_factor'.
  *  Use 4:2:0 subsampling if specified by 'format', else use 4:4:4 format.
@@ -788,8 +787,8 @@ enlarge_image (int enlarge_factor, format_e format, unsigned y_root,
  *  No return value.
  *
  *  Side effects:
- *	coordinates of ranges and motion blocks in the WFA structure 'wfa'
- *	are modified.
+ *  coordinates of ranges and motion blocks in the WFA structure 'wfa'
+ *  are modified.
  */
 {
    
@@ -799,53 +798,53 @@ enlarge_image (int enlarge_factor, format_e format, unsigned y_root,
 
       if (enlarge_factor == 0)
       {
-	 state 		= y_root + 1;
-	 enlarge_factor = -1;
+     state      = y_root + 1;
+     enlarge_factor = -1;
       }
       else
-	 state = wfa->basis_states;
+     state = wfa->basis_states;
       
       for (; state < wfa->states; state++)
       {
-	 unsigned label, n;
-	 
-	 wfa->level_of_state [state]
-	    = max (wfa->level_of_state [state] + enlarge_factor * 2, 0);
-
-	 for (label = 0; label < MAXLABELS; label++)
-	    if (enlarge_factor > 0)
-	    {
-	       wfa->x [state][label] <<= enlarge_factor;
-	       wfa->y [state][label] <<= enlarge_factor;
-	       for (n = enlarge_factor; n; n--)
-	       {
-		  wfa->mv_tree [state][label].fx *= 2;
-		  wfa->mv_tree [state][label].fy *= 2;
-		  wfa->mv_tree [state][label].bx *= 2;
-		  wfa->mv_tree [state][label].by *= 2;
-	       }
-	    }
-	    else				/* enlarge_factor < 0 */
-	    {
-	       wfa->x [state][label] >>= - enlarge_factor;
-	       wfa->y [state][label] >>= - enlarge_factor;
-	       for (n = - enlarge_factor; n; n--)
-	       {
-		  wfa->mv_tree [state][label].fx /= 2;
-		  wfa->mv_tree [state][label].fy /= 2;
-		  wfa->mv_tree [state][label].bx /= 2;
-		  wfa->mv_tree [state][label].by /= 2;
-	       }
-	    }
-	 if (format == FORMAT_4_2_0 && state == y_root)
-	    enlarge_factor--;
+     unsigned label, n;
+     
+     wfa->level_of_state [state]
+        = MAX(wfa->level_of_state [state] + enlarge_factor * 2, 0);
+
+     for (label = 0; label < MAXLABELS; label++)
+        if (enlarge_factor > 0)
+        {
+           wfa->x [state][label] <<= enlarge_factor;
+           wfa->y [state][label] <<= enlarge_factor;
+           for (n = enlarge_factor; n; n--)
+           {
+          wfa->mv_tree [state][label].fx *= 2;
+          wfa->mv_tree [state][label].fy *= 2;
+          wfa->mv_tree [state][label].bx *= 2;
+          wfa->mv_tree [state][label].by *= 2;
+           }
+        }
+        else                /* enlarge_factor < 0 */
+        {
+           wfa->x [state][label] >>= - enlarge_factor;
+           wfa->y [state][label] >>= - enlarge_factor;
+           for (n = - enlarge_factor; n; n--)
+           {
+          wfa->mv_tree [state][label].fx /= 2;
+          wfa->mv_tree [state][label].fy /= 2;
+          wfa->mv_tree [state][label].bx /= 2;
+          wfa->mv_tree [state][label].by /= 2;
+           }
+        }
+     if (format == FORMAT_4_2_0 && state == y_root)
+        enlarge_factor--;
       }
    }
 }
 
 static void
 compute_actual_size (unsigned luminance_root,
-		     unsigned *width, unsigned *height, const wfa_t *wfa)
+             unsigned *width, unsigned *height, const wfa_t *wfa)
 /*
  *  Compute actual size of the frame represented by the given 'wfa'.
  *  (The reconstructed frame may get larger than the original due
@@ -853,24 +852,24 @@ compute_actual_size (unsigned luminance_root,
  *  If 'luminance_root' < MAXSTATES then the size of chroma ranges (4:2:0).
  *
  *  Return values:
- *	actual 'width' and 'height' of the decoded frame.
+ *  actual 'width' and 'height' of the decoded frame.
  */
 {
-   unsigned x = 0, y = 0;		/* maximum coordinates */
-   unsigned state;			/* counter */
+   unsigned x = 0, y = 0;       /* maximum coordinates */
+   unsigned state;          /* counter */
    
    for (state = wfa->basis_states; state < wfa->states; state++)
       if (isedge (wfa->into [state][0][0]) || isedge (wfa->into [state][1][0]))
       {
-	 unsigned mult = state > luminance_root ? 2 : 1;
-	 
-	 x = max ((wfa->x [state][0]
-		   + width_of_level (wfa->level_of_state [state])) * mult, x);
-	 y = max ((wfa->y [state][0]
-		   + height_of_level (wfa->level_of_state [state])) * mult, y);
+          unsigned mult = state > luminance_root ? 2 : 1;
+          
+          x = MAX((wfa->x [state][0]
+                   + width_of_level (wfa->level_of_state [state])) * mult, x);
+          y = MAX((wfa->y [state][0]
+                   + height_of_level (wfa->level_of_state [state])) * mult, y);
       }
 
-   if (x & 1)				/* ensure that image size is even */
+   if (x & 1)               /* ensure that image size is even */
       x++;
    if (y & 1)
       y++;
@@ -880,8 +879,8 @@ compute_actual_size (unsigned luminance_root,
 
 static void
 alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
-		    const unsigned *root_state, unsigned range_state,
-		    unsigned max_level, format_e format, const wfa_t *wfa)
+            const unsigned *root_state, unsigned range_state,
+            unsigned max_level, format_e format, const wfa_t *wfa)
 /*
  *  Generate list of 'wfa' state images which have to be computed for
  *  each level to obtain the decoded 'frame'. 'root_state[]' denotes the
@@ -893,24 +892,24 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
  *  image of 'wfa->root_state'.
  *
  *  Return values:
- *	'*images'	Pointer to array of state image pointers
- *	'*offsets'	Pointer to array of state image offsets.
+ *  '*images'   Pointer to array of state image pointers
+ *  '*offsets'  Pointer to array of state image offsets.
  *
  *  Side effects:
- *	The arrays given above are filled with useful values.
+ *  The arrays given above are filled with useful values.
  */
 {
-   word_t   **simg;			/* ptr to list of state image ptr's */
-   u_word_t  *offs;			/* ptr to list of offsets */
-   unsigned   level;			/* counter */
+   word_t   **simg;         /* ptr to list of state image ptr's */
+   u_word_t  *offs;         /* ptr to list of offsets */
+   unsigned   level;            /* counter */
    
-   simg	= Calloc (wfa->states * (max_level + 1), sizeof (word_t *));
-   offs	= Calloc (wfa->states * (max_level + 1), sizeof (u_word_t));
+   simg = Calloc (wfa->states * (max_level + 1), sizeof (word_t *));
+   offs = Calloc (wfa->states * (max_level + 1), sizeof (u_word_t));
 
    /*
     *  Initialize buffers for those state images which are at 'max_level'.
     */
-   if (range_state > 0)			/* a range is given */
+   if (range_state > 0)         /* a range is given */
    {
       simg [range_state + max_level * wfa->states] = frame->pixels [GRAY];
       offs [range_state + max_level * wfa->states] = frame->width;
@@ -920,25 +919,25 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
       unsigned state;
 
       for (state = wfa->basis_states; state <= root_state [Y]; state++)
-	 if (wfa->level_of_state [state] == max_level)
-	 {
-	    simg [state + max_level * wfa->states]
-	       = (frame->pixels [Y] + wfa->y [state][0] * frame->width
-		  + wfa->x [state][0]);
-	    offs [state + max_level * wfa->states] = frame->width;
-	 }
+     if (wfa->level_of_state [state] == max_level)
+     {
+        simg [state + max_level * wfa->states]
+           = (frame->pixels [Y] + wfa->y [state][0] * frame->width
+          + wfa->x [state][0]);
+        offs [state + max_level * wfa->states] = frame->width;
+     }
       if (frame->color)
       {
-	 unsigned width = format == FORMAT_4_2_0 ?
-			  (frame->width >> 1) : frame->width;
-	 for (; state < wfa->states; state++)
-	    if (wfa->level_of_state [state] == max_level)
-	    {
-	       simg [state + max_level * wfa->states]
-		  = (frame->pixels [state > root_state [Cb] ? Cr : Cb]
-		     + wfa->y [state][0] * width + wfa->x [state][0]);
-	       offs [state + max_level * wfa->states] = width;
-	    }
+     unsigned width = format == FORMAT_4_2_0 ?
+              (frame->width >> 1) : frame->width;
+     for (; state < wfa->states; state++)
+        if (wfa->level_of_state [state] == max_level)
+        {
+           simg [state + max_level * wfa->states]
+          = (frame->pixels [state > root_state [Cb] ? Cr : Cb]
+             + wfa->y [state][0] * width + wfa->x [state][0]);
+           offs [state + max_level * wfa->states] = width;
+        }
       }
    }
    
@@ -954,63 +953,63 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
        *  Range approximation with child. 
        */
       for (state = 1; state < (range_state > 0 ?
-			       range_state + 1 : wfa->states); state++)
-	 if (simg [state + level * wfa->states])
-	    for (label = 0; label < MAXLABELS; label++)
-	       if (ischild (child = wfa->tree[state][label]))
-	       {
-		  if (isedge (wfa->into[state][label][0]))
-		  {
-		     /*
-		      *  Allocate new image block.
-		      */
-		     simg [child + (level - 1) * wfa->states]
-			= Calloc (size_of_level (level - 1), sizeof (word_t));
-		     offs [child + (level - 1) * wfa->states]
-			= width_of_level (level - 1);
-		  }
-		  else
-		  {
-		     /*
-		      *  Use image block and offset of parent.
-		      */
-		     if (level & 1)	/* split vertically */
-		     {
-			simg [child + (level - 1) * wfa->states]
-			   = (simg [state + level * wfa->states]
-			      + label * (height_of_level (level - 1)
-					 * offs [state
-						+ level * wfa->states]));
-		     }
-		     else		/* split horizontally */
-		     {
-			simg [child + (level - 1) * wfa->states]
-			   = (simg [state + level * wfa->states]
-			      + label * width_of_level (level - 1));
-		     }
-		     offs [child + (level - 1) * wfa->states]
-			= offs [state + level * wfa->states];
-		  }
-	       }
+                   range_state + 1 : wfa->states); state++)
+     if (simg [state + level * wfa->states])
+        for (label = 0; label < MAXLABELS; label++)
+           if (ischild (child = wfa->tree[state][label]))
+           {
+          if (isedge (wfa->into[state][label][0]))
+          {
+             /*
+              *  Allocate new image block.
+              */
+             simg [child + (level - 1) * wfa->states]
+            = Calloc (size_of_level (level - 1), sizeof (word_t));
+             offs [child + (level - 1) * wfa->states]
+            = width_of_level (level - 1);
+          }
+          else
+          {
+             /*
+              *  Use image block and offset of parent.
+              */
+             if (level & 1) /* split vertically */
+             {
+            simg [child + (level - 1) * wfa->states]
+               = (simg [state + level * wfa->states]
+                  + label * (height_of_level (level - 1)
+                     * offs [state
+                        + level * wfa->states]));
+             }
+             else       /* split horizontally */
+             {
+            simg [child + (level - 1) * wfa->states]
+               = (simg [state + level * wfa->states]
+                  + label * width_of_level (level - 1));
+             }
+             offs [child + (level - 1) * wfa->states]
+            = offs [state + level * wfa->states];
+          }
+           }
       /*
        *  Range approximation with linear combination 
        */
       for (state = 1; state < (range_state > 0 ?
-			       range_state + 1 : wfa->states); state++)
-	 if (simg [state + level * wfa->states])
-	    for (label = 0; label < MAXLABELS; label++)
-	       for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
-		    edge++)
-	       {
-		  if (domain > 0	/* don't allocate memory for state 0 */
-		      && !simg [domain + (level - 1) * wfa->states])
-		  {
-		     simg [domain + (level - 1) * wfa->states]
-			= Calloc (size_of_level (level - 1), sizeof (word_t));
-		     offs [domain + (level - 1) * wfa->states]
-			= width_of_level (level - 1);
-		  }
-	       }
+                   range_state + 1 : wfa->states); state++)
+     if (simg [state + level * wfa->states])
+        for (label = 0; label < MAXLABELS; label++)
+           for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
+            edge++)
+           {
+          if (domain > 0    /* don't allocate memory for state 0 */
+              && !simg [domain + (level - 1) * wfa->states])
+          {
+             simg [domain + (level - 1) * wfa->states]
+            = Calloc (size_of_level (level - 1), sizeof (word_t));
+             offs [domain + (level - 1) * wfa->states]
+            = width_of_level (level - 1);
+          }
+           }
       
    }
 
@@ -1020,8 +1019,8 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
 
 static void
 free_state_images (unsigned max_level, bool_t color, word_t **state_image,
-		   u_word_t *offset, const unsigned *root_state,
-		   unsigned range_state, format_e format, const wfa_t *wfa)
+           u_word_t *offset, const unsigned *root_state,
+           unsigned range_state, format_e format, const wfa_t *wfa)
 /*
  *  Free memory of state images.
  *  For more details refer to the inverse function 'alloc_state_images()'.
@@ -1029,10 +1028,10 @@ free_state_images (unsigned max_level, bool_t color, word_t **state_image,
  *  No return value.
  *
  *  Side effects:
- *	arrays 'state_image' and 'offset' are discarded.
+ *  arrays 'state_image' and 'offset' are discarded.
  */
 {
-   word_t   marker;			/* ptr is required as a marker */
+   word_t   marker;         /* ptr is required as a marker */
    unsigned level;
 
    if (range_state > 0)
@@ -1047,19 +1046,19 @@ free_state_images (unsigned max_level, bool_t color, word_t **state_image,
        *  Initialize state image array with states at 'max_level'
        */
       for (state = wfa->basis_states; state <= root_state [Y]; state++)
-	 if (wfa->level_of_state [state] == max_level)
-	    state_image [state + max_level * wfa->states] = &marker;
+     if (wfa->level_of_state [state] == max_level)
+        state_image [state + max_level * wfa->states] = &marker;
 
       if (color)
       {
-	 if (format == FORMAT_4_2_0)
-	    level = max_level - 2;
-	 else
-	    level = max_level;
+     if (format == FORMAT_4_2_0)
+        level = max_level - 2;
+     else
+        level = max_level;
       
-	 for (; state < wfa->states; state++)
-	    if (wfa->level_of_state [state] == level)
-	       state_image [state + level * wfa->states] = &marker;
+     for (; state < wfa->states; state++)
+        if (wfa->level_of_state [state] == level)
+           state_image [state + level * wfa->states] = &marker;
       }
    }
    
@@ -1071,37 +1070,37 @@ free_state_images (unsigned max_level, bool_t color, word_t **state_image,
        *  Range approximation with child. 
        */
       for (state = 1; state < (range_state > 0 ?
-			       range_state + 1 : wfa->states); state++)
-	 if (state_image [state + level * wfa->states])
-	    for (label = 0; label < MAXLABELS; label++)
-	       if (ischild (child = wfa->tree[state][label]))
-	       {
-		  if (isedge (wfa->into[state][label][0])
-		      && (state_image [child + (level - 1) * wfa->states]
-			  != &marker))
-		     Free (state_image [child + (level - 1) * wfa->states]);
-		  state_image [child + (level - 1) * wfa->states] = &marker;
-	       }
+                   range_state + 1 : wfa->states); state++)
+     if (state_image [state + level * wfa->states])
+        for (label = 0; label < MAXLABELS; label++)
+           if (ischild (child = wfa->tree[state][label]))
+           {
+          if (isedge (wfa->into[state][label][0])
+              && (state_image [child + (level - 1) * wfa->states]
+              != &marker))
+             Free (state_image [child + (level - 1) * wfa->states]);
+          state_image [child + (level - 1) * wfa->states] = &marker;
+           }
       /*
        *  Range approximation with linear combination 
        */
       for (state = 1; state < (range_state > 0 ?
-			       range_state + 1 : wfa->states);
-	   state++)
-	 if (state_image [state + level * wfa->states])
-	    for (label = 0; label < MAXLABELS; label++)
-	       for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
-		    edge++)
-		  if (domain > 0	
-		      && (state_image [domain + (level - 1) * wfa->states]
-			  != NULL)
-		      && (state_image [domain + (level - 1) * wfa->states]
-			  != &marker))
-		  {
-		     Free (state_image [domain + (level - 1) * wfa->states]);
-		     state_image [domain + (level - 1) * wfa->states]
-			= &marker;
-		  }
+                   range_state + 1 : wfa->states);
+       state++)
+     if (state_image [state + level * wfa->states])
+        for (label = 0; label < MAXLABELS; label++)
+           for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
+            edge++)
+          if (domain > 0    
+              && (state_image [domain + (level - 1) * wfa->states]
+              != NULL)
+              && (state_image [domain + (level - 1) * wfa->states]
+              != &marker))
+          {
+             Free (state_image [domain + (level - 1) * wfa->states]);
+             state_image [domain + (level - 1) * wfa->states]
+            = &marker;
+          }
    }
    Free (state_image);
    Free (offset);
@@ -1109,7 +1108,7 @@ free_state_images (unsigned max_level, bool_t color, word_t **state_image,
 
 static void
 compute_state_images (unsigned max_level, word_t **simg,
-		      const u_word_t *offset, const wfa_t *wfa)
+              const u_word_t *offset, const wfa_t *wfa)
 /*
  *  Compute all state images of the 'wfa' at level {1, ... , 'max_level'}
  *  which are marked in the array 'simg' (offsets of state images
@@ -1121,8 +1120,8 @@ compute_state_images (unsigned max_level, word_t **simg,
  *  No return value.
  *
  *  Side effects:
- *	state images (given by pointers in the array 'state_image')
- *	are computed.
+ *  state images (given by pointers in the array 'state_image')
+ *  are computed.
  */
 {
    unsigned level, state;
@@ -1132,8 +1131,8 @@ compute_state_images (unsigned max_level, word_t **simg,
     */
 
    for (state = 1; state < wfa->states; state++)
-      if (simg [state] != NULL)		/* compute image at level 0 */
-	 *simg [state] = (int) (wfa->final_distribution[state] * 8 + .5) * 2;
+      if (simg [state] != NULL)     /* compute image at level 0 */
+     *simg [state] = (int) (wfa->final_distribution[state] * 8 + .5) * 2;
 
    /*
     *  Compute images of states
@@ -1153,351 +1152,351 @@ compute_state_images (unsigned max_level, word_t **simg,
       unsigned height = height_of_level (level - 1);
       
       for (state = 1; state < wfa->states; state++)
-	 if (simg [state + level * wfa->states] != NULL)
-	    for (label = 0; label < MAXLABELS; label++)
-	       if (isedge (wfa->into [state][label][0]))
-	       {
-		  unsigned  edge;
-		  int       domain;
-		  word_t   *range;	/* address of current range */
-		  bool_t    prediction_used; /* ND prediction found ? */
-
-		  /*
-		   *  Compute address of range image
-		   */
-		  if (level & 1)	/* split vertically */
-		  {
-		     range = simg [state + level * wfa->states]
-			     + label * (height_of_level (level - 1)
-					* offset [state
-						 + level * wfa->states]);
-		  }
-		  else			/* split horizontally */
-		  {
-		     range = simg [state + level * wfa->states]
-			     + label * width_of_level (level - 1);
-		  }
-
-		  /*
-		   *  Generate the state images by adding the corresponding 
-		   *  weighted state images:
-		   *  subimage [label] =
-		   *       weight_1 * image_1 + ... + weight_n * image_n
-		   */
-		  if (!ischild (domain = wfa->tree[state][label]))
-		     prediction_used = NO;
-		  else
-		  {
-		     unsigned  y;
-		     word_t   *src;
-		     word_t   *dst;
-		     unsigned  src_offset;
-		     unsigned  dst_offset;
-
-		     prediction_used = YES;
-		     /*
-		      *  Copy child image
-		      */
-		     src        = simg [domain + (level - 1) * wfa->states];
-		     src_offset = offset [domain + (level - 1) * wfa->states] ;
-		     dst        = range;
-		     dst_offset	= offset [state + level * wfa->states];
-		     for (y = height; y; y--)
-		     {
-			memcpy (dst, src, width * sizeof (word_t));
-			src += src_offset;
-			dst += dst_offset;
-		     }
-		  }
-
-		  if (!prediction_used
-		      && isedge (domain = wfa->into[state][label][0]))
-		  {
-		     /*
-		      *  If prediction is not used then the range is
-		      *  filled with the first domain. No addition is needed.
-		      */
-		     edge = 0;
-		     if (domain != 0)
-		     {
-			int	  weight;
-			word_t 	 *src;
-			unsigned  src_offset;
-
-			src        = simg [domain + ((level - 1)
-						     * wfa->states)];
-			src_offset = offset [domain + ((level - 1)
-						       * wfa->states)] - width;
-			weight     = wfa->int_weight [state][label][edge];
-			
-			if (width == 1)	/* can't add two-pixels in a row */
-			{
-			   word_t   *dst;
-			   unsigned  dst_offset;
-			   
-			   dst        = range;
-			   dst_offset = offset [state + level * wfa->states]
-					- width;
+     if (simg [state + level * wfa->states] != NULL)
+        for (label = 0; label < MAXLABELS; label++)
+           if (isedge (wfa->into [state][label][0]))
+           {
+          unsigned  edge;
+          int       domain;
+          word_t   *range;  /* address of current range */
+          bool_t    prediction_used; /* ND prediction found ? */
+
+          /*
+           *  Compute address of range image
+           */
+          if (level & 1)    /* split vertically */
+          {
+             range = simg [state + level * wfa->states]
+                 + label * (height_of_level (level - 1)
+                    * offset [state
+                         + level * wfa->states]);
+          }
+          else          /* split horizontally */
+          {
+             range = simg [state + level * wfa->states]
+                 + label * width_of_level (level - 1);
+          }
+
+          /*
+           *  Generate the state images by adding the corresponding 
+           *  weighted state images:
+           *  subimage [label] =
+           *       weight_1 * image_1 + ... + weight_n * image_n
+           */
+          if (!ischild (domain = wfa->tree[state][label]))
+             prediction_used = NO;
+          else
+          {
+             unsigned  y;
+             word_t   *src;
+             word_t   *dst;
+             unsigned  src_offset;
+             unsigned  dst_offset;
+
+             prediction_used = YES;
+             /*
+              *  Copy child image
+              */
+             src        = simg [domain + (level - 1) * wfa->states];
+             src_offset = offset [domain + (level - 1) * wfa->states] ;
+             dst        = range;
+             dst_offset = offset [state + level * wfa->states];
+             for (y = height; y; y--)
+             {
+            memcpy (dst, src, width * sizeof (word_t));
+            src += src_offset;
+            dst += dst_offset;
+             }
+          }
+
+          if (!prediction_used
+              && isedge (domain = wfa->into[state][label][0]))
+          {
+             /*
+              *  If prediction is not used then the range is
+              *  filled with the first domain. No addition is needed.
+              */
+             edge = 0;
+             if (domain != 0)
+             {
+            int   weight;
+            word_t   *src;
+            unsigned  src_offset;
+
+            src        = simg [domain + ((level - 1)
+                             * wfa->states)];
+            src_offset = offset [domain + ((level - 1)
+                               * wfa->states)] - width;
+            weight     = wfa->int_weight [state][label][edge];
+            
+            if (width == 1) /* can't add two-pixels in a row */
+            {
+               word_t   *dst;
+               unsigned  dst_offset;
+               
+               dst        = range;
+               dst_offset = offset [state + level * wfa->states]
+                    - width;
 #ifdef HAVE_SIGNED_SHIFT
-			   *dst++ = ((weight * (int) *src++) >> 10) << 1;
-#else 					/* not HAVE_SIGNED_SHIFT */
-			   *dst++ = ((weight * (int) *src++) / 1024) * 2;
+               *dst++ = ((weight * (int) *src++) >> 10) << 1;
+#else                   /* not HAVE_SIGNED_SHIFT */
+               *dst++ = ((weight * (int) *src++) / 1024) * 2;
 #endif /* not HAVE_SIGNED_SHIFT */
-			   if (height == 2) 
-			   {
-			      src += src_offset;
-			      dst += dst_offset;
+               if (height == 2) 
+               {
+                  src += src_offset;
+                  dst += dst_offset;
 #ifdef HAVE_SIGNED_SHIFT
-			      *dst++ = ((weight * (int) *src++) >> 10) << 1;
+                  *dst++ = ((weight * (int) *src++) >> 10) << 1;
 #else /* not HAVE_SIGNED_SHIFT */
-			      *dst++ = ((weight * (int) *src++) / 1024) * 2;
+                  *dst++ = ((weight * (int) *src++) / 1024) * 2;
 #endif /* not HAVE_SIGNED_SHIFT */
-			   }
-			}
-			else
-			{
-			   unsigned  y;
-			   int 	    *idst;
-			   unsigned  idst_offset;
-			   
-			   idst        = (int *) range;
-			   idst_offset = (offset [state + level * wfa->states]
-					  - width) / 2;
-			   for (y = height; y; y--)
-			   {
-			      int *comp_dst = idst + (width >> 1);
-			      
-			      for (; idst != comp_dst; )
- 			      {
-				 int tmp; /* temp. value of adjacent pixels */
+               }
+            }
+            else
+            {
+               unsigned  y;
+               int      *idst;
+               unsigned  idst_offset;
+               
+               idst        = (int *) range;
+               idst_offset = (offset [state + level * wfa->states]
+                      - width) / 2;
+               for (y = height; y; y--)
+               {
+                  int *comp_dst = idst + (width >> 1);
+                  
+                  for (; idst != comp_dst; )
+                  {
+                 int tmp; /* temp. value of adjacent pixels */
 #ifdef HAVE_SIGNED_SHIFT
-#	if BYTE_ORDER == LITTLE_ENDIAN
-                                 tmp = (((weight * (int) src [1]) >> 10) << 17)
-				       | (((weight * (int) src [0]) >> 9)
-					  & 0xfffe);
-#	else
-                                 tmp = (((weight * (int) src [0]) >> 10) << 17)
-				       | (((weight * (int) src [1]) >> 9)
-					  & 0xfffe);
-#	endif
+#   if BYTE_ORDER == LITTLE_ENDIAN
+                 tmp = (((weight * (int) src [1]) >> 10) << 17)
+                     | (((weight * (int) src [0]) >> 9)
+                        & 0xfffe);
+#   else
+                 tmp = (((weight * (int) src [0]) >> 10) << 17)
+                     | (((weight * (int) src [1]) >> 9)
+                        & 0xfffe);
+#   endif
 #else /* not HAVE_SIGNED_SHIFT */
-#	if BYTE_ORDER == LITTLE_ENDIAN
-                                 tmp = (((weight * (int) src [1]) / 1024)
-					* 131072)
-				       | (((weight * (int) src [0])/ 512)
-					  & 0xfffe);
-#	else
-                                 tmp = (((weight * (int) src [0]) / 1024)
-					* 131072)
-				       | (((weight * (int) src [1]) / 512)
-					  & 0xfffe);
-#	endif /* not WORDS_BIGENDIAN */
-#endif
-				 src    +=  2;
-				 *idst++ = tmp & 0xfffefffe;
-			      }
-			      src  += src_offset;
-			      idst += idst_offset;
-			   }
-			}
-		     }
-		     else
-		     {
-			int weight = (int) (wfa->weight[state][label][edge]
-					    * wfa->final_distribution[0]
-					    * 8 + .5) * 2;
-			/*
-			 *  Range needs domain 0
-			 *  (the constant function f(x, y) = 1),
-			 *  hence a faster algorithm is used.
-			 */
-			if (width == 1)	/* can't add two-pixels in a row */
-			{
-			   word_t   *dst;
-			   unsigned  dst_offset;
-			   
-			   dst        = range;
-			   dst_offset = offset [state + level * wfa->states]
-					- width;
-			   
-			   *dst++ = weight;
-			   if (height == 2)
-			   {
-			      dst += dst_offset;
-			      *dst++ = weight;
-			   }
-			}
-			else
-			{
-			   unsigned  x, y;
-			   int 	    *idst;
-			   unsigned  idst_offset;
-			   
-			   weight      = (weight * 65536) | (weight & 0xffff);
-			   idst	       = (int *) range;
-			   idst_offset = offset [state + level * wfa->states]
-					 / 2;
-			   for (x = width >> 1; x; x--)
-			      *idst++ = weight & 0xfffefffe;
-			   idst += (offset [state + level * wfa->states]
-				    - width) / 2;
-
-			   for (y = height - 1; y; y--)
-			   {
-			      memcpy (idst, idst - idst_offset,
-				      width * sizeof (word_t));
-			      idst += idst_offset;
-			   }
-			}
-		     }
-		     edge = 1;
-		  }
-		  else
-		     edge = 0;
-		  
-		  /*
-		   *  Add remaining weighted domain images to current range
-		   */
-		  for (; isedge (domain = wfa->into[state][label][edge]);
-		       edge++)
-		  {
-		     if (domain != 0)
-		     {
-			word_t 	 *src;
-			unsigned  src_offset;
-			int	  weight;
-
-			src        = simg [domain + (level - 1) * wfa->states];
-			src_offset = offset [domain + ((level - 1)
-						       * wfa->states)] - width;
-			weight     = wfa->int_weight [state][label][edge];
-			
-			if (width == 1)	/* can't add two-pixels in a row */
-			{
-			   word_t   *dst;
-			   unsigned  dst_offset;
-			   
-			   dst        = range;
-			   dst_offset = offset [state + level * wfa->states]
-					- width;
+#   if BYTE_ORDER == LITTLE_ENDIAN
+                 tmp = (((weight * (int) src [1]) / 1024)
+                        * 131072)
+                     | (((weight * (int) src [0])/ 512)
+                        & 0xfffe);
+#   else
+                 tmp = (((weight * (int) src [0]) / 1024)
+                        * 131072)
+                     | (((weight * (int) src [1]) / 512)
+                        & 0xfffe);
+#   endif
+#endif /* not HAVE_SIGNED_SHIFT */
+                 src    +=  2;
+                 *idst++ = tmp & 0xfffefffe;
+                  }
+                  src  += src_offset;
+                  idst += idst_offset;
+               }
+            }
+             }
+             else
+             {
+            int weight = (int) (wfa->weight[state][label][edge]
+                        * wfa->final_distribution[0]
+                        * 8 + .5) * 2;
+            /*
+             *  Range needs domain 0
+             *  (the constant function f(x, y) = 1),
+             *  hence a faster algorithm is used.
+             */
+            if (width == 1) /* can't add two-pixels in a row */
+            {
+               word_t   *dst;
+               unsigned  dst_offset;
+               
+               dst        = range;
+               dst_offset = offset [state + level * wfa->states]
+                    - width;
+               
+               *dst++ = weight;
+               if (height == 2)
+               {
+                  dst += dst_offset;
+                  *dst++ = weight;
+               }
+            }
+            else
+            {
+               unsigned  x, y;
+               int      *idst;
+               unsigned  idst_offset;
+               
+               weight      = (weight * 65536) | (weight & 0xffff);
+               idst        = (int *) range;
+               idst_offset = offset [state + level * wfa->states]
+                     / 2;
+               for (x = width >> 1; x; x--)
+                  *idst++ = weight & 0xfffefffe;
+               idst += (offset [state + level * wfa->states]
+                    - width) / 2;
+
+               for (y = height - 1; y; y--)
+               {
+                  memcpy (idst, idst - idst_offset,
+                      width * sizeof (word_t));
+                  idst += idst_offset;
+               }
+            }
+             }
+             edge = 1;
+          }
+          else
+             edge = 0;
+          
+          /*
+           *  Add remaining weighted domain images to current range
+           */
+          for (; isedge (domain = wfa->into[state][label][edge]);
+               edge++)
+          {
+             if (domain != 0)
+             {
+            word_t   *src;
+            unsigned  src_offset;
+            int   weight;
+
+            src        = simg [domain + (level - 1) * wfa->states];
+            src_offset = offset [domain + ((level - 1)
+                               * wfa->states)] - width;
+            weight     = wfa->int_weight [state][label][edge];
+            
+            if (width == 1) /* can't add two-pixels in a row */
+            {
+               word_t   *dst;
+               unsigned  dst_offset;
+               
+               dst        = range;
+               dst_offset = offset [state + level * wfa->states]
+                    - width;
 
 #ifdef HAVE_SIGNED_SHIFT
-			   *dst++ += ((weight * (int) *src++) >> 10) << 1;
+               *dst++ += ((weight * (int) *src++) >> 10) << 1;
 #else /* not HAVE_SIGNED_SHIFT */
-			   *dst++ += ((weight * (int) *src++) / 1024) * 2;
+               *dst++ += ((weight * (int) *src++) / 1024) * 2;
 #endif /* not HAVE_SIGNED_SHIFT */
-			   if (height == 2) 
-			   {
-			      src += src_offset;
-			      dst += dst_offset;
+               if (height == 2) 
+               {
+                  src += src_offset;
+                  dst += dst_offset;
 #ifdef HAVE_SIGNED_SHIFT
-			      *dst++ += ((weight * (int) *src++) >> 10) << 1;
+                  *dst++ += ((weight * (int) *src++) >> 10) << 1;
 #else /* not HAVE_SIGNED_SHIFT */
-			      *dst++ += ((weight * (int) *src++) / 1024) * 2;
+                  *dst++ += ((weight * (int) *src++) / 1024) * 2;
 #endif /* not HAVE_SIGNED_SHIFT */
-			   }
-			}
-			else
-			{
-			   int 	    *idst;
-			   unsigned  idst_offset;
-			   unsigned  y;
-			   
-			   idst        = (int *) range;
-			   idst_offset = (offset [state + level * wfa->states]
-					  - width) / 2;
-			   
-			   for (y = height; y; y--)
-			   {
-			      int *comp_dst = idst + (width >> 1);
-			      
-			      for (; idst != comp_dst;)
- 			      {
-				 int tmp; /* temp. value of adjacent pixels */
+               }
+            }
+            else
+            {
+               int      *idst;
+               unsigned  idst_offset;
+               unsigned  y;
+               
+               idst        = (int *) range;
+               idst_offset = (offset [state + level * wfa->states]
+                      - width) / 2;
+               
+               for (y = height; y; y--)
+               {
+                  int *comp_dst = idst + (width >> 1);
+                  
+                  for (; idst != comp_dst;)
+                  {
+                 int tmp; /* temp. value of adjacent pixels */
 #ifdef HAVE_SIGNED_SHIFT
-#	if BYTE_ORDER == LITTLE_ENDIAN
-                                 tmp = (((weight * (int) src [1]) >> 10) << 17)
-				       | (((weight * (int) src [0]) >> 9)
-					  & 0xfffe);
-#	else
-                                 tmp = (((weight * (int)src [0]) >> 10) << 17)
-				       | (((weight * (int)src [1]) >> 9)
-					  & 0xfffe);
-#	endif
+#   if BYTE_ORDER == LITTLE_ENDIAN
+                 tmp = (((weight * (int) src [1]) >> 10) << 17)
+                     | (((weight * (int) src [0]) >> 9)
+                        & 0xfffe);
+#   else
+                 tmp = (((weight * (int)src [0]) >> 10) << 17)
+                     | (((weight * (int)src [1]) >> 9)
+                        & 0xfffe);
+#   endif
 #else /* not HAVE_SIGNED_SHIFT */
-#	if BYTE_ORDER == LITTLE_ENDIAN
-                                 tmp = (((weight * (int) src [1]) / 1024)
-					* 131072)
-				       | (((weight * (int) src [0])/ 512)
-					  & 0xfffe);
-#	else
-                                 tmp = (((weight * (int) src [0]) / 1024)
-					* 131072)
-				       | (((weight * (int) src [1])/ 512)
-					  & 0xfffe);
-#	endif /* not WORDS_BIGENDIAN */
-#endif
-				 src +=  2;
-				 *idst = (*idst + tmp) & 0xfffefffe;
-				 idst++;
-			      }
-			      src  += src_offset;
-			      idst += idst_offset;
-			   }
-			}
-		     }
-		     else
-		     {
-			int weight = (int) (wfa->weight[state][label][edge]
-					    * wfa->final_distribution[0]
-					    * 8 + .5) * 2;
-			/*
-			 *  Range needs domain 0
-			 *  (the constant function f(x, y) = 1),
-			 *  hence a faster algorithm is used.
-			 */
-			if (width == 1)	/* can't add two-pixels in a row */
-			{
-			   word_t   *dst;
-			   unsigned  dst_offset;
-			   
-			   dst        = range;
-			   dst_offset = offset [state + level * wfa->states]
-					- width;
-			   
-			   *dst++ += weight;
-			   if (height == 2)
-			   {
-			      dst    += dst_offset;
-			      *dst++ += weight;
-			   }
-			}
-			else
-			{
-			   int 	    *idst;
-			   unsigned  idst_offset;
-			   unsigned  y;
-			   
-			   weight      = (weight * 65536) | (weight & 0xffff);
-			   idst	       = (int *) range;
-			   idst_offset = (offset [state + level * wfa->states]
-					  - width) /2;
-			   
-			   for (y = height; y; y--)
-			   {
-			      int *comp_dst = idst + (width >> 1);
-			      
-			      for (; idst != comp_dst; )
-			      {
-				 *idst = (*idst + weight) & 0xfffefffe;
+#   if BYTE_ORDER == LITTLE_ENDIAN
+                 tmp = (((weight * (int) src [1]) / 1024)
+                        * 131072)
+                     | (((weight * (int) src [0])/ 512)
+                        & 0xfffe);
+#   else
+                 tmp = (((weight * (int) src [0]) / 1024)
+                        * 131072)
+                     | (((weight * (int) src [1])/ 512)
+                        & 0xfffe);
+#   endif
+#endif /* not HAVE_SIGNED_SHIFT */
+                 src +=  2;
+                 *idst = (*idst + tmp) & 0xfffefffe;
+                 idst++;
+                  }
+                  src  += src_offset;
+                  idst += idst_offset;
+               }
+            }
+             }
+             else
+             {
+            int weight = (int) (wfa->weight[state][label][edge]
+                        * wfa->final_distribution[0]
+                        * 8 + .5) * 2;
+            /*
+             *  Range needs domain 0
+             *  (the constant function f(x, y) = 1),
+             *  hence a faster algorithm is used.
+             */
+            if (width == 1) /* can't add two-pixels in a row */
+            {
+               word_t   *dst;
+               unsigned  dst_offset;
+               
+               dst        = range;
+               dst_offset = offset [state + level * wfa->states]
+                    - width;
+               
+               *dst++ += weight;
+               if (height == 2)
+               {
+                  dst    += dst_offset;
+                  *dst++ += weight;
+               }
+            }
+            else
+            {
+               int      *idst;
+               unsigned  idst_offset;
+               unsigned  y;
+               
+               weight      = (weight * 65536) | (weight & 0xffff);
+               idst        = (int *) range;
+               idst_offset = (offset [state + level * wfa->states]
+                      - width) /2;
+               
+               for (y = height; y; y--)
+               {
+                  int *comp_dst = idst + (width >> 1);
+                  
+                  for (; idst != comp_dst; )
+                  {
+                 *idst = (*idst + weight) & 0xfffefffe;
                                  idst++;
-			      }
-			      idst += idst_offset;
-			   }
-			}
-		     }
-		  }
-	       } 
+                  }
+                  idst += idst_offset;
+               }
+            }
+             }
+          }
+           } 
    }
 }
 
@@ -1509,24 +1508,24 @@ duplicate_state_image (const word_t *domain, unsigned offset, unsigned level)
  *  to the lock 'pixels'.
  *
  *  Return value:
- *	pointer to the new domain block
+ *  pointer to the new domain block
  */
 {
    word_t *dst, *pixels;
-   int	   y, n;
+   int     y, n;
 
    dst = pixels = Calloc (size_of_level (level), sizeof (word_t));
 
    if (domain)
       for (y = height_of_level (level); y; y--)
       {
-	 memcpy (dst, domain, width_of_level (level) * sizeof (word_t));
-	 dst    += width_of_level (level);
-	 domain += offset;
+     memcpy (dst, domain, width_of_level (level) * sizeof (word_t));
+     dst    += width_of_level (level);
+     domain += offset;
       }
-   else					/* state 0 */
+   else                 /* state 0 */
       for (n = size_of_level (level); n; n--)
-	 *dst++ = (int) (128 * 8 + .5) * 2;
+     *dst++ = (int) (128 * 8 + .5) * 2;
 
    return pixels;
 }
diff --git a/converter/other/fiasco/codec/dfiasco.c b/converter/other/fiasco/codec/dfiasco.c
index 1cdfc672..2fdec573 100644
--- a/converter/other/fiasco/codec/dfiasco.c
+++ b/converter/other/fiasco/codec/dfiasco.c
@@ -14,8 +14,12 @@
  *  $State: Exp $
  */
 
+#include <stdlib.h>
 #include <string.h>
 
+#include "pm_c_util.h"
+#include "nstring.h"
+
 #include "config.h"
 
 #include "types.h"
@@ -112,7 +116,7 @@ fiasco_decoder_new (const char *filename, const fiasco_d_options_t *options)
 	    {
 	       set_error (_("Magnifaction factor `%d' is too large. "
 			    "Maximium value is %d."),
-			  dfiasco->enlarge_factor, max (0, n - 1));
+			  dfiasco->enlarge_factor, MAX(0, n - 1));
 	       fiasco_decoder_delete (decoder);
 	       return NULL;
 	    }
@@ -129,7 +133,7 @@ fiasco_decoder_new (const char *filename, const fiasco_d_options_t *options)
 	    {
 	       set_error (_("Magnifaction factor `%d' is too small. "
 			    "Minimum value is %d."),
-			  dfiasco->enlarge_factor, - max (0, n - 1));
+			  dfiasco->enlarge_factor, - MAX(0, n - 1));
 	       fiasco_decoder_delete (decoder);
 	       return NULL;
 	    }
diff --git a/converter/other/fiasco/codec/domain-pool.c b/converter/other/fiasco/codec/domain-pool.c
index 09f854a6..7cc3900e 100644
--- a/converter/other/fiasco/codec/domain-pool.c
+++ b/converter/other/fiasco/codec/domain-pool.c
@@ -17,16 +17,10 @@
 #include "config.h"
 
 #include <math.h>
+#include <stdlib.h>
+#include <string.h>
 
-#if STDC_HEADERS
-#   include <stdlib.h>
-#endif /* not STDC_HEADERS */
-
-#if HAVE_STRING_H
-#   include <string.h>
-#else /* not HAVE_STRING_H */
-#   include <strings.h>
-#endif /* not HAVE_STRING_H */
+#include "pm_c_util.h"
 
 #include "types.h"
 #include "macros.h"
@@ -466,7 +460,7 @@ qac_chroma (unsigned max_domains, const wfa_t *wfa, void *model)
                                 max_domains, wfa);
         for (n = 0; n < max_domains && domains [n] >= 0; n++)
             states [n] = domains [n];
-        max_domains = min (max_domains, n);
+        max_domains = MIN(max_domains, n);
         Free (domains);
 
         for (old = 0, new = 0; new < max_domains && old < qac_model->n; old++)
@@ -854,7 +848,7 @@ rle_chroma (unsigned max_domains, const wfa_t *wfa, void *model)
             states [n] = domains [n];
 
         assert (states [0] == 0);
-        max_domains = min (max_domains, n);
+        max_domains = MIN(max_domains, n);
         Free (domains);
 
         Free (rle_model->states);
diff --git a/converter/other/fiasco/codec/ip.c b/converter/other/fiasco/codec/ip.c
index caa97baf..ade0d916 100644
--- a/converter/other/fiasco/codec/ip.c
+++ b/converter/other/fiasco/codec/ip.c
@@ -282,7 +282,7 @@ standard_ip_image_state (unsigned address, unsigned level, unsigned domain,
    real_t   ip = 0, *imageptr, *stateptr;
 
    if (level > c->options.images_level)
-      error ("Level %d not supported.", level);
+      error ("We cannot interpret a Level %d image.", level);
    
    imageptr = &c->pixels [address * size_of_level (level)];
 
@@ -311,7 +311,7 @@ standard_ip_state_state (unsigned domain1, unsigned domain2, unsigned level,
    real_t   ip = 0, *state1ptr, *state2ptr;
 
    if (level > c->options.images_level)
-      error ("Level %d not supported.", level);
+      error ("We cannot interpret and image with Level %d.", level);
 
    state1ptr = c->images_of_state [domain1] + address_of_level (level);
    state2ptr = c->images_of_state [domain2] + address_of_level (level);
diff --git a/converter/other/fiasco/codec/motion.c b/converter/other/fiasco/codec/motion.c
index 92951281..876a2998 100644
--- a/converter/other/fiasco/codec/motion.c
+++ b/converter/other/fiasco/codec/motion.c
@@ -17,11 +17,9 @@
 
 #include "config.h"
 
-#if HAVE_STRING_H
-#	include <string.h>
-#else /* not HAVE_STRING_H */
-#	include <strings.h>
-#endif /* not HAVE_STRING_H */
+#include <string.h>
+
+#include "pm_c_util.h"
 
 #include "types.h"
 #include "macros.h"
@@ -54,10 +52,10 @@ restore_mc (int enlarge_factor, image_t *image, const image_t *past,
 
 #define FX(v) ((image->format == FORMAT_4_2_0) && band != Y ? ((v) / 2) : v)
    
-   mcblock1 = Calloc (size_of_level (max ((int) wfa->wfainfo->p_max_level
+   mcblock1 = Calloc (size_of_level (MAX((int) wfa->wfainfo->p_max_level
 					  + 2 * enlarge_factor, 0)),
 		      sizeof (word_t));
-   mcblock2 = Calloc (size_of_level (max ((int) wfa->wfainfo->p_max_level
+   mcblock2 = Calloc (size_of_level (MAX((int) wfa->wfainfo->p_max_level
 					  + 2 * enlarge_factor, 0)),
 		      sizeof (word_t));
 
diff --git a/converter/other/fiasco/codec/mwfa.c b/converter/other/fiasco/codec/mwfa.c
index 6f0af8be..43a7dae2 100644
--- a/converter/other/fiasco/codec/mwfa.c
+++ b/converter/other/fiasco/codec/mwfa.c
@@ -18,12 +18,9 @@
 #include "config.h"
 
 #include <ctype.h>
+#include <string.h>
 
-#if HAVE_STRING_H
-#	include <string.h>
-#else /* not HAVE_STRING_H */
-#	include <strings.h>
-#endif /* not HAVE_STRING_H */
+#include "pm_c_util.h"
 
 #include "types.h"
 #include "macros.h"
@@ -441,7 +438,7 @@ find_B_frame_mc (word_t *mcpe, real_t price, range_t *range,
    else					/* local exhaustive search */
    {
       /*
-       *  Keep forward and backward mv due to time constraints
+       *  Keep forward and backward mv because of time constraints
        */
 
       ifx = fx;
@@ -813,10 +810,10 @@ find_second_mv (real_t price, const image_t *original,
 
    sr = wi->search_range;
 
-   y0 = max ((int) -sr, *my - (int) local_range);
-   y1 = min ((int) sr, *my + (int) local_range);
-   x0 = max ((int) -sr, *mx - (int) local_range);
-   x1 = min ((int) sr, *mx + (int) local_range);
+   y0 = MAX((int) -sr, *my - (int) local_range);
+   y1 = MIN((int) sr, *my + (int) local_range);
+   x0 = MAX((int) -sr, *mx - (int) local_range);
+   x1 = MIN((int) sr, *mx + (int) local_range);
 
    *mx = *my = 0;
 
diff --git a/converter/other/fiasco/codec/options.c b/converter/other/fiasco/codec/options.c
index 77dbaf00..c8e4d2e2 100644
--- a/converter/other/fiasco/codec/options.c
+++ b/converter/other/fiasco/codec/options.c
@@ -20,12 +20,11 @@
 #include "config.h"
 
 #include <string.h>
-#if STDC_HEADERS
-#	include <stdlib.h>
-#endif /* not STDC_HEADERS */
-
+#include <stdlib.h>
 #include <stdio.h>
 
+#include "nstring.h"
+
 #include "types.h"
 #include "macros.h"
 #include "error.h"
diff --git a/converter/other/fiasco/codec/prediction.c b/converter/other/fiasco/codec/prediction.c
index 351ba9df..e056d10f 100644
--- a/converter/other/fiasco/codec/prediction.c
+++ b/converter/other/fiasco/codec/prediction.c
@@ -17,11 +17,7 @@
 
 #include "config.h"
 
-#if HAVE_STRING_H
-#	include <string.h>
-#else /* not HAVE_STRING_H */
-#	include <strings.h>
-#endif /* not HAVE_STRING_H */
+#include <string.h>
 
 #include "types.h"
 #include "macros.h"
diff --git a/converter/other/fiasco/codec/subdivide.c b/converter/other/fiasco/codec/subdivide.c
index b7982716..2ace18e4 100644
--- a/converter/other/fiasco/codec/subdivide.c
+++ b/converter/other/fiasco/codec/subdivide.c
@@ -16,11 +16,9 @@
 
 #include "config.h"
 
-#if HAVE_STRING_H
-#	include <string.h>
-#else /* not HAVE_STRING_H */
-#	include <strings.h>
-#endif /* not HAVE_STRING_H */
+#include <string.h>
+
+#include "pm_c_util.h"
 
 #include "types.h"
 #include "macros.h"
@@ -293,7 +291,7 @@ subdivide (real_t max_costs, unsigned band, int y_state, range_t *range,
 			  : rrange.y;
 	 
 	 /* 
-	  *  If neccessary compute the inner products of the new states
+	  *  If necessary compute the inner products of the new states
 	  *  (generated during the recursive approximation of child [0])
 	  */
 	 if (label && rrange.level <= c->options.lc_max_level)
@@ -304,7 +302,7 @@ subdivide (real_t max_costs, unsigned band, int y_state, range_t *range,
 	  *  Abort the recursion if 'subdivide_costs' exceed 'lincomb_costs'
 	  *  or 'max_costs'.
 	  */
-	 remaining_costs = min (lincomb_costs, max_costs) - subdivide_costs;
+	 remaining_costs = MIN(lincomb_costs, max_costs) - subdivide_costs;
 
 	 if (remaining_costs > 0)	/* still a way for improvement */
 	 {
@@ -356,7 +354,7 @@ subdivide (real_t max_costs, unsigned band, int y_state, range_t *range,
 	  *  If costs of subdivision exceed costs of linear combination 
 	  *  then abort recursion.
 	  */
-	 if (subdivide_costs >= min (lincomb_costs, max_costs)) 
+	 if (subdivide_costs >= MIN(lincomb_costs, max_costs)) 
 	 {
 	    subdivide_costs = MAXCOSTS;
 	    break; 
@@ -386,28 +384,28 @@ subdivide (real_t max_costs, unsigned band, int y_state, range_t *range,
     */
    if (try_mc || try_nd)		/* try prediction */
    {
-      real_t prediction_costs;	/* Costs arising from approx. the current
-				   range with prediction */
-
-      prediction_costs
-	 = predict_range (min (min (lincomb_costs, subdivide_costs),
-			       max_costs),
-			  price, range, wfa, c, band, y_state, states,
-			  &tree_model, &p_tree_model, domain_model,
-			  d_domain_model, coeff_model, d_coeff_model);
-      if (prediction_costs < MAXCOSTS)	/* prediction has smallest costs */
-      {
-	 c->domain_pool->model_free (domain_model);
-	 c->d_domain_pool->model_free (d_domain_model);
-	 c->domain_pool->model_free (lc_domain_model);
-	 c->d_domain_pool->model_free (lc_d_domain_model);
-	 c->coeff->model_free (coeff_model);
-	 c->d_coeff->model_free (d_coeff_model);
-	 c->coeff->model_free (lc_coeff_model);
-	 c->d_coeff->model_free (lc_d_coeff_model);
+       real_t prediction_costs;	/* Costs arising from approx. the current
+                                   range with prediction */
+
+       prediction_costs
+           = predict_range (MIN(MIN(lincomb_costs, subdivide_costs),
+                                max_costs),
+                            price, range, wfa, c, band, y_state, states,
+                            &tree_model, &p_tree_model, domain_model,
+                            d_domain_model, coeff_model, d_coeff_model);
+       if (prediction_costs < MAXCOSTS)	/* prediction has smallest costs */
+       {
+           c->domain_pool->model_free (domain_model);
+           c->d_domain_pool->model_free (d_domain_model);
+           c->domain_pool->model_free (lc_domain_model);
+           c->d_domain_pool->model_free (lc_d_domain_model);
+           c->coeff->model_free (coeff_model);
+           c->d_coeff->model_free (d_coeff_model);
+           c->coeff->model_free (lc_coeff_model);
+           c->d_coeff->model_free (lc_d_coeff_model);
 	 
-	 return prediction_costs;
-      }
+           return prediction_costs;
+       }
    }
 
    if (lincomb_costs >= MAXCOSTS && subdivide_costs >= MAXCOSTS)
diff --git a/converter/other/fiasco/codec/tiling.c b/converter/other/fiasco/codec/tiling.c
index e820f7fb..21e4428a 100644
--- a/converter/other/fiasco/codec/tiling.c
+++ b/converter/other/fiasco/codec/tiling.c
@@ -16,9 +16,9 @@
 
 #include "config.h"
 
-#if STDC_HEADERS
-#	include <stdlib.h>
-#endif /* not STDC_HEADERS */
+#include <stdlib.h>
+
+#include "pm_c_util.h"
 
 #include "types.h"
 #include "macros.h"
@@ -29,22 +29,7 @@
 #include "wfalib.h"
 #include "tiling.h"
 
-/*****************************************************************************
-
-				prototypes
-  
-*****************************************************************************/
-
-static int
-cmpdecvar (const void *value1, const void *value2);
-static int
-cmpincvar (const void *value1, const void *value2);
-
-/*****************************************************************************
 
-				public code
-  
-*****************************************************************************/
 
 typedef struct var_list
 {
@@ -52,6 +37,38 @@ typedef struct var_list
    real_t variance;			/* variance of tile */
 } var_list_t;
 
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn cmpincvar;
+#endif
+
+static int
+cmpincvar(const void * const value1,
+          const void * const value2) {
+/*----------------------------------------------------------------------------
+  Sorts by increasing variances (quicksort sorting function)
+-----------------------------------------------------------------------------*/
+    return
+        ((var_list_t *) value1)->variance - ((var_list_t *) value2)->variance;
+}
+
+
+
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn cmpdecvar;
+#endif
+
+static int
+cmpdecvar(const void * const value1,
+          const void * const value2) {
+/*----------------------------------------------------------------------------
+  Sorts by decreasing variances (quicksort sorting function).
+-----------------------------------------------------------------------------*/
+    return
+        ((var_list_t *) value2)->variance - ((var_list_t *) value1)->variance;
+}
+
+
+
 tiling_t *
 alloc_tiling (fiasco_tiling_e method, unsigned tiling_exponent,
 	      unsigned image_level)
@@ -146,7 +163,7 @@ perform_tiling (const image_t *image, tiling_t *tiling)
 	 unsigned    number;		/* number of image tiles */
 	 unsigned    lx       = log2 (image->width - 1) + 1; /* x level */
 	 unsigned    ly       = log2 (image->height - 1) + 1; /* y level */
-	 unsigned    level    = max (lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
+	 unsigned    level    = MAX(lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
 	 var_list_t *var_list = Calloc (tiles, sizeof (var_list_t));
 	 
 	 /*
@@ -207,33 +224,10 @@ perform_tiling (const image_t *image, tiling_t *tiling)
       }
       else
       {
-	 warning ("Unsupported image tiling method.\n"
+	 warning ("We do not know the tiling method.\n"
 		  "Skipping image tiling step.");
 	 tiling->exponent = 0;
       }
    }
 }
 
-/*****************************************************************************
-
-				private code
-  
-*****************************************************************************/
-
-static int
-cmpincvar (const void *value1, const void *value2)
-/*
- *  Sorts by increasing variances (quicksort sorting function).
- */
-{
-  return ((var_list_t *) value1)->variance - ((var_list_t *) value2)->variance;
-}
-
-static int
-cmpdecvar (const void *value1, const void *value2)
-/*
- *  Sorts by decreasing variances (quicksort sorting function).
- */
-{
-  return ((var_list_t *) value2)->variance - ((var_list_t *) value1)->variance;
-}
diff --git a/converter/other/fiasco/codec/wfa.h b/converter/other/fiasco/codec/wfa.h
index 8b9793f2..9253affd 100644
--- a/converter/other/fiasco/codec/wfa.h
+++ b/converter/other/fiasco/codec/wfa.h
@@ -19,7 +19,7 @@
 
 #define MAXEDGES  5
 #define MAXSTATES 6000
-#define MAXLABELS 2			/* only bintree supported anymore */
+#define MAXLABELS 2			/* only bintree possible anymore */
 #define MAXLEVEL  22 
 
 #define FIASCO_BINFILE_RELEASE   2
@@ -122,7 +122,7 @@ typedef struct wfa
    real_t	*final_distribution;    /* one pixel images */
    byte_t	*level_of_state;	/* level of the image part which is
 					   represented by the current state */
-   byte_t	*domain_type;		/* Bit_0==1: auxilliary state
+   byte_t	*domain_type;		/* Bit_0==1: auxiliary state
 					   Bit_1==1: used for Y compr */
    mv_t		(*mv_tree)[MAXLABELS];	/* motion vectors */
    word_t	(*tree)[MAXLABELS];	/* bintree partitioning */
diff --git a/converter/other/fiasco/codec/wfalib.c b/converter/other/fiasco/codec/wfalib.c
index a3acb975..61d64d2f 100644
--- a/converter/other/fiasco/codec/wfalib.c
+++ b/converter/other/fiasco/codec/wfalib.c
@@ -19,12 +19,11 @@
 
 #include "config.h"
 
-#if STDC_HEADERS
-#	include <stdlib.h>
-#endif /* not STDC_HEADERS */
-
+#include <stdlib.h>
 #include <string.h>
 
+#include "pm_c_util.h"
+
 #include "types.h"
 #include "macros.h"
 #include "error.h"
@@ -218,7 +217,7 @@ compute_hits (unsigned from, unsigned to, unsigned n, const wfa_t *wfa)
 
    qsort (hits + 1, to - 1, sizeof (pair_t), sort_desc_pair);
 
-   n       = min (to, n);
+   n       = MIN(to, n);
    domains = Calloc (n + 1, sizeof (word_t));
 
    for (domain = 0; domain < (int) n && (!domain || hits [domain].key);
@@ -486,7 +485,7 @@ compute_spiral (int *vorder, unsigned image_width, unsigned image_height,
    
    lx     = log2 (image_width - 1) + 1;
    ly     = log2 (image_height - 1) + 1;
-   level  = max (lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
+   level  = MAX(lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
    tiles  = 1 << tiling_exp;		/* Number of image tiles */
    width  = width_of_level (level - tiling_exp);
    height = height_of_level (level - tiling_exp);
diff --git a/converter/other/fiasco/config.h b/converter/other/fiasco/config.h
index 64b905f8..57b3518d 100644
--- a/converter/other/fiasco/config.h
+++ b/converter/other/fiasco/config.h
@@ -56,9 +56,6 @@
 /* Define if you have the strcasecmp function.  */
 #define HAVE_STRCASECMP 1
 
-/* Define if you have the strdup function.  */
-#define HAVE_STRDUP 1
-
 /* Define if you have the <X11/extensions/XShm.h> header file.  */
 /* #undef HAVE_X11_EXTENSIONS_XSHM_H */
 
diff --git a/converter/other/fiasco/display.c b/converter/other/fiasco/display.c
index 9e531149..cf160329 100644
--- a/converter/other/fiasco/display.c
+++ b/converter/other/fiasco/display.c
@@ -27,16 +27,8 @@
 #include <X11/Xutil.h>
 #include <X11/keysym.h>
 
-#if STDC_HEADERS
-#	include <stdlib.h>
-#	include <string.h>
-#else /* not STDC_HEADERS */
-#	if HAVE_STRING_H
-#		include <string.h>
-#	else /* not HAVE_STRING_H */
-#		include <strings.h>
-#	endif /* not HAVE_STRING_H */
-#endif /* not STDC_HEADERS */
+#include <stdlib.h>
+#include <string.h>
 
 #include "types.h"
 #include "macros.h"
@@ -315,7 +307,8 @@ alloc_ximage (x11_info_t *xinfo, unsigned width, unsigned height)
       shmem_flag = 0;
       if (fiasco_get_verbosity ())
 	 fprintf (stderr,
-		  "Shared memory not supported\nReverting to normal Xlib.\n");
+              "Shared memory does not work on this system\n"
+              "Reverting to normal Xlib.\n");
    }
 
    if (shmem_flag)
diff --git a/converter/other/fiasco/fiascotopnm.c b/converter/other/fiasco/fiascotopnm.c
index 6d8b6f7f..dfba2256 100644
--- a/converter/other/fiasco/fiascotopnm.c
+++ b/converter/other/fiasco/fiascotopnm.c
@@ -25,6 +25,8 @@
 #include <string.h>
 #include <math.h>
 
+#include "nstring.h"
+
 #include "types.h"
 #include "macros.h"
 
@@ -176,21 +178,21 @@ checkargs (int argc, char **argv, bool_t *double_resolution, bool_t *panel,
     *options = fiasco_d_options_new ();
 
     {
-        int n = *((int *) parameter_value (params, "smoothing"));
+        int const n = *((int *) parameter_value (params, "smoothing"));
       
-        if (!fiasco_d_options_set_smoothing (*options, max (-1, n)))
+        if (!fiasco_d_options_set_smoothing (*options, MAX(-1, n)))
             error (fiasco_get_error_message ());
     }
 
     {
-        int n = *((int *) parameter_value (params, "magnify"));
+        int const n = *((int *) parameter_value (params, "magnify"));
       
         if (!fiasco_d_options_set_magnification (*options, n))
             error (fiasco_get_error_message ());
     }
    
     {
-        bool_t n = *((bool_t *) parameter_value (params, "fast"));
+        bool_t const n = *((bool_t *) parameter_value (params, "fast"));
       
         if (!fiasco_d_options_set_4_2_0_format (*options, n > 0 ? YES : NO))
             error (fiasco_get_error_message ());
diff --git a/converter/other/fiasco/getopt.c b/converter/other/fiasco/getopt.c
index 0b2d1b75..2f45c7cc 100644
--- a/converter/other/fiasco/getopt.c
+++ b/converter/other/fiasco/getopt.c
@@ -73,15 +73,7 @@
 #include <unistd.h>
 #endif	/* GNU C library.  */
 
-#ifdef VMS
-#include <unixlib.h>
-#if HAVE_STRING_H - 0
-#include <string.h>
-#endif
-#endif
-
-#if defined (WIN32) && !defined (__CYGWIN32__)
-/* It's not Unix, really.  See?  Capital letters.  */
+#if MSVCRT
 #include <windows.h>
 #define getpid() GetCurrentProcessId()
 #endif
diff --git a/converter/other/fiasco/input/basis.c b/converter/other/fiasco/input/basis.c
index cef075e6..4a748f61 100644
--- a/converter/other/fiasco/input/basis.c
+++ b/converter/other/fiasco/input/basis.c
@@ -16,6 +16,8 @@
 
 #include "config.h"
 
+#include "nstring.h"
+
 #include "types.h"
 #include "macros.h"
 #include "error.h"
diff --git a/converter/other/fiasco/input/read.c b/converter/other/fiasco/input/read.c
index 26bae7e4..e6e2d7e8 100644
--- a/converter/other/fiasco/input/read.c
+++ b/converter/other/fiasco/input/read.c
@@ -23,6 +23,8 @@
 
 #include <string.h>
 
+#include "nstring.h"
+
 #include "types.h"
 #include "macros.h"
 #include "error.h"
@@ -155,7 +157,7 @@ open_wfa (const char *filename, wfa_info_t *wi)
 	 unsigned lx = log2 (wi->width - 1) + 1;
 	 unsigned ly = log2 (wi->height - 1) + 1;
       
-	 wi->level = max (lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
+	 wi->level = MAX(lx, ly) * 2 - ((ly == lx + 1) ? 1 : 0);
       }
       wi->chroma_max_states = wi->color ? read_rice_code (rice_k, input) : -1;
       wi->p_min_level       = read_rice_code (rice_k, input);
@@ -275,7 +277,7 @@ read_basis (const char *filename, wfa_t *wfa)
     *   string		|MAGIC Number "Wfa"
     *	int		|Number of basis states 'N'
     *	bool_t-array[N]	|use vector in linear combinations,
-    *			|0: don't use vector (auxilliary state)
+    *			|0: don't use vector (auxiliary state)
     *			|1: use vector in linear combinations
     *	float-array[N]	|final distribution of every state
     *
@@ -395,7 +397,7 @@ read_next_wfa (wfa_t *wfa, bitfile_t *input)
 
    /*
     *  Compute domain pool.
-    *  Large images have not been used due to image tiling.
+    *  Large images have not been used because of image tiling.
     */
    {
       unsigned state;
diff --git a/converter/other/fiasco/input/weights.c b/converter/other/fiasco/input/weights.c
index 55339980..15c35731 100644
--- a/converter/other/fiasco/input/weights.c
+++ b/converter/other/fiasco/input/weights.c
@@ -1,8 +1,8 @@
 /*
- *  weights.c:		Input of weights
+ *  weights.c:          Input of weights
  *
- *  Written by:		Ullrich Hafner
- *		
+ *  Written by:         Ullrich Hafner
+ *              
  *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
  *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
  */
@@ -16,6 +16,8 @@
 
 #include "config.h"
 
+#include "pm_c_util.h"
+
 #include "types.h"
 #include "macros.h"
 #include "error.h"
@@ -29,7 +31,7 @@
 
 /*****************************************************************************
 
-				public code
+                                public code
   
 *****************************************************************************/
 
@@ -42,17 +44,17 @@ read_weights (unsigned total, wfa_t *wfa, bitfile_t *input)
  *  No return value.
  *
  *  Side effects:
- *	'wfa->weights' are filled with the decoded values
+ *      'wfa->weights' are filled with the decoded values
  */
 {
-   unsigned	    state;
-   unsigned	    label;
-   unsigned	    edge;		/* current edge */
-   unsigned	   *weights_array;	/* array of weights to encode */
-   unsigned	   *level_array;	/* array of corresponding levels */
-   unsigned	    offset1, offset2;	/* prob. model offsets. */
-   unsigned	    offset3, offset4;	/* prob. model offsets. */
-   bool_t	    delta_approx = NO; 	/* true if delta has been used */
+   unsigned         state;
+   unsigned         label;
+   unsigned         edge;               /* current edge */
+   unsigned        *weights_array;      /* array of weights to encode */
+   unsigned        *level_array;        /* array of corresponding levels */
+   unsigned         offset1, offset2;   /* prob. model offsets. */
+   unsigned         offset3, offset4;   /* prob. model offsets. */
+   bool_t           delta_approx = NO;  /* true if delta has been used */
    
    /*
     *  Check whether delta approximation has been used
@@ -60,52 +62,54 @@ read_weights (unsigned total, wfa_t *wfa, bitfile_t *input)
    for (state = wfa->basis_states; state < wfa->states; state++)
       if (wfa->delta_state [state])
       {
-	 delta_approx = YES;
-	 break;
+         delta_approx = YES;
+         break;
       }
   
    /*
     *  Generate array of corresponding levels (context of probability model)
     */
    {
-      int 	min_level, max_level; 	/* min and max range level */
-      int 	d_min_level, d_max_level; /* min and max range level (delta) */
-      unsigned *lptr;			/* pointer to current corresp. level */
-      int	domain;			/* current domain */
-      bool_t	dc, d_dc;		/* indicates whether DC is used */
+      int       min_level, max_level;   /* min and max range level */
+      int       d_min_level, d_max_level; /* min and max range level (delta) */
+      unsigned *lptr;                   /* pointer to current corresp. level */
+      int       domain;                 /* current domain */
+      bool_t    dc, d_dc;               /* indicates whether DC is used */
 
       /*
        *  Compute minimum and maximum level of delta and normal approximations
        */
       min_level = d_min_level = MAXLEVEL;
       max_level = d_max_level = 0;
-      dc 	= d_dc	   = NO;
+      dc        = d_dc     = NO;
    
       for (state = wfa->basis_states; state < wfa->states; state++)
-	 for (label = 0; label < MAXLABELS; label++)
-	    if (isrange (wfa->tree [state][label]))
-	    {
-	       if (delta_approx && wfa->delta_state [state])
-	       {
-		  d_min_level = min (d_min_level,
-				     wfa->level_of_state [state] - 1);
-		  d_max_level = max (d_max_level,
-				     wfa->level_of_state [state] - 1);
-		  if (wfa->into [state][label][0] == 0)
-		     d_dc = YES;
-	       }
-	       else
-	       {
-		  min_level = min (min_level, wfa->level_of_state [state] - 1);
-		  max_level = max (max_level, wfa->level_of_state [state] - 1);
-		  if (wfa->into [state][label][0] == 0)
-		     dc = YES;
-	       }
-	    }
-      if (min_level > max_level)		/* no lc found */
-	 max_level = min_level - 1;
+          for (label = 0; label < MAXLABELS; label++)
+              if (isrange (wfa->tree [state][label]))
+              {
+                  if (delta_approx && wfa->delta_state [state])
+                  {
+                      d_min_level =
+                          MIN(d_min_level, wfa->level_of_state [state] - 1);
+                      d_max_level =
+                          MAX(d_max_level, wfa->level_of_state [state] - 1);
+                      if (wfa->into [state][label][0] == 0)
+                          d_dc = YES;
+                  }
+                  else
+                  {
+                      min_level =
+                          MIN(min_level, wfa->level_of_state [state] - 1);
+                      max_level =
+                          MAX(max_level, wfa->level_of_state [state] - 1);
+                      if (wfa->into [state][label][0] == 0)
+                          dc = YES;
+                  }
+              }
+      if (min_level > max_level)                /* no lc found */
+         max_level = min_level - 1;
       if (d_min_level > d_max_level)
-	 d_max_level = d_min_level - 1;
+         d_max_level = d_min_level - 1;
 
       offset1 = dc ? 1 : 0;
       offset2 = offset1 + (d_dc ? 1 : 0);
@@ -114,47 +118,47 @@ read_weights (unsigned total, wfa_t *wfa, bitfile_t *input)
 
       lptr = level_array = Calloc (total, sizeof (int));
       for (state = wfa->basis_states; state < wfa->states; state++)
-	 for (label = 0; label < MAXLABELS; label++)
-	    if (isrange (wfa->tree[state][label]))
-	       for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
-		    edge++)
-	       {
-		  if ((unsigned) (lptr - level_array) >= total)
-		     error ("Can't read more than %d weights.", total);
-		  if (domain)
-		  {
-		     if (delta_approx && wfa->delta_state [state])
-			*lptr++ = offset3 + wfa->level_of_state [state]
-				  - 1 - d_min_level;
-		     else
-			*lptr++ = offset2 + wfa->level_of_state [state]
-				  - 1 - min_level;
-		  }
-		  else
-		     *lptr++ = delta_approx && wfa->delta_state [state]
-			       ? offset1 : 0;
-	       }
+         for (label = 0; label < MAXLABELS; label++)
+            if (isrange (wfa->tree[state][label]))
+               for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
+                    edge++)
+               {
+                  if ((unsigned) (lptr - level_array) >= total)
+                     error ("Can't read more than %d weights.", total);
+                  if (domain)
+                  {
+                     if (delta_approx && wfa->delta_state [state])
+                        *lptr++ = offset3 + wfa->level_of_state [state]
+                                  - 1 - d_min_level;
+                     else
+                        *lptr++ = offset2 + wfa->level_of_state [state]
+                                  - 1 - min_level;
+                  }
+                  else
+                     *lptr++ = delta_approx && wfa->delta_state [state]
+                               ? offset1 : 0;
+               }
    }
 
    /*
     *  Decode the list of weights with an arithmetic decoder
     */
    {
-      unsigned	      i;
-      unsigned	     *c_symbols = Calloc (offset4, sizeof (unsigned));
-      const unsigned  scale 	= 500; 	/* scaling of probability model */
+      unsigned        i;
+      unsigned       *c_symbols = Calloc (offset4, sizeof (unsigned));
+      const unsigned  scale     = 500;  /* scaling of probability model */
 
       c_symbols [0] = 1 << (wfa->wfainfo->dc_rpf->mantissa_bits + 1);
       if (offset1 != offset2)
-	 c_symbols [offset1] = 1 << (wfa->wfainfo->d_dc_rpf->mantissa_bits
-				     + 1);
+         c_symbols [offset1] = 1 << (wfa->wfainfo->d_dc_rpf->mantissa_bits
+                                     + 1);
       for (i = offset2; i < offset3; i++)
-	 c_symbols [i] = 1 << (wfa->wfainfo->rpf->mantissa_bits + 1);
+         c_symbols [i] = 1 << (wfa->wfainfo->rpf->mantissa_bits + 1);
       for (; i < offset4; i++)
-	 c_symbols [i] = 1 << (wfa->wfainfo->d_rpf->mantissa_bits + 1);
+         c_symbols [i] = 1 << (wfa->wfainfo->d_rpf->mantissa_bits + 1);
       
       weights_array = decode_array (input, level_array, c_symbols,
-				    offset4, total, scale);
+                                    offset4, total, scale);
       Free (c_symbols);
    }
    Free (level_array);
@@ -163,36 +167,36 @@ read_weights (unsigned total, wfa_t *wfa, bitfile_t *input)
     *  Update transitions with decoded weights
     */
    {
-      unsigned *wptr = weights_array;	/* pointer to current weight */
-      int	domain;			/* current domain */
+      unsigned *wptr = weights_array;   /* pointer to current weight */
+      int       domain;                 /* current domain */
 
       for (state = wfa->basis_states; state < wfa->states; state++)
-	 for (label = 0; label < MAXLABELS; label++)
-	    if (isrange (wfa->tree[state][label]))
-	       for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
-		    edge++)
-	       {
-		  if (domain)		/* not DC component */
-		  {
-		     if (delta_approx && wfa->delta_state [state])
-			wfa->weight [state][label][edge]
-			   = btor (*wptr++, wfa->wfainfo->d_rpf);
-		     else
-			wfa->weight [state][label][edge]
-			   = btor (*wptr++, wfa->wfainfo->rpf);
-		  }
-		  else
-		  {
-		     if (delta_approx && wfa->delta_state [state])
-			wfa->weight [state][label][edge]
-			   = btor (*wptr++, wfa->wfainfo->d_dc_rpf);
-		     else
-			wfa->weight [state][label][edge]
-			   = btor (*wptr++, wfa->wfainfo->dc_rpf);
-		  }
-		  wfa->int_weight [state][label][edge]
-		     = wfa->weight [state][label][edge] * 512 + 0.5;
-	       }
+         for (label = 0; label < MAXLABELS; label++)
+            if (isrange (wfa->tree[state][label]))
+               for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
+                    edge++)
+               {
+                  if (domain)           /* not DC component */
+                  {
+                     if (delta_approx && wfa->delta_state [state])
+                        wfa->weight [state][label][edge]
+                           = btor (*wptr++, wfa->wfainfo->d_rpf);
+                     else
+                        wfa->weight [state][label][edge]
+                           = btor (*wptr++, wfa->wfainfo->rpf);
+                  }
+                  else
+                  {
+                     if (delta_approx && wfa->delta_state [state])
+                        wfa->weight [state][label][edge]
+                           = btor (*wptr++, wfa->wfainfo->d_dc_rpf);
+                     else
+                        wfa->weight [state][label][edge]
+                           = btor (*wptr++, wfa->wfainfo->dc_rpf);
+                  }
+                  wfa->int_weight [state][label][edge]
+                     = wfa->weight [state][label][edge] * 512 + 0.5;
+               }
    }
    
    Free (weights_array);
diff --git a/converter/other/fiasco/lib/arith.c b/converter/other/fiasco/lib/arith.c
index e3745bf7..dc35d1d1 100644
--- a/converter/other/fiasco/lib/arith.c
+++ b/converter/other/fiasco/lib/arith.c
@@ -90,7 +90,7 @@ encode_symbol (unsigned symbol, arith_t *arith, model_t *model)
  *  The current state of the arithmetic coder is given by 'arith'.
  *  Output bits are appended to the stream 'output'.
  *
- *  The model is updated after encoding the symbol (if neccessary the
+ *  The model is updated after encoding the symbol (if necessary the
  *  symbol counts are rescaled).
  *  
  *  Return value:
@@ -354,7 +354,7 @@ decode_symbol (arith_t *arith, model_t *model)
  *  Decode the next symbol - the state of the arithmetic decoder
  *  is given in 'arith'. Read refinement bits from the stream 'input'
  *  and use the given probability 'model'. Update the probability model after
- *  deconding the symbol (if neccessary also rescale the symbol counts).
+ *  deconding the symbol (if necessary also rescale the symbol counts).
  *  
  *  Return value:
  *	decoded symbol
diff --git a/converter/other/fiasco/lib/bit-io.c b/converter/other/fiasco/lib/bit-io.c
index 364a1c05..1bfef598 100644
--- a/converter/other/fiasco/lib/bit-io.c
+++ b/converter/other/fiasco/lib/bit-io.c
@@ -20,9 +20,9 @@
 #include "config.h"
 
 #include <string.h>
-#if STDC_HEADERS
-#   include <stdlib.h>
-#endif /* not STDC_HEADERS */
+#include <stdlib.h>
+
+#include "nstring.h"
 
 #include "macros.h"
 #include "types.h"
diff --git a/converter/other/fiasco/lib/dither.c b/converter/other/fiasco/lib/dither.c
index a39afa3c..accd9dd6 100644
--- a/converter/other/fiasco/lib/dither.c
+++ b/converter/other/fiasco/lib/dither.c
@@ -38,14 +38,8 @@
 #include "pm_config.h"
 #include "config.h"
 
-#if HAVE_STRING_H
-#	include <string.h>
-#else /* not HAVE_STRING_H */
-#	include <strings.h>
-#endif /* not HAVE_STRING_H */
-#if STDC_HEADERS
-#	include <stdlib.h>
-#endif /* not STDC_HEADERS */
+#include <string.h>
+#include <stdlib.h>
 
 #include "types.h"
 #include "macros.h"
@@ -65,19 +59,22 @@
 static int 
 display_16_bit (const struct fiasco_renderer *this, unsigned char *ximage,
 		const fiasco_image_t *fiasco_image);
+
 static int 
 display_24_bit_bgr (const struct fiasco_renderer *this, unsigned char *ximage,
 		    const fiasco_image_t *fiasco_image);
+
 static int 
 display_24_bit_rgb (const struct fiasco_renderer *this, unsigned char *ximage,
 		    const fiasco_image_t *fiasco_image);
+
 static int 
 display_32_bit (const struct fiasco_renderer *this, unsigned char *ximage,
 		const fiasco_image_t *fiasco_image);
+
 static int
 free_bits_at_bottom (unsigned long a);
-static int
-free_bits_at_top (unsigned long a);
+
 static int
 number_of_bits_set (unsigned long a);
 
@@ -345,21 +342,6 @@ number_of_bits_set (unsigned long a)
 }
 
 static int
-free_bits_at_top (unsigned long a)
-/*
- *  How many 0 bits are there at most significant end of longword.
- *  Low performance, do not call often.
- */
-{
-   if(!a)				/* assume char is 8 bits */
-      return sizeof (unsigned long) * 8;
-   else if (((long) a) < 0l)		/* assume twos complement */
-      return 0;
-   else
-      return 1 + free_bits_at_top ( a << 1);
-}
-
-static int
 free_bits_at_bottom (unsigned long a)
 /*
  *  How many 0 bits are there at least significant end of longword.
diff --git a/converter/other/fiasco/lib/error.c b/converter/other/fiasco/lib/error.c
index b858badf..ee3afe1f 100644
--- a/converter/other/fiasco/lib/error.c
+++ b/converter/other/fiasco/lib/error.c
@@ -29,18 +29,9 @@
 #include <stdio.h>
 #include <errno.h>
 
-#if STDC_HEADERS
-#	include <stdarg.h>
-#	define VA_START(args, lastarg) va_start(args, lastarg)
-#else  /* not STDC_HEADERS */
-#	include <varargs.h>
-#	define VA_START(args, lastarg) va_start(args)
-#endif /* not STDC_HEADERS */
-#if HAVE_STRING_H
-#	include <string.h>
-#else /* not HAVE_STRING_H */
-#	include <strings.h>
-#endif /* not HAVE_STRING_H */
+#include <stdarg.h>
+#define VA_START(args, lastarg) va_start(args, lastarg)
+#include <string.h>
 
 #if HAVE_SETJMP_H
 #	include <setjmp.h>
@@ -117,11 +108,7 @@ set_error (const char *format, ...)
       Free (error_message);
    error_message = Calloc (len, sizeof (char));
    
-#if HAVE_VPRINTF
    vsprintf (error_message, format, args);
-#elif HAVE_DOPRNT
-   _doprnt (format, args, stderr);
-#endif /* HAVE_DOPRNT */
 
    va_end (args);
 }
@@ -178,11 +165,7 @@ error (const char *format, ...)
       Free (error_message);
    error_message = Calloc (len, sizeof (char));
    
-#if HAVE_VPRINTF
    vsprintf (error_message, format, args);
-#elif HAVE_DOPRNT
-   _doprnt (format, args, stderr);
-#endif /* HAVE_DOPRNT */
 
    va_end (args);
    
@@ -236,11 +219,7 @@ warning (const char *format, ...)
       return;
 	
    fprintf (stderr, "Warning: ");
-#if HAVE_VPRINTF
    vfprintf (stderr, format, args);
-#elif HAVE_DOPRNT
-   _doprnt (format, args, stderr);
-#endif /* HAVE_DOPRNT */
    fputc ('\n', stderr);
 
    va_end (args);
@@ -259,11 +238,7 @@ message (const char *format, ...)
    if (verboselevel == FIASCO_NO_VERBOSITY)
       return;
 
-#if HAVE_VPRINTF
    vfprintf (stderr, format, args);
-#elif HAVE_DOPRNT
-   _doprnt (format, args, stderr);
-#endif /* HAVE_DOPRNT */
    fputc ('\n', stderr);
    va_end (args);
 }
@@ -282,11 +257,7 @@ debug_message (const char *format, ...)
       return;
 
    fprintf (stderr, "*** ");
-#if HAVE_VPRINTF
    vfprintf (stderr, format, args);
-#elif HAVE_DOPRNT
-   _doprnt (format, args, stderr);
-#endif /* HAVE_DOPRNT */
    fputc ('\n', stderr);
    va_end (args);
 }
@@ -304,11 +275,7 @@ info (const char *format, ...)
    if (verboselevel == FIASCO_NO_VERBOSITY)
       return;
 
-#if HAVE_VPRINTF
    vfprintf (stderr, format, args);
-#elif HAVE_DOPRNT
-   _doprnt (format, args, stderr);
-#endif /* HAVE_DOPRNT */
    fflush (stderr);
    va_end (args);
 }
diff --git a/converter/other/fiasco/lib/image.c b/converter/other/fiasco/lib/image.c
index 0168734c..fa3b2db5 100644
--- a/converter/other/fiasco/lib/image.c
+++ b/converter/other/fiasco/lib/image.c
@@ -18,6 +18,8 @@
 
 #include <string.h>
 
+#include "nstring.h"
+
 #include "types.h"
 #include "macros.h"
 #include "error.h"
@@ -239,7 +241,7 @@ alloc_image (unsigned width, unsigned height, bool_t color, format_e format)
    image->format      = format;
    image->reference_count = 1;
    
-   strcpy (image->id, "IFIASCO");
+   STRSCPY(image->id, "IFIASCO");
 
    for (band = first_band (color); band <= last_band (color); band++)
       if (format == FORMAT_4_2_0 && band != Y)
@@ -447,7 +449,7 @@ write_image (const char *image_name, const image_t *image)
    
    if (image->format == FORMAT_4_2_0)
    {
-      warning ("Writing of images in 4:2:0 format not supported.");
+      warning ("We cannot write images in 4:2:0 format.");
       return;
    }
    
diff --git a/converter/other/fiasco/lib/list.c b/converter/other/fiasco/lib/list.c
index 9f516c2e..bb4efae1 100644
--- a/converter/other/fiasco/lib/list.c
+++ b/converter/other/fiasco/lib/list.c
@@ -16,11 +16,7 @@
 
 #include "config.h"
 
-#if HAVE_STRING_H
-#	include <string.h>
-#else /* not HAVE_STRING_H */
-#	include <strings.h>
-#endif /* not HAVE_STRING_H */
+#include <string.h>
 
 #include "types.h"
 #include "macros.h"
diff --git a/converter/other/fiasco/lib/macros.h b/converter/other/fiasco/lib/macros.h
index 877abeea..9968110a 100644
--- a/converter/other/fiasco/lib/macros.h
+++ b/converter/other/fiasco/lib/macros.h
@@ -28,11 +28,6 @@
 #   define SEEK_CUR	1
 #endif /* not SEEK_CUR */
 
-#ifdef WIN32
-#undef max
-#undef min
-#endif /* not WIN32 */
-
 /*****************************************************************************
 
 				Various macros
@@ -50,12 +45,6 @@
 #define address_of_level(l)	((unsigned) (size_of_level (l) - 1))
 #define size_of_tree(l)		((unsigned) (address_of_level ((l) + 1)))
 #define is_odd(n)		(abs (n) % 2)
-#ifndef max
-#define max(a,b)		((a) < (b) ? (b) : (a))
-#endif
-#ifndef min
-#define min(a,b)	        ((a) > (b) ? (b) : (a))
-#endif
 #define _(x) (x) 
 
 
diff --git a/converter/other/fiasco/lib/misc.c b/converter/other/fiasco/lib/misc.c
index 12b94e7a..782ed1e9 100644
--- a/converter/other/fiasco/lib/misc.c
+++ b/converter/other/fiasco/lib/misc.c
@@ -1,5 +1,5 @@
 /*
- *  misc.c:		Some usefull functions, that don't fit in one of 
+ *  misc.c:		Some useful functions, that don't fit in one of 
  *			the other files and that are needed by at least
  *			two modules. 
  *
@@ -33,15 +33,10 @@
 #	endif /* not HAVE_SYS_TIME_H */
 #endif /* not TIME_WITH_SYS_TIME */
 
-#if STDC_HEADERS
-#	include <stdlib.h>
-#endif /* not STDC_HEADERS */
+#include <stdlib.h>
+#include <string.h>
 
-#if HAVE_STRING_H
-#	include <string.h>
-#else /* not HAVE_STRING_H */
-#	include <strings.h>
-#endif /* not HAVE_STRING_H */
+#include "pm_c_util.h"
 
 #include "types.h"
 #include "macros.h"
@@ -401,22 +396,6 @@ memmove (void *v_dst, const void *v_src, size_t n)
 }
 #endif /* not HAVE_MEMMOVE */
 
-#ifndef HAVE_STRDUP
-char *
-strdup (const char *s)
-/*
- *  Duplicate given string 's'.
- *
- *  Return value:
- *	pointer to new string value
- */
-{
-   assert (s);
-   
-   return strcpy (Calloc (strlen (s) + 1, sizeof (char)), s);
-}
-#endif /* not HAVE_STRDUP */
-
 /* Note that some systems have a "log2()" in the math library and some
    have a "log2" macro.  So we name ours Log2.  But to avoid lots of
    differences from the original fiasco source code, we define a
@@ -452,13 +431,13 @@ variance (const word_t *pixels, unsigned x0, unsigned y0,
    assert (pixels);
    
    for (average = 0, n = 0, y = y0; y < y0 + height; y++)
-      for (x = x0; x < min (x0 + width, cols); x++, n++)
+      for (x = x0; x < MIN(x0 + width, cols); x++, n++)
 	 average += pixels [y * cols + x] / 16;
 
    average /= n;
 
    for (variance = 0, y = y0; y < y0 + height; y++)
-      for (x = x0; x < min (x0 + width, cols); x++)
+      for (x = x0; x < MIN(x0 + width, cols); x++)
 	 variance += square ((pixels [y * cols + x] / 16) - average);
 
    return variance;
diff --git a/converter/other/fiasco/lib/misc.h b/converter/other/fiasco/lib/misc.h
index 29456590..28fd8b5a 100644
--- a/converter/other/fiasco/lib/misc.h
+++ b/converter/other/fiasco/lib/misc.h
@@ -72,10 +72,6 @@ memmove(void *dest, const void *src, size_t n);
 
 double
 Log2 (double x);
-#ifndef HAVE_STRDUP
-char *
-strdup (const char *s);
-#endif
 #ifndef HAVE_STRCASECMP
 bool_t
 strcaseeq (const char *s1, const char *s2);
diff --git a/converter/other/fiasco/output/matrices.c b/converter/other/fiasco/output/matrices.c
index fd8d31e2..01189669 100644
--- a/converter/other/fiasco/output/matrices.c
+++ b/converter/other/fiasco/output/matrices.c
@@ -20,9 +20,9 @@
 
 #include "config.h"
 
-#if STDC_HEADERS
-#	include <stdlib.h>
-#endif /* not STDC_HEADERS */
+#include <stdlib.h>
+
+#include "pm_c_util.h"
 
 #include "types.h"
 #include "macros.h"
@@ -144,7 +144,7 @@ delta_encoding (bool_t use_normal_domains, bool_t use_delta_domains,
 		  ;
 	       count [edge]++;
 	       edges++;
-	       M = max (edge, M);
+	       M = MAX(edge, M);
 	    }
       write_rice_code (M, 3, output);
       for (n = 0; n <= M; n++)
diff --git a/converter/other/fiasco/output/weights.c b/converter/other/fiasco/output/weights.c
index 085a1f00..5aa17674 100644
--- a/converter/other/fiasco/output/weights.c
+++ b/converter/other/fiasco/output/weights.c
@@ -16,6 +16,8 @@
 
 #include "config.h"
 
+#include "pm_c_util.h"
+
 #include "types.h"
 #include "macros.h"
 #include "error.h"
@@ -43,158 +45,159 @@ write_weights (unsigned total, const wfa_t *wfa, bitfile_t *output)
  *  No return value.
  */
 {
-   unsigned  state, label;		/* current label */
-   unsigned  offset1, offset2;		/* model offsets. */
-   unsigned  offset3, offset4;		/* model offsets. */
-   unsigned *weights_array;		/* array of weights to encode */
-   unsigned *wptr;			/* pointer to current weight */
-   unsigned *level_array;		/* array of corresponding levels */
-   unsigned *lptr;			/* pointer to current corr. level */
-   int	     min_level, max_level;	/* min and max range level */
-   int	     d_min_level, d_max_level; 	/* min and max delta range level */
-   bool_t    dc, d_dc;			/* true if dc or delta dc are used */
-   bool_t    delta_approx = NO;		/* true if delta has been used */
-   unsigned  delta_count  = 0;		/* number of delta ranges */
-   unsigned  bits 	  = bits_processed (output);
+    unsigned  state, label;		/* current label */
+    unsigned  offset1, offset2;		/* model offsets. */
+    unsigned  offset3, offset4;		/* model offsets. */
+    unsigned *weights_array;		/* array of weights to encode */
+    unsigned *wptr;			/* pointer to current weight */
+    unsigned *level_array;		/* array of corresponding levels */
+    unsigned *lptr;			/* pointer to current corr. level */
+    int	     min_level, max_level;	/* min and max range level */
+    int	     d_min_level, d_max_level; 	/* min and max delta range level */
+    bool_t    dc, d_dc;			/* true if dc or delta dc are used */
+    bool_t    delta_approx = NO;		/* true if delta has been used */
+    unsigned  delta_count  = 0;		/* number of delta ranges */
+    unsigned  bits 	  = bits_processed (output);
    
-   /*
-    *  Check whether delta approximation has been used
-    */
-   for (state = wfa->basis_states; state < wfa->states; state++)
-      if (wfa->delta_state [state])
-      {
-	 delta_approx = YES;
-	 break;
-      }
+    /*
+     *  Check whether delta approximation has been used
+     */
+    for (state = wfa->basis_states; state < wfa->states; state++)
+        if (wfa->delta_state [state])
+        {
+            delta_approx = YES;
+            break;
+        }
    
-   /*
-    *  Generate array of corresponding levels (context of probability model)
-    */
-   min_level = d_min_level = MAXLEVEL;
-   max_level = d_max_level = 0;
-   dc 	     = d_dc	   = NO;
+    /*
+     *  Generate array of corresponding levels (context of probability model)
+     */
+    min_level = d_min_level = MAXLEVEL;
+    max_level = d_max_level = 0;
+    dc 	     = d_dc	   = NO;
    
-   for (state = wfa->basis_states; state < wfa->states; state++)
-      for (label = 0; label < MAXLABELS; label++)
-         if (isrange (wfa->tree [state][label]))
-	 {
-	    if (delta_approx && wfa->delta_state [state]) /* delta approx. */
-	    {
-	       d_min_level = min (d_min_level,
-				  wfa->level_of_state [state] - 1);
-	       d_max_level = max (d_max_level,
-				  wfa->level_of_state [state] - 1);
-	       if (wfa->into [state][label][0] == 0)
-		  d_dc = YES;
-	    }
-	    else
-	    {
-	       min_level = min (min_level, wfa->level_of_state [state] - 1);
-	       max_level = max (max_level, wfa->level_of_state [state] - 1);
-	       if (wfa->into [state][label][0] == 0)
-		  dc = YES;
-	    }
-	 }
-   if (min_level > max_level)		/* no lc found */
-      max_level = min_level - 1;
-   if (d_min_level > d_max_level)
-      d_max_level = d_min_level - 1;
-
-   /*
-    *  Context model:
-    *		0		DC weight
-    *		1		Delta DC weight
-    *		2-k		normal weights per level
-    *		k+1 - m		Delta weights per level
-    */
-
-   offset1 = dc ? 1 : 0;
-   offset2 = offset1 + (d_dc ? 1 : 0);
-   offset3 = offset2 + (max_level - min_level + 1);
-   offset4 = offset3 + (d_max_level - d_min_level + 1);
+    for (state = wfa->basis_states; state < wfa->states; state++)
+        for (label = 0; label < MAXLABELS; label++)
+            if (isrange (wfa->tree [state][label]))
+            {
+                if (delta_approx && wfa->delta_state [state]) /* delta approx. */
+                {
+                    d_min_level = MIN(d_min_level, wfa->level_of_state [state] - 1);
+                    d_max_level = MAX(d_max_level, wfa->level_of_state [state] - 1);
+                    if (wfa->into [state][label][0] == 0)
+                        d_dc = YES;
+                }
+                else
+                {
+                    min_level = MIN(min_level, wfa->level_of_state [state] - 1);
+                    max_level = MAX(max_level, wfa->level_of_state [state] - 1);
+                    if (wfa->into [state][label][0] == 0)
+                        dc = YES;
+                }
+            }
+    if (min_level > max_level)		/* no lc found */
+        max_level = min_level - 1;
+    if (d_min_level > d_max_level)
+        d_max_level = d_min_level - 1;
+
+    /*
+     *  Context model:
+     *		0		DC weight
+     *		1		Delta DC weight
+     *		2-k		normal weights per level
+     *		k+1 - m		Delta weights per level
+     */
+
+    offset1 = dc ? 1 : 0;
+    offset2 = offset1 + (d_dc ? 1 : 0);
+    offset3 = offset2 + (max_level - min_level + 1);
+    offset4 = offset3 + (d_max_level - d_min_level + 1);
    
-   /*
-    *  Weights are encoded as follows:
-    *  all weights of state n
-    *     sorted by label
-    *        sorted by domain number
-    */
-
-   wptr = weights_array = Calloc (total, sizeof (unsigned));
-   lptr = level_array   = Calloc (total, sizeof (unsigned));
-
-   for (state = wfa->basis_states; state < wfa->states; state++)
-      for (label = 0; label < MAXLABELS; label++)
-         if (isrange (wfa->tree [state][label]))
-	 {
-	    int	edge;			/* current edge */
-	    int	domain;			/* current domain (context of model) */
-	    
-            for (edge = 0; isedge (domain = wfa->into [state][label][edge]);
-		 edge++)
+    /*
+     *  Weights are encoded as follows:
+     *  all weights of state n
+     *     sorted by label
+     *        sorted by domain number
+     */
+
+    wptr = weights_array = Calloc (total, sizeof (unsigned));
+    lptr = level_array   = Calloc (total, sizeof (unsigned));
+
+    for (state = wfa->basis_states; state < wfa->states; state++)
+        for (label = 0; label < MAXLABELS; label++)
+            if (isrange (wfa->tree [state][label]))
             {
-	       if (wptr - weights_array >= (int) total)
-		  error ("Can't write more than %d weights.", total);
-	       if (domain)		/* not DC component */
-	       {
-		  if (delta_approx && wfa->delta_state [state]) /* delta */
-		  {
-		     *wptr++ = rtob (wfa->weight [state][label][edge],
-				     wfa->wfainfo->d_rpf);
-		     *lptr++ = offset3
-			       + wfa->level_of_state [state] - 1 - d_min_level;
-		     delta_count++;
-		  }
-		  else
-		  {
-		     *wptr++ = rtob (wfa->weight [state][label][edge],
-				     wfa->wfainfo->rpf);
-		     *lptr++ = offset2
-			       + wfa->level_of_state [state] - 1 - min_level;
-		  }
-	       }
-	       else			/* DC component */
-	       {
-		  if (delta_approx && wfa->delta_state [state]) /* delta */
-		  {
-		     *wptr++ = rtob (wfa->weight [state][label][edge],
-				     wfa->wfainfo->d_dc_rpf);
-		     *lptr++ = offset1;
-		  }
-		  else
-		  {
-		     *wptr++ = rtob (wfa->weight [state][label][edge],
-				     wfa->wfainfo->dc_rpf);
-		     *lptr++ = 0;
-		  }
-	       }
+                int	edge;			/* current edge */
+                int	domain;			/* current domain (context of model) */
+	    
+                for (edge = 0; isedge (domain = wfa->into [state][label][edge]);
+                     edge++)
+                {
+                    if (wptr - weights_array >= (int) total)
+                        error ("Can't write more than %d weights.", total);
+                    if (domain)		/* not DC component */
+                    {
+                        if (delta_approx && wfa->delta_state [state]) /* delta */
+                        {
+                            *wptr++ = rtob (wfa->weight [state][label][edge],
+                                            wfa->wfainfo->d_rpf);
+                            *lptr++ = offset3
+                                + wfa->level_of_state [state] - 1 - d_min_level;
+                            delta_count++;
+                        }
+                        else
+                        {
+                            *wptr++ = rtob (wfa->weight [state][label][edge],
+                                            wfa->wfainfo->rpf);
+                            *lptr++ = offset2
+                                + wfa->level_of_state [state] - 1 - min_level;
+                        }
+                    }
+                    else			/* DC component */
+                    {
+                        if (delta_approx && wfa->delta_state [state]) /* delta */
+                        {
+                            *wptr++ = rtob (wfa->weight [state][label][edge],
+                                            wfa->wfainfo->d_dc_rpf);
+                            *lptr++ = offset1;
+                        }
+                        else
+                        {
+                            *wptr++ = rtob (wfa->weight [state][label][edge],
+                                            wfa->wfainfo->dc_rpf);
+                            *lptr++ = 0;
+                        }
+                    }
+                }
             }
-	 }
-
-   {
-      unsigned	 i;
-      unsigned	*c_symbols = Calloc (offset4, sizeof (int));
-      const int	 scale 	   = 500;	/* scaling of probability model */
-
-      c_symbols [0] = 1 << (wfa->wfainfo->dc_rpf->mantissa_bits + 1);
-      if (offset1 != offset2)
-	 c_symbols [offset1] = 1 << (wfa->wfainfo->d_dc_rpf->mantissa_bits
-				     + 1);
-      for (i = offset2; i < offset3; i++)
-	 c_symbols [i] = 1 << (wfa->wfainfo->rpf->mantissa_bits + 1);
-      for (; i < offset4; i++)
-	 c_symbols [i] = 1 << (wfa->wfainfo->d_rpf->mantissa_bits + 1);
+
+    {
+        unsigned	 i;
+        unsigned	*c_symbols = Calloc (offset4, sizeof (int));
+        const int	 scale 	   = 500;	/* scaling of probability model */
+
+        c_symbols [0] = 1 << (wfa->wfainfo->dc_rpf->mantissa_bits + 1);
+        if (offset1 != offset2)
+            c_symbols [offset1] = 1 << (wfa->wfainfo->d_dc_rpf->mantissa_bits
+                                        + 1);
+        for (i = offset2; i < offset3; i++)
+            c_symbols [i] = 1 << (wfa->wfainfo->rpf->mantissa_bits + 1);
+        for (; i < offset4; i++)
+            c_symbols [i] = 1 << (wfa->wfainfo->d_rpf->mantissa_bits + 1);
       
-      encode_array (output, weights_array, level_array, c_symbols, offset4,
-		    total, scale);
-      Free (c_symbols);
-   }
+        encode_array (output, weights_array, level_array, c_symbols, offset4,
+                      total, scale);
+        Free (c_symbols);
+    }
    
-   debug_message ("%d delta weights out of %d.", delta_count, total);
-   debug_message ("weights:      %5d bits. (%5d symbols => %5.2f bps)",
-		  bits_processed (output) - bits, total,
-		  (bits_processed (output) - bits) / (double) total);
+    debug_message ("%d delta weights out of %d.", delta_count, total);
+    debug_message ("weights:      %5d bits. (%5d symbols => %5.2f bps)",
+                   bits_processed (output) - bits, total,
+                   (bits_processed (output) - bits) / (double) total);
 
-   Free (weights_array);
-   Free (level_array);
+    Free (weights_array);
+    Free (level_array);
 }
+
+
+
diff --git a/converter/other/fiasco/params.c b/converter/other/fiasco/params.c
index 7a302b82..afacbada 100644
--- a/converter/other/fiasco/params.c
+++ b/converter/other/fiasco/params.c
@@ -30,6 +30,7 @@
  
 #include <getopt.h>			/* system or ../lib */
 
+#include "pm_c_util.h"
 #include "nstring.h"
 
 #include "types.h"
@@ -631,6 +632,8 @@ read_parameter_file (param_t *params, FILE *file)
    }
 }   
 
+
+
 static void 
 usage (const param_t *params, const char *progname, const char *synopsis,
        const char *comment, const char *non_opt_string,
@@ -647,82 +650,84 @@ usage (const param_t *params, const char *progname, const char *synopsis,
  *  No return value.
  */
 {
-   int	  i;
-   size_t width = 0;
+    int	  i;
+    size_t width = 0;
    
-   fprintf (stderr, "Usage: %s [OPTION]...%s\n", progname,
-	    non_opt_string ? non_opt_string : " ");
-   if (synopsis != NULL)
-      fprintf (stderr, synopsis);
-   fprintf (stderr, "\n\n");
-   fprintf (stderr, "Mandatory or optional arguments to long options "
-	    "are mandatory or optional\nfor short options too. "
-	    "Default values are surrounded by {}.\n");
-   for (i = 0; params [i].name != NULL; i++)
-      if (params [i].optchar != '\0' || show_all_options)
-      {
-	 if (params [i].type == POSTR)
-	    width = max (width, (strlen (params [i].name)
-				 + strlen (params [i].argument_name) + 2));
-	 else if (params [i].type != PFLAG)
-	    width = max (width, (strlen (params [i].name)
-				 + strlen (params [i].argument_name)));
-	 else
-	    width = max (width, (strlen (params [i].name)) - 1);
-      }
+    fprintf (stderr, "Usage: %s [OPTION]...%s\n", progname,
+             non_opt_string ? non_opt_string : " ");
+    if (synopsis != NULL)
+        fprintf (stderr, "%s", synopsis);
+    fprintf (stderr, "\n\n");
+    fprintf (stderr, "Mandatory or optional arguments to long options "
+             "are mandatory or optional\nfor short options too. "
+             "Default values are surrounded by {}.\n");
+    for (i = 0; params [i].name != NULL; i++)
+        if (params [i].optchar != '\0' || show_all_options)
+        {
+            if (params [i].type == POSTR)
+                width = MAX(width, (strlen (params [i].name)
+                                     + strlen (params [i].argument_name) + 2));
+            else if (params [i].type != PFLAG)
+                width = MAX(width, (strlen (params [i].name)
+                                     + strlen (params [i].argument_name)));
+            else
+                width = MAX(width, (strlen (params [i].name)) - 1);
+        }
    
-   for (i = 0; params [i].name != NULL; i++)
-      if (params [i].optchar != '\0' || show_all_options)
-      {
-	 if (params [i].optchar != '\0')
-	    fprintf (stderr, "  -%c, --", params [i].optchar);
-	 else
-	    fprintf (stderr, "      --");
+    for (i = 0; params [i].name != NULL; i++)
+        if (params [i].optchar != '\0' || show_all_options)
+        {
+            if (params [i].optchar != '\0')
+                fprintf (stderr, "  -%c, --", params [i].optchar);
+            else
+                fprintf (stderr, "      --");
 	 
-	 if (params [i].type == POSTR)
-	    fprintf (stderr, "%s=[%s]%-*s  ", params [i].name,
-		     params [i].argument_name,
-		     max (0, (width - 2 - strlen (params [i].name)
-			   - strlen (params [i].argument_name))), "");
-	 else if (params [i].type != PFLAG)
-	    fprintf (stderr, "%s=%-*s  ", params [i].name,
-		  width - strlen (params [i].name),
-		  params [i].argument_name);
-	 else
-	    fprintf (stderr, "%-*s  ", width + 1, params [i].name);
-
-	 fprintf (stderr, params [i].use, params [i].argument_name);
+            if (params [i].type == POSTR)
+                fprintf (stderr, "%s=[%s]%-*s  ", params [i].name,
+                         params [i].argument_name,
+                         (unsigned)
+                         MAX(0, (width - 2 - strlen (params [i].name)
+                                 - strlen (params [i].argument_name))), "");
+            else if (params [i].type != PFLAG)
+                fprintf (stderr, "%s=%-*s  ", params [i].name,
+                         (unsigned)(width - strlen (params [i].name)),
+                         params [i].argument_name);
+            else
+                fprintf (stderr, "%-*s  ",
+                         (unsigned)(width + 1), params [i].name);
+
+            fprintf (stderr, params [i].use, params [i].argument_name);
 	 
-	 switch (params [i].type)
-	 {
-	    case PFLAG:
-	       break;
-	    case PINT:
-	       fprintf (stderr, "{%d}", params [i].value.i);
-	       break;
-	    case PFLOAT:
-	       fprintf (stderr, "{%.2f}", (double) params [i].value.f);
-	       break;
-	    case PSTR:
-	    case POSTR:
-	       if (params [i].value.s)
-		  fprintf (stderr, "{%s}", params [i].value.s);
-	       break;
-	    default:
-	       error ("type %d for %s invalid",
-		      params [i].type, params [i].name);
-	 }
-	 fprintf (stderr, "\n");
-      }
-   fprintf (stderr, "\n");
-   fprintf (stderr, "Parameter initialization order:\n");
-   fprintf (stderr,
-	    "1.) %s\n2.) $HOME/%s\t 3.) command line\t 4.) --config=file",
-	    sys_file_name, usr_file_name);
-   fprintf (stderr, "\n\n");
-   if (comment != NULL)
-      fprintf (stderr, "%s\n", comment);
-
-   exit (1);
+            switch (params [i].type)
+            {
+            case PFLAG:
+                break;
+            case PINT:
+                fprintf (stderr, "{%d}", params [i].value.i);
+                break;
+            case PFLOAT:
+                fprintf (stderr, "{%.2f}", (double) params [i].value.f);
+                break;
+            case PSTR:
+            case POSTR:
+                if (params [i].value.s)
+                    fprintf (stderr, "{%s}", params [i].value.s);
+                break;
+            default:
+                error ("type %d for %s invalid",
+                       params [i].type, params [i].name);
+            }
+            fprintf (stderr, "\n");
+        }
+    fprintf (stderr, "\n");
+    fprintf (stderr, "Parameter initialization order:\n");
+    fprintf (stderr,
+             "1.) %s\n2.) $HOME/%s\t 3.) command line\t 4.) --config=file",
+             sys_file_name, usr_file_name);
+    fprintf (stderr, "\n\n");
+    if (comment != NULL)
+        fprintf (stderr, "%s\n", comment);
+
+    exit (1);
 }
 
diff --git a/converter/other/fiasco/pnmtofiasco.c b/converter/other/fiasco/pnmtofiasco.c
index 2218256d..eebd09a9 100644
--- a/converter/other/fiasco/pnmtofiasco.c
+++ b/converter/other/fiasco/pnmtofiasco.c
@@ -1,8 +1,8 @@
 /*
- *  cwfa.c:		FIASCO coder
+ *  cwfa.c:     FIASCO coder
  *
- *  Written by:		Ullrich Hafner
- *		
+ *  Written by:     Ullrich Hafner
+ *      
  *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
  *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
  */
@@ -15,18 +15,12 @@
  */
 
 #include "config.h"
+#include "pm_c_util.h"
 #include "pnm.h"
 
-#if STDC_HEADERS
-#	include <stdlib.h>
-#	include <string.h>
-#else /* not STDC_HEADERS */
-#	if HAVE_STRING_H
-#		include <string.h>
-#	else /* not HAVE_STRING_H */
-#		include <strings.h>
-#	endif /* not HAVE_STRING_H */
-#endif /* not STDC_HEADERS */
+#include <stdlib.h>
+#include <string.h>
+#include <string.h>
 
 #include "types.h"
 #include "macros.h"
@@ -38,7 +32,7 @@
 
 /*****************************************************************************
 
-			     local variables
+                 local variables
   
 *****************************************************************************/
 
@@ -144,27 +138,27 @@ static param_t params [] =
 
 /*****************************************************************************
 
-				prototypes
+                prototypes
   
 *****************************************************************************/
 
 static void 
 checkargs (int argc, char **argv, char const ***image_template,
-	   char **wfa_name, float *quality, fiasco_c_options_t **options);
+       char **wfa_name, float *quality, fiasco_c_options_t **options);
 
 /*****************************************************************************
 
-				public code
+                public code
   
 *****************************************************************************/
  
 int 
 main (int argc, char **argv)
 {
-   char const 	      **image_template;	/* template for input image files */
-   char	       	       *wfa_name;	/* filename of output WFA */
-   float	      	quality;	/* approximation quality */
-   fiasco_c_options_t  *options;	/* additional coder options */
+   char const         **image_template; /* template for input image files */
+   char                *wfa_name;   /* filename of output WFA */
+   float            quality;    /* approximation quality */
+   fiasco_c_options_t  *options;    /* additional coder options */
    
    pnm_init(&argc, argv);
    
@@ -176,7 +170,7 @@ main (int argc, char **argv)
       return 0;
    else
    {
-      fprintf (stderr, fiasco_get_error_message ());
+       fprintf (stderr, "%s", fiasco_get_error_message ());
       fprintf (stderr, "\n");
       return 1;
    }
@@ -184,228 +178,228 @@ main (int argc, char **argv)
 
 /*****************************************************************************
 
-				private code
+                private code
   
 *****************************************************************************/
 
 static void 
 checkargs (int argc, char **argv, char const ***image_template,
-	   char **wfa_name, float *quality, fiasco_c_options_t **options)
+           char **wfa_name, float *quality, fiasco_c_options_t **options)
 /*
  *  Check validness of command line parameters and of the parameter files.
  *
  *  Return value:
- *	1 on success
- *	0 otherwise
+ *  1 on success
+ *  0 otherwise
  *  
  *
  *  Side effects:
- *	'image_template', 'wfa_name', 'quality' and 'options' are set.
+ *  'image_template', 'wfa_name', 'quality' and 'options' are set.
  */
 {
-   int	 optind;			/* last processed commandline param */
-   char	*image_name;			/* filename given by option '-i' */
-   int	 i;				/* counter */
+    int   optind;            /* last processed commandline param */
+    char *image_name;            /* filename given by option '--input_name' */
+    int   i;             /* counter */
    
-   optind = parseargs (params, argc, argv,
-		       "Compress raw PPM/PGM image FILEs to a FIASCO file.",
-		       "With no image FILE, or if FILE is -, "
-		       "read standard input.\n"
-		       "FILE must be either a filename"
-		       " or an image template of the form:\n"
-		       "`prefix[start-end{+,-}step]suffix'\n"
-		       "e.g., img0[12-01-1].pgm is substituted by"
-		       " img012.pgm ... img001.pgm\n\n"
-		       "Environment:\n"
-		       "FIASCO_DATA   Search and save path for FIASCO files. "
-		       "Default: ./\n"
-		       "FIASCO_IMAGES Search path for image files. "
-		       "Default: ./", " [FILE]...",
-		       FIASCO_SHARE, "system.fiascorc", ".fiascorc");
+    optind = parseargs (params, argc, argv,
+                        "Compress raw PPM/PGM image FILEs to a FIASCO file.",
+                        "With no image FILE, or if FILE is -, "
+                        "read standard input.\n"
+                        "FILE must be either a filename"
+                        " or an image template of the form:\n"
+                        "`prefix[start-end{+,-}step]suffix'\n"
+                        "e.g., img0[12-01-1].pgm is substituted by"
+                        " img012.pgm ... img001.pgm\n\n"
+                        "Environment:\n"
+                        "FIASCO_DATA   Search and save path for FIASCO files. "
+                        "Default: ./\n"
+                        "FIASCO_IMAGES Search path for image files. "
+                        "Default: ./", " [FILE]...",
+                        FIASCO_SHARE, "system.fiascorc", ".fiascorc");
 
-   /*
-    *  Default options ...
-    */
-   image_name = (char *) parameter_value (params, "image-name"); 
-   *wfa_name  = (char *) parameter_value (params, "output-name");
-   for (;;)
-   {
-      *quality = * (float *) parameter_value (params, "quality");
-      if (*quality > 100)
-	 fprintf (stderr, "Typical range of quality: (0,100].\n"
-		  "Expect some trouble on slow machines.\n");
-      if (*quality > 0)
-	 break;
-      ask_and_set (params, "quality",
-		   "Please enter coding quality 'q' ('q' > 0): ");
-   }
+    /*
+     *  Default options ...
+     */
+    image_name = (char *) parameter_value (params, "image-name"); 
+    *wfa_name  = (char *) parameter_value (params, "output-name");
+    for (;;)
+    {
+        *quality = * (float *) parameter_value (params, "quality");
+        if (*quality > 100)
+            fprintf (stderr, "Typical range of quality: (0,100].\n"
+                     "Expect some trouble on slow machines.\n");
+        if (*quality > 0)
+            break;
+        ask_and_set (params, "quality",
+                     "Please enter coding quality 'q' ('q' > 0): ");
+    }
    
-   if (optind < argc)			/* Additional command line param */
-   {
-      if (image_name)
-	 error ("Multiple image_template arguments."
-		"\nOption -i %s already specified!", image_name);
+    if (optind < argc)           /* Additional command line param */
+    {
+        if (image_name)
+            error ("Multiple image_template arguments."
+                   "\nOption --input-name %s already specified!", image_name);
 
-      *image_template = calloc (argc - optind + 1, sizeof (char *));
-      if (!*image_template)
-	 error ("Out of memory.");
-      for (i = 0; optind < argc; i++, optind++)
-	 (*image_template) [i] = argv [optind];
-      (*image_template) [i] = NULL;
-   }
-   else					/* option -i image_name */
-   {
-      *image_template = calloc (2, sizeof (char *));
-      if (!*image_template)
-	 error ("Out of memory.");
-      (*image_template) [0] = image_name;
-      (*image_template) [1] = NULL;
-   }
-   /*
-    *  Additional options ... (have to be set with the fiasco_set_... methods)
-    */
-   {
-      *options = fiasco_c_options_new ();
+        *image_template = calloc (argc - optind + 1, sizeof (char *));
+        if (!*image_template)
+            error ("Out of memory.");
+        for (i = 0; optind < argc; i++, optind++)
+            (*image_template) [i] = argv [optind];
+        (*image_template) [i] = NULL;
+    }
+    else                 /* option -i image_name */
+    {
+        *image_template = calloc (2, sizeof (char *));
+        if (!*image_template)
+            error ("Out of memory.");
+        (*image_template) [0] = image_name;
+        (*image_template) [1] = NULL;
+    }
+    /*
+     *  Additional options ... (have to be set with the fiasco_set_... methods)
+     */
+    {
+        *options = fiasco_c_options_new ();
       
-      {
-	 char *pattern = (char *) parameter_value (params, "pattern");
+        {
+            char *pattern = (char *) parameter_value (params, "pattern");
 
-	 if (!fiasco_c_options_set_frame_pattern (*options, pattern))
-	    error (fiasco_get_error_message ());
-      }
+            if (!fiasco_c_options_set_frame_pattern (*options, pattern))
+                error (fiasco_get_error_message ());
+        }
 
-      {
-	 char *basis = (char *) parameter_value (params, "basis-name");
-	 
-	 if (!fiasco_c_options_set_basisfile (*options, basis))
-	    error (fiasco_get_error_message ());
-      }
+        {
+            char *basis = (char *) parameter_value (params, "basis-name");
+     
+            if (!fiasco_c_options_set_basisfile (*options, basis))
+                error (fiasco_get_error_message ());
+        }
 
-      {
-	 int   n = * (int *) parameter_value (params, "chroma-dictionary");
-	 float q = * (float *) parameter_value (params, "chroma-qfactor");
+        {
+            int   n = * (int *) parameter_value (params, "chroma-dictionary");
+            float q = * (float *) parameter_value (params, "chroma-qfactor");
       
-	 if (!fiasco_c_options_set_chroma_quality (*options, q, max (0, n)))
-	    error (fiasco_get_error_message ());
-      }
+            if (!fiasco_c_options_set_chroma_quality (*options, q, MAX(0, n)))
+                error (fiasco_get_error_message ());
+        }
       
-      {
-	 int n = *((int *) parameter_value (params, "smooth"));
-	 
-	 if (!fiasco_c_options_set_smoothing (*options, max (0, n)))
-	    error (fiasco_get_error_message ());
-      }
+        {
+            int n = *((int *) parameter_value (params, "smooth"));
+     
+            if (!fiasco_c_options_set_smoothing (*options, MAX(0, n)))
+                error (fiasco_get_error_message ());
+        }
       
-      {
-          int n = * (int *) parameter_value (params, "progress-meter");
-          fiasco_progress_e type = (n < 0) ? 
-              FIASCO_PROGRESS_NONE : (fiasco_progress_e) n;
+        {
+            int n = * (int *) parameter_value (params, "progress-meter");
+            fiasco_progress_e type = (n < 0) ? 
+                FIASCO_PROGRESS_NONE : (fiasco_progress_e) n;
       
-          if (!fiasco_c_options_set_progress_meter (*options, type))
-              error (fiasco_get_error_message ());
-      }
+            if (!fiasco_c_options_set_progress_meter (*options, type))
+                error (fiasco_get_error_message ());
+        }
       
-      {
-	 char *t = (char *) parameter_value (params, "title");
-	 
-	 if (strlen (t) > 0 && !fiasco_c_options_set_title (*options, t))
-	    error (fiasco_get_error_message ());
-      }
+        {
+            char *t = (char *) parameter_value (params, "title");
+     
+            if (strlen (t) > 0 && !fiasco_c_options_set_title (*options, t))
+                error (fiasco_get_error_message ());
+        }
       
-      {
-	 char *c = (char *) parameter_value (params, "comment");
+        {
+            char *c = (char *) parameter_value (params, "comment");
 
-	 if (strlen (c) > 0 && !fiasco_c_options_set_comment (*options, c))
-	    error (fiasco_get_error_message ());
-      }
+            if (strlen (c) > 0 && !fiasco_c_options_set_comment (*options, c))
+                error (fiasco_get_error_message ());
+        }
       
-      {
-	 fiasco_tiling_e method = FIASCO_TILING_VARIANCE_DSC;
-	 int   e  = * (int *) parameter_value (params, "tiling-exponent");
-	 char *m  = (char *) parameter_value (params, "tiling-method");
+        {
+            fiasco_tiling_e method = FIASCO_TILING_VARIANCE_DSC;
+            int   e  = * (int *) parameter_value (params, "tiling-exponent");
+            char *m  = (char *) parameter_value (params, "tiling-method");
 
-	 if (strcaseeq (m, "desc-variance"))
-	    method = FIASCO_TILING_VARIANCE_DSC;
-	 else if (strcaseeq (m, "asc-variance"))
-	    method = FIASCO_TILING_VARIANCE_ASC;
-	 else if (strcaseeq (m, "asc-spiral"))
-	    method = FIASCO_TILING_SPIRAL_ASC;
-	 else if (strcaseeq (m, "dsc-spiral"))
-	    method = FIASCO_TILING_SPIRAL_DSC;
-	 else
-	    error (_("Invalid tiling method `%s' specified."), m);
+            if (strcaseeq (m, "desc-variance"))
+                method = FIASCO_TILING_VARIANCE_DSC;
+            else if (strcaseeq (m, "asc-variance"))
+                method = FIASCO_TILING_VARIANCE_ASC;
+            else if (strcaseeq (m, "asc-spiral"))
+                method = FIASCO_TILING_SPIRAL_ASC;
+            else if (strcaseeq (m, "dsc-spiral"))
+                method = FIASCO_TILING_SPIRAL_DSC;
+            else
+                error (_("Invalid tiling method `%s' specified."), m);
 
-	 if (!fiasco_c_options_set_tiling (*options, method, max (0, e)))
-	    error (fiasco_get_error_message ());
-      }
+            if (!fiasco_c_options_set_tiling (*options, method, MAX(0, e)))
+                error (fiasco_get_error_message ());
+        }
       
-      {
-	 int M/*  = * (int *) parameter_value (params, "max-level") */;
-	 int m/*  = * (int *) parameter_value (params, "min-level") */;
-	 int N/*  = * (int *) parameter_value (params, "max-elements") */;
-	 int D = * (int *) parameter_value (params, "dictionary-size");
-	 int o = * (int *) parameter_value (params, "optimize");
+        {
+            int M/*  = * (int *) parameter_value (params, "max-level") */;
+            int m/*  = * (int *) parameter_value (params, "min-level") */;
+            int N/*  = * (int *) parameter_value (params, "max-elements") */;
+            int D = * (int *) parameter_value (params, "dictionary-size");
+            int o = * (int *) parameter_value (params, "optimize");
 
-	 if (o <= 0)
-	 {
-	    o = 0;
-	    M = 10;
-	    m = 6;
-	    N = 3;
-	 }
-	 else
-	 {
-	    o -= 1;
-	    M = 12;
-	    m = 4;
-	    N = 5;
-	 }
-	 
-	 if (!fiasco_c_options_set_optimizations (*options, m, M, N,
-						  max (0, D), o))
-	    error (fiasco_get_error_message ());
-      }
-      {
-	 int M = * (int *) parameter_value (params, "max-level");
-	 int m = * (int *) parameter_value (params, "min-level");
-	 int p = * (int *) parameter_value (params, "prediction");
-	 
-	 if (!fiasco_c_options_set_prediction (*options,
-					       p, max (0, m), max (0, M)))
-	    error (fiasco_get_error_message ());
-      }
-      {
-	 float r    = * (float *) parameter_value (params, "rpf-range");
-	 float dc_r = * (float *) parameter_value (params, "dc-rpf-range");
-	 int   m    = * (int *)   parameter_value (params, "rpf-mantissa");
-	 int   dc_m = * (int *)   parameter_value (params, "dc-rpf-mantissa");
-	 fiasco_rpf_range_e range, dc_range;
-	 
-	 if (r < 1)
-	    range = FIASCO_RPF_RANGE_0_75;
-	 else if (r < 1.5)
-	    range = FIASCO_RPF_RANGE_1_00;
-	 else if (r < 2.0)
-	    range = FIASCO_RPF_RANGE_1_50;
-	 else
-	    range = FIASCO_RPF_RANGE_2_00;
-	    
-	 if (dc_r < 1)
-	    dc_range = FIASCO_RPF_RANGE_0_75;
-	 else if (dc_r < 1.5)
-	    dc_range = FIASCO_RPF_RANGE_1_00;
-	 else if (dc_r < 2.0)
-	    dc_range = FIASCO_RPF_RANGE_1_50;
-	 else
-	    dc_range = FIASCO_RPF_RANGE_2_00;
-	    
-	 if (!fiasco_c_options_set_quantization (*options,
-						 max (0, m), range,
-						 max (0, dc_m), dc_range))
-	    error (fiasco_get_error_message ());
-      }
+            if (o <= 0)
+            {
+                o = 0;
+                M = 10;
+                m = 6;
+                N = 3;
+            }
+            else
+            {
+                o -= 1;
+                M = 12;
+                m = 4;
+                N = 5;
+            }
+     
+            if (!fiasco_c_options_set_optimizations (*options, m, M, N,
+                                                     MAX(0, D), o))
+                error (fiasco_get_error_message ());
+        }
+        {
+            int M = * (int *) parameter_value (params, "max-level");
+            int m = * (int *) parameter_value (params, "min-level");
+            int p = * (int *) parameter_value (params, "prediction");
+     
+            if (!fiasco_c_options_set_prediction (*options,
+                                                  p, MAX(0, m), MAX(0, M)))
+                error (fiasco_get_error_message ());
+        }
+        {
+            float r    = * (float *)parameter_value(params, "rpf-range");
+            float dc_r = * (float *)parameter_value(params, "dc-rpf-range");
+            int   m    = * (int *)  parameter_value(params, "rpf-mantissa");
+            int   dc_m = * (int *)  parameter_value(params, "dc-rpf-mantissa");
+            fiasco_rpf_range_e range, dc_range;
+     
+            if (r < 1)
+                range = FIASCO_RPF_RANGE_0_75;
+            else if (r < 1.5)
+                range = FIASCO_RPF_RANGE_1_00;
+            else if (r < 2.0)
+                range = FIASCO_RPF_RANGE_1_50;
+            else
+                range = FIASCO_RPF_RANGE_2_00;
+        
+            if (dc_r < 1)
+                dc_range = FIASCO_RPF_RANGE_0_75;
+            else if (dc_r < 1.5)
+                dc_range = FIASCO_RPF_RANGE_1_00;
+            else if (dc_r < 2.0)
+                dc_range = FIASCO_RPF_RANGE_1_50;
+            else
+                dc_range = FIASCO_RPF_RANGE_2_00;
+        
+            if (!fiasco_c_options_set_quantization (*options,
+                                                    MAX(0, m), range,
+                                                    MAX(0, dc_m), dc_range))
+                error (fiasco_get_error_message ());
+        }
 
-      if (fiasco_get_verbosity () == FIASCO_ULTIMATE_VERBOSITY)
-	 write_parameters (params, stderr);
-   }
-}	
+        if (fiasco_get_verbosity () == FIASCO_ULTIMATE_VERBOSITY)
+            write_parameters (params, stderr);
+    }
+}   
diff --git a/converter/other/fitstopnm.c b/converter/other/fitstopnm.c
index 73564c4b..bdf5c78a 100644
--- a/converter/other/fitstopnm.c
+++ b/converter/other/fitstopnm.c
@@ -34,10 +34,16 @@
   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
+
+  An example FITS file is at
+
+    http://fits.gsfc.nasa.gov/nrao_data/tests/incunabula/mndrll-8.fits
+
 */
 
 #include <string.h>
 #include <float.h>
+#include <assert.h>
 
 #include "pm_config.h"
 #include "pm_c_util.h"
@@ -48,7 +54,7 @@
 
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     const char * inputFileName;
     unsigned int image;  /* zero if unspecified */
     float max;
@@ -69,8 +75,8 @@ struct cmdlineInfo {
 
 
 static void 
-parseCommandLine(int argc, char ** argv, 
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** argv, 
+                 struct CmdlineInfo * const cmdlineP) {
 /* --------------------------------------------------------------------------
    Parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -82,7 +88,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 --------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options. */
+        /* Instructions to pm_optParseOptions3 on how to parse our options. */
     optStruct3 opt;
 
     unsigned int imageSpec;
@@ -114,7 +120,7 @@ parseCommandLine(int argc, char ** argv,
 
     /* Set some defaults the lazy way (using multiple setting of variables) */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char**)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (imageSpec) {
@@ -139,13 +145,17 @@ parseCommandLine(int argc, char ** argv,
             pm_error("Too many arguments (%u).  The only non-option argument "
                      "is the input file name.", argc-1);
     }
+    free(option_def);
 }
 
 
 
 struct FITS_Header {
   int simple;       /* basic format or not */
-  int bitpix;       /* number of bits per pixel */
+  int bitpix;
+      /* number of bits per pixel, positive for integer, negative 
+         for floating point
+      */
   int naxis;        /* number of axes */
   int naxis1;       /* number of points on axis 1 */
   int naxis2;       /* number of points on axis 2 */
@@ -157,6 +167,16 @@ struct FITS_Header {
 };
 
 
+typedef enum {
+    VF_CHAR, VF_SHORT, VF_LONG, VF_FLOAT, VF_DOUBLE
+} valFmt;
+
+struct fitsRasterInfo {
+    valFmt valFmt;
+    double bzer;
+    double bscale;
+};
+
 /* 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
@@ -276,34 +296,56 @@ readFitsDouble(FILE *   const ifP,
 
 
 
+static valFmt
+valFmtFromBitpix(int const bitpix) {
+/*----------------------------------------------------------------------------
+   Return the format of a "value" in the FITS file, given the value
+   of the BITPIX header in the FITS file.
+
+   BITPIX has a stupid format wherein it is fundamentally the number
+   of bits per value, but its sign indicates whether it is integer
+   or floating point.
+-----------------------------------------------------------------------------*/
+    switch (bitpix) {
+    case  +8: return VF_CHAR;
+    case +16: return VF_SHORT;
+    case +32: return VF_LONG;
+    case -32: return VF_FLOAT;
+    case -64: return VF_DOUBLE;
+    default:
+        /* Every possibility is covered above. */
+        assert(false);
+        return 0;  /* quiet compiler warning */
+    }
+}
+
+
+
 static void
 readVal(FILE *   const ifP,
-        int      const bitpix,
+        valFmt   const fmt,
         double * const vP) {
 
-    switch (bitpix) {
-    case 8:
+    switch (fmt) {
+    case VF_CHAR:
         readFitsChar(ifP, vP);
         break;
 
-    case 16:
+    case VF_SHORT:
         readFitsShort(ifP, vP);
         break;
       
-    case 32:
+    case VF_LONG:
         readFitsLong(ifP, vP);
         break;
       
-    case -32:
+    case VF_FLOAT:
         readFitsFloat(ifP, vP);
         break;
       
-    case -64:
+    case VF_DOUBLE:
         readFitsDouble(ifP, vP);
         break;
-      
-    default:
-        pm_error("Strange bitpix value %d in readVal()", bitpix);
     }
 }
 
@@ -419,7 +461,7 @@ scanImageForMinMax(FILE *       const ifP,
                    unsigned int const images,
                    int          const cols,
                    int          const rows,
-                   unsigned int const bitpix,
+                   valFmt       const valFmt,
                    double       const bscale,
                    double       const bzer,
                    unsigned int const imagenum,
@@ -427,6 +469,9 @@ scanImageForMinMax(FILE *       const ifP,
                    double *     const dataminP,
                    double *     const datamaxP) {
 
+    /* Note that a value in the file might be Not-A-Number.  We ignore
+       such entries in computing the minimum and maximum for the image.
+    */
     double dmax, dmin;
     unsigned int image;
     pm_filepos rasterPos;
@@ -436,14 +481,12 @@ scanImageForMinMax(FILE *       const ifP,
 
     pm_message("Scanning file for scaling parameters");
 
-    switch (bitpix) {
-    case   8: fmaxval = 255.0;        break;
-    case  16: fmaxval = 65535.0;      break;
-    case  32: fmaxval = 4294967295.0; break;
-    case -32: fmaxval = FLT_MAX;      break;
-    case -64: fmaxval = DBL_MAX;      break;
-    default:
-        pm_error("unusual bits per pixel (%u), can't read", bitpix);
+    switch (valFmt) {
+    case VF_CHAR:   fmaxval = 255.0;        break;
+    case VF_SHORT:  fmaxval = 65535.0;      break;
+    case VF_LONG:   fmaxval = 4294967295.0; break;
+    case VF_FLOAT:  fmaxval = FLT_MAX;      break;
+    case VF_DOUBLE: fmaxval = DBL_MAX;      break;
     }
 
     dmax = -fmaxval;
@@ -454,10 +497,11 @@ scanImageForMinMax(FILE *       const ifP,
             unsigned int col;
             for (col = 0; col < cols; ++col) {
                 double val;
-                readVal(ifP, bitpix, &val);
+                readVal(ifP, valFmt, &val);
                 if (image == imagenum || multiplane ) {
-                    dmax = MAX(dmax, val);
-                    dmin = MIN(dmin, val);
+                    /* Note: if 'val' is NaN, result is 2nd operand */
+                    dmax = MAX(val, dmax);
+                    dmin = MIN(val, dmin);
                 }
             }
         }
@@ -509,7 +553,8 @@ computeMinMax(FILE *             const ifP,
 
     if (datamin == -DBL_MAX || datamax == DBL_MAX) {
         double scannedDatamin, scannedDatamax;
-        scanImageForMinMax(ifP, images, cols, rows, h.bitpix, h.bscale, h.bzer,
+        scanImageForMinMax(ifP, images, cols, rows,
+                           valFmtFromBitpix(h.bitpix), h.bscale, h.bzer,
                            imagenum, multiplane,
                            &scannedDatamin, &scannedDatamax);
 
@@ -525,8 +570,8 @@ computeMinMax(FILE *             const ifP,
 
 
 static xelval
-determineMaxval(struct cmdlineInfo const cmdline,
-                struct FITS_Header const fitsHeader,
+determineMaxval(struct CmdlineInfo const cmdline,
+                valFmt             const valFmt,
                 double             const datamax,
                 double             const datamin) {
 
@@ -535,7 +580,7 @@ determineMaxval(struct cmdlineInfo const cmdline,
     if (cmdline.omaxvalSpec)
         retval = cmdline.omaxval;
     else {
-        if (fitsHeader.bitpix < 0) {
+        if (valFmt == VF_FLOAT || valFmt == VF_DOUBLE) {
             /* 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
@@ -561,16 +606,16 @@ determineMaxval(struct cmdlineInfo const cmdline,
 
 
 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) {
+convertPgmRaster(FILE *                const ifP,
+                 unsigned int          const cols,
+                 unsigned int          const rows,
+                 xelval                const maxval,
+                 unsigned int          const desiredImage,
+                 unsigned int          const imageCount,
+                 struct fitsRasterInfo const rasterInfo,
+                 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
@@ -581,8 +626,7 @@ convertPgmRaster(FILE *             const ifP,
     */
     unsigned int image;
 
-    pm_message("Writing PPM file "
-               "(Probably not what you want - consider an -image option)");
+    pm_message("writing PGM file");
 
     for (image = 1; image <= desiredImage; ++image) {
         unsigned int row;
@@ -594,10 +638,10 @@ convertPgmRaster(FILE *             const ifP,
             unsigned int col;
             for (col = 0; col < cols; ++col) {
                 double val;
-                readVal(ifP, fitsHdr.bitpix, &val);
+                readVal(ifP, rasterInfo.valFmt, &val);
                 {
                     double const t = scale *
-                        (val * fitsHdr.bscale + fitsHdr.bzer - datamin);
+                        (val * rasterInfo.bscale + rasterInfo.bzer - datamin);
                     xelval const tx = MAX(0, MIN(t, maxval));
                     if (image == desiredImage)
                         PNM_ASSIGN1(xels[row][col], tx);
@@ -610,14 +654,14 @@ convertPgmRaster(FILE *             const ifP,
 
 
 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) {
+convertPpmRaster(FILE *                const ifP,
+                 unsigned int          const cols,
+                 unsigned int          const rows,
+                 xelval                const maxval,
+                 struct fitsRasterInfo const rasterInfo,
+                 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
@@ -625,7 +669,8 @@ convertPpmRaster(FILE *             const ifP,
 -----------------------------------------------------------------------------*/
     unsigned int plane;
 
-    pm_message("writing PPM file");
+    pm_message("Writing PPM file "
+               "(Probably not what you want - consider an -image option)");
 
     for (plane = 0; plane < 3; ++plane) {
         unsigned int row;
@@ -635,10 +680,10 @@ convertPpmRaster(FILE *             const ifP,
             unsigned int col;
             for (col = 0; col < cols; ++col) {
                 double val;
-                readVal(ifP, fitsHdr.bitpix, &val);
+                readVal(ifP, rasterInfo.valFmt, &val);
                 {
                     double const t = scale *
-                        (val * fitsHdr.bscale + fitsHdr.bzer - datamin);
+                        (val * rasterInfo.bscale + rasterInfo.bzer - datamin);
                     xelval const sample = MAX(0, MIN(t, maxval));
 
                     switch (plane) {
@@ -655,17 +700,17 @@ convertPpmRaster(FILE *             const ifP,
 
 
 static void
-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) {
+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 fitsRasterInfo const rasterInfo,
+              double                const scale,
+              double                const datamin) {
 
     xel ** xels;
     int format;
@@ -674,12 +719,12 @@ convertRaster(FILE *             const ifP,
 
     if (multiplane) {
         format = PPM_FORMAT;
-        convertPpmRaster(ifP, cols, rows, maxval, fitsHdr, scale, datamin,
+        convertPpmRaster(ifP, cols, rows, maxval, rasterInfo, scale, datamin,
                          xels);
     } else {
         format = PGM_FORMAT;
         convertPgmRaster(ifP, cols, rows, maxval,
-                         desiredImage, imageCount, fitsHdr, scale, datamin,
+                         desiredImage, imageCount, rasterInfo, scale, datamin,
                          xels);
     }
     pnm_writepnm(stdout, xels, cols, rows, maxval, format, forceplain);
@@ -689,15 +734,16 @@ convertRaster(FILE *             const ifP,
 
 
 int
-main(int argc, char * argv[]) {
+main(int argc, const char * argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     unsigned int cols, rows;
     xelval maxval;
     double scale;
     double datamin, datamax;
     struct FITS_Header fitsHeader;
+    struct fitsRasterInfo rasterInfo;
 
     unsigned int imageCount;
     unsigned int desiredImage;
@@ -709,7 +755,7 @@ main(int argc, char * argv[]) {
            is undefined
         */
   
-    pnm_init( &argc, argv );
+    pm_proginit(&argc, argv);
   
     parseCommandLine(argc, argv, &cmdline);
 
@@ -726,6 +772,10 @@ main(int argc, char * argv[]) {
     cols = fitsHeader.naxis1;
     rows = fitsHeader.naxis2;
 
+    rasterInfo.bscale = fitsHeader.bscale;
+    rasterInfo.bzer   = fitsHeader.bzer;
+    rasterInfo.valFmt = valFmtFromBitpix(fitsHeader.bitpix);
+
     interpretPlanes(fitsHeader, cmdline.image, cmdline.verbose,
                     &imageCount, &multiplane, &desiredImage);
 
@@ -735,7 +785,7 @@ main(int argc, char * argv[]) {
                   cmdline.min, cmdline.max,
                   &datamin, &datamax);
 
-    maxval = determineMaxval(cmdline, fitsHeader, datamax, datamin);
+    maxval = determineMaxval(cmdline, rasterInfo.valFmt, datamax, datamin);
 
     if (datamax - datamin == 0)
         scale = 1.0;
@@ -747,10 +797,13 @@ main(int argc, char * argv[]) {
     else
         convertRaster(ifP, cols, rows, maxval, cmdline.noraw,
                       multiplane, desiredImage, imageCount,
-                      fitsHeader, scale, datamin);
+                      rasterInfo, scale, datamin);
 
     pm_close(ifP);
     pm_close(stdout);
 
     return 0;
 }
+
+
+
diff --git a/converter/other/giftopnm.c b/converter/other/giftopnm.c
index 4cba5068..76cf4bff 100644
--- a/converter/other/giftopnm.c
+++ b/converter/other/giftopnm.c
@@ -8,8 +8,9 @@
 /* |   provided "as is" without express or implied warranty.           | */
 /* +-------------------------------------------------------------------+ */
 
-/* There is a copy of the GIF89 specification, as defined by its
-   inventor, Compuserve, in 1989, at http://members.aol.com/royalef/gif89a.txt
+/* There is a copy of the GIF89 specification, as defined by its inventor,
+   Compuserve, in 1990 at: 
+   http://www.w3.org/Graphics/GIF/spec-gif89a.txt
 
    This covers the high level format, but does not cover how the "data"
    contents of a GIF image represent the raster of color table indices.
@@ -18,11 +19,12 @@
    describe the Lempel-Ziv base.
 */
 
-#define _BSD_SOURCE   /* Make sure strcasecmp() is in string.h */
-
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+#define _BSD_SOURCE   /* for strcaseeq */
 #include <string.h>
 #include <assert.h>
 
+#include "pm_config.h"
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
@@ -38,35 +40,18 @@
 
 #define MAX_LZW_BITS  12
 
-#define INTERLACE      0x40
-#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
-
-#if defined(__x86_64__) | defined(__i486__) | defined(__vax__)
-# define UNALIGNED_OK 1
-#else
-# define UNALIGNED_OK 0
+#ifndef   FASTPBMRENDER
+  #define FASTPBMRENDER TRUE
 #endif
 
+static const bool useFastPbmRender = FASTPBMRENDER;
 
+#ifndef   REPORTLZWCODES
+  #define REPORTLZWCODES FALSE
+#endif
 
-static __inline__ bool
-ReadOK(FILE *          const fileP,
-       unsigned char * const buffer,
-       size_t          const len) {
-
-    size_t bytesRead;
-
-    bytesRead = fread(buffer, len, 1, fileP);
-
-    return (bytesRead != 0);
-}
+static const bool wantLzwCodes = REPORTLZWCODES;
+    /* In verbose output, include all the LZW codes */
 
 
 
@@ -78,41 +63,37 @@ readFile(FILE *          const ifP,
 
     size_t bytesRead;
 
-    bytesRead = fread(buffer, len, 1, ifP);
+    bytesRead = fread(buffer, 1, len, ifP);
 
     if (bytesRead == len)
         *errorP = NULL;
     else {
         if (ferror(ifP))
-            asprintfN(errorP, "Error reading file.  errno=%d (%s)",
-                      errno, strerror(errno));
+            pm_asprintf(errorP, "Error reading file.  errno=%d (%s)",
+                        errno, strerror(errno));
         else if (feof(ifP))
-            asprintfN(errorP, "End of file encountered");
+            pm_asprintf(errorP, "End of file encountered");
         else
-            asprintfN(errorP, "Short read -- %u bytes of %u",
-                              (unsigned)bytesRead, (unsigned)len);
+            pm_asprintf(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);
-
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char * input_filespec;  /* Filespecs of input files */
+    const char * inputFilespec;  /* Filespecs of input files */
     unsigned int verbose;    /* -verbose option */
     unsigned int comments;   /* -comments option */
-    bool all_images;  /* He wants all the images */
-    unsigned int image_no;
+    bool allImages;  /* He wants all the images */
+    unsigned int imageNum;
         /* image number he wants from input, starting at 0.  Undefined
-           if all_images is TRUE
+           if allImages is TRUE
         */
-    const char * alpha_filename;
+    const char * alphaFileName;
     unsigned int quitearly;
     unsigned int repair;
 };
@@ -121,13 +102,13 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     
     optStruct3 opt;
@@ -150,120 +131,198 @@ parseCommandLine(int argc, char ** argv,
             &cmdlineP->repair,          0);
     OPTENT3(0, "image",       OPT_STRING, &image,
             &imageSpec,                 0);
-    OPTENT3(0, "alphaout",    OPT_STRING, &cmdlineP->alpha_filename, 
+    OPTENT3(0, "alphaout",    OPT_STRING, &cmdlineP->alphaFileName, 
             &alphaSpec,                 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
+    free(option_def);
+
     if (!imageSpec) {
-        cmdlineP->image_no = 0;
-        cmdlineP->all_images = FALSE;
+        cmdlineP->imageNum = 0;
+        cmdlineP->allImages = FALSE;
     } else {
-        if (strcasecmp(image, "all") == 0) 
-            cmdlineP->all_images = TRUE;
-        else {
+        if (strcaseeq(image, "all")) { 
+            cmdlineP->allImages = TRUE;
+        } else {
             char * tailptr;
 
-            long const imageNo = strtol(image, &tailptr, 10);
+            long const imageNum = strtol(image, &tailptr, 10);
 
             if (*tailptr != '\0')
                 pm_error("Invalid value for '-image' option.  Must be either "
                          "a number or 'all'.  You specified '%s'", image);
-            else if (imageNo < 0)
+            else if (imageNum < 0)
                 pm_error("Invalid value for '-image' option.  Must be "
-                         "positive.  You specified %ld", imageNo);
-            else if (imageNo == 0)
+                         "positive.  You specified %ld", imageNum);
+            else if (imageNum == 0)
                 pm_error("Invalid value for 'image' option.  You specified "
                          "zero.  The first image is 1.");
 
-            cmdlineP->all_images = FALSE;
-            cmdlineP->image_no = (unsigned int) imageNo - 1;
+            cmdlineP->allImages = FALSE;
+            cmdlineP->imageNum = (unsigned int) imageNum - 1;
         }
     }
     
     if (argc-1 == 0) 
-        cmdlineP->input_filespec = "-";
+        cmdlineP->inputFilespec = "-";
     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];
+        cmdlineP->inputFilespec = argv[1];
 
     if (!alphaSpec) 
-        cmdlineP->alpha_filename = NULL;
+        cmdlineP->alphaFileName = NULL;
 }
 
 
-typedef unsigned char gifColorMap[3][MAXCOLORMAPSIZE];
 
-struct gifScreen {
-    unsigned int    Width;
-    unsigned int    Height;
-    gifColorMap     ColorMap;
-    unsigned int    ColorMapSize;
-        /* Number of colors in the color map. */
-    unsigned int    ColorResolution;
-    unsigned int    Background;
-    unsigned int    AspectRatio;
+typedef struct {
+    unsigned char map[MAXCOLORMAPSIZE][3];
+    unsigned int size;
+} GifColorMap;
+
+/*----------------------------------------------------------------------
+  On GIF color maps:
+
+  The color map can have any number of colors up to 256.  If the color map
+  size is not a power of 2 the table is padded up.  The LZW "clear" control
+  code is always placed at a power of 2.
+
+  The color map and code table of an image with three colors (black, white
+  and red) will look like this:
+
+  0: black
+  1: white
+  2: red
+  3: (unused)
+  4: clear code
+  5: end code
+  6: first LZW string code
+  7: second LZW string code
+     ...
+  4095: last LZW string code
+
+  Some GIFs have odd color maps.
+
+  (1) Some encoders use fixed color maps.  A GIF image produced by this
+      kind of encoder may have colors in the table which never appear in
+      the image.
+
+      Note that we make the decision on whether the output should be PBM,
+      PGM or PPM by scanning through the color map, not the entire image.
+      Any unused colors will influence our decision.
+
+  (2) There are GIF editors which allow one to rewrite the color map.
+      These programs will produce color maps with multiple entries for the
+      same color.
+
+  (3) Some encoders put the transparent code outside the color map.
+      (In the above example, the unused value 3.)  Around 2000 there were
+      several encoders that did this, including "Animation Gif Maker
+      (GifAnim)".  As of 2012, such images are rare.  We reject them with
+      an error message unless -repair is specified.
+-----------------------------------------------------------------------*/
+
+
+struct GifScreen {
+    unsigned int    width;
+    unsigned int    height;
+    bool            hasGlobalColorMap;
+        /* The stream has a global color map, to wit 'colorMap'.
+           (If the stream doesn't have a global color map, the individual
+           images must each have a local color map)
+        */
+    GifColorMap     colorMap;
+        /* The global color map for the stream.  Meaningful only if
+           'hasGlobalColorMap' is true.
+        */
+    unsigned int    colorResolution;
+    unsigned int    background;
+    unsigned int    aspectRatio;
         /* Aspect ratio of each pixel, times 64, minus 15.  (i.e. 1 => 1:4).
            But Zero means 1:1.
         */
-    int      hasGray;  
+    bool     hasGray;  
         /* Boolean: global colormap has at least one gray color
            (not counting black and white) 
         */
-    int      hasColor;
+    bool     hasColor;
         /* Boolean: global colormap has at least one non-gray,
            non-black, non-white color 
         */
 };
 
-struct gif89 {
-       int     transparent;
-       int     delayTime;
-       int     inputFlag;
-       int     disposal;
+struct Gif89 {
+    bool         haveTransColor;
+        /* The GIF specifies a transparent background color */
+    unsigned int transparentIndex;
+        /* The color index of the color which is the transparent 
+           background color.
+
+           Meaningful only when 'haveTransColor' is true
+        */
+    bool          haveDelayTime;
+    unsigned int  delayTime;
+    bool          haveInputFlag;
+    unsigned char inputFlag;
+    bool          haveDisposal;
+    unsigned char disposal;
 };
 
 static void
-initGif89(struct gif89 * const gif89P) {
-    gif89P->transparent = -1;
-    gif89P->delayTime = -1;
-    gif89P->inputFlag = -1;
-    gif89P->disposal = -1;
+initGif89(struct Gif89 * const gif89P) {
+    gif89P->haveTransColor = false;
+    gif89P->haveDelayTime  = false;
+    gif89P->haveInputFlag  = false;
+    gif89P->haveDisposal   = false;
 }       
 
 
-static int verbose;
-int    showComment;
+static bool verbose;
+static bool showComment;
 
 
 
 static void
-readColorMap(FILE *ifP, const int colormapsize, 
-             unsigned char colormap[3][MAXCOLORMAPSIZE],
-             int *hasGrayP, int * const hasColorP) {
+readColorMap(FILE *        const ifP,
+             unsigned int  const cmapSize,
+             GifColorMap * const cmapP,
+             bool *        const hasGrayP,
+             bool *        const hasColorP) {
+/*----------------------------------------------------------------------------
+   Read a color map from a GIF stream, where the stream is on *ifP,
+   which is positioned to a color map, which is 'cmapSize' bytes long.
 
-    int             i;
-    unsigned char   rgb[3];
+   Return as *cmapP that color map.
 
-    assert(colormapsize <= MAXCOLORMAPSIZE);
+   Furthermore, analyze that color map and return *hasGrayP == true iff it
+   contains any gray (black and white don't count) and *hasColorP == true iff
+   it contains anything that is not gray or black or white.
+-----------------------------------------------------------------------------*/
+    unsigned int  i;
+    unsigned char rgb[3];
+
+    assert(cmapSize <= MAXCOLORMAPSIZE);
 
     *hasGrayP = FALSE;  /* initial assumption */
     *hasColorP = FALSE;  /* initial assumption */
 
-    for (i = 0; i < colormapsize; ++i) {
-        if (! ReadOK(ifP, rgb, sizeof(rgb)))
-            pm_error("Unable to read Color %d from colormap", i);
+    for (i = 0; i < cmapSize; ++i) {
+        const char * error;
+        readFile(ifP, rgb, sizeof(rgb), &error);
+        if (error)
+            pm_error("Unable to read Color %u from colormap.  %s", i, error);
 
-        colormap[CM_RED][i] = rgb[0] ;
-        colormap[CM_GRN][i] = rgb[1] ;
-        colormap[CM_BLU][i] = rgb[2] ;
+        cmapP->map[i][CM_RED] = rgb[0] ;
+        cmapP->map[i][CM_GRN] = rgb[1] ;
+        cmapP->map[i][CM_BLU] = rgb[2] ;
 
         if (rgb[0] == rgb[1] && rgb[1] == rgb[2]) {
             if (rgb[0] != 0 && rgb[0] != GIFMAXVAL)
@@ -271,6 +330,7 @@ readColorMap(FILE *ifP, const int colormapsize,
         } else
             *hasColorP = TRUE;
     }
+    cmapP->size = cmapSize;
 }
 
 
@@ -302,13 +362,17 @@ getDataBlock(FILE *          const ifP,
    If we hit EOF or have an I/O error reading the data portion of the
    DataBlock, we exit the program with pm_error().
 -----------------------------------------------------------------------------*/
+    long const pos = ftell(ifP);
+
     unsigned char count;
-    bool successfulRead;
+    const char * error;
     
-    long const pos = ftell(ifP);
-    successfulRead = ReadOK(ifP, &count, 1);
-    if (!successfulRead) {
-        pm_message("EOF or error in reading DataBlock size from file" );
+    readFile(ifP, &count, sizeof(count), &error);
+
+    if (error) {
+        pm_message("EOF or error in reading DataBlock size from file.  %s",
+                   error);
+        pm_strfree(error);
         *errorP = NULL;
         *eofP = TRUE;
         *lengthP = 0;
@@ -322,17 +386,18 @@ getDataBlock(FILE *          const ifP,
             *errorP = NULL;
             zeroDataBlock = TRUE;
         } else {
-            bool successfulRead;
+            const char * error;
 
             zeroDataBlock = FALSE;
-            successfulRead = ReadOK(ifP, buf, count); 
+            readFile(ifP, buf, count, &error); 
 
-            if (successfulRead) 
+            if (error) {
+                pm_asprintf(errorP,
+                            "Unable to read data portion of %u byte "
+                            "DataBlock from file.  %s", count, error);
+                pm_strfree(error);
+            } else
                 *errorP = NULL;
-            else
-                asprintfN(errorP,
-                          "EOF or error reading data portion of %u byte "
-                          "DataBlock from file", count);
         }
     }
 }
@@ -348,7 +413,7 @@ readThroughEod(FILE * const ifP) {
   If there is no EOD marker between the present file position and EOF,
   we read to EOF and issue warning message about a missing EOD marker.
 -----------------------------------------------------------------------------*/
-    unsigned char buf[260];
+    unsigned char buf[256];
     bool eod;
 
     eod = FALSE;  /* initial value */
@@ -371,6 +436,27 @@ readThroughEod(FILE * const ifP) {
 
 
 static void
+doPlainTextExtension(FILE * const ifP) {
+#if 0
+    /* incomplete code fragment, attempt to handle Plain Text Extension */
+    GetDataBlock(ifP, (unsigned char*) buf, &eof, &length);
+    
+    lpos   = LM_to_uint(buf[0], buf[1]);
+    tpos   = LM_to_uint(buf[2], buf[3]);
+    width  = LM_to_uint(buf[4], buf[5]);
+    height = LM_to_uint(buf[6], buf[7]);
+    cellw  = buf[8];
+    cellh  = buf[9];
+    foreground = buf[10];
+    background = buf[11];
+#else
+    readThroughEod(ifP);
+#endif
+}
+
+
+
+static void
 doCommentExtension(FILE * const ifP) {
 /*----------------------------------------------------------------------------
    Read the rest of a comment extension from the input file 'ifP' and handle
@@ -380,7 +466,7 @@ doCommentExtension(FILE * const ifP) {
    it could have nonprintable characters or embedded nulls.  I don't know if
    the GIF spec requires regular text or not.
 -----------------------------------------------------------------------------*/
-    char buf[255+1];
+    char buf[256];
     unsigned int blocklen;  
     bool done;
 
@@ -405,13 +491,22 @@ doCommentExtension(FILE * const ifP) {
 
 
 
+static unsigned int
+LM_to_uint(unsigned char const a,
+           unsigned char const b) {
+
+    return ((unsigned int)b << 8) | ((unsigned int) a << 0);
+}
+
+
+
 static void 
 doGraphicControlExtension(FILE *         const ifP,
-                          struct gif89 * const gif89P) {
+                          struct Gif89 * const gif89P) {
 
     bool eof;
     unsigned int length;
-    static unsigned char buf[256];
+    unsigned char buf[256];
     const char * error;
 
     getDataBlock(ifP, buf, &eof, &length, &error);
@@ -425,11 +520,16 @@ doGraphicControlExtension(FILE *         const ifP,
                  "It must be at least 4 bytes; it is %d bytes.",
                  length);
     else {
+        gif89P->haveDisposal = true;
         gif89P->disposal = (buf[0] >> 2) & 0x7;
+        gif89P->haveInputFlag = true;
         gif89P->inputFlag = (buf[0] >> 1) & 0x1;
-        gif89P->delayTime = LM_to_uint(buf[1],buf[2]);
-        if ((buf[0] & 0x1) != 0)
-            gif89P->transparent = buf[3];
+        gif89P->haveDelayTime = true;
+        gif89P->delayTime = LM_to_uint(buf[1], buf[2]);
+        if ((buf[0] & 0x1) != 0) {
+            gif89P->haveTransColor = true;
+            gif89P->transparentIndex = buf[3];
+        }
         readThroughEod(ifP);
     }
 }
@@ -437,34 +537,16 @@ doGraphicControlExtension(FILE *         const ifP,
 
 
 static void
-doExtension(FILE * const ifP, int const label, struct gif89 * const gif89P) {
+doExtension(FILE *         const ifP,
+            unsigned char  const label,
+            struct Gif89 * const gif89P) {
+
     const char * str;
     
     switch (label) {
     case 0x01:              /* Plain Text Extension */
         str = "Plain Text";
-#ifdef notdef
-        GetDataBlock(ifP, (unsigned char*) buf, &eof, &length);
-        
-        lpos   = LM_to_uint(buf[0], buf[1]);
-        tpos   = LM_to_uint(buf[2], buf[3]);
-        width  = LM_to_uint(buf[4], buf[5]);
-        height = LM_to_uint(buf[6], buf[7]);
-        cellw  = buf[8];
-        cellh  = buf[9];
-        foreground = buf[10];
-        background = buf[11];
-        
-        while (GetDataBlock(ifP, (unsigned char*) buf) != 0) {
-            PPM_ASSIGN(xels[ypos][xpos],
-                       cmap[CM_RED][v],
-                       cmap[CM_GRN][v],
-                       cmap[CM_BLU][v]);
-            ++index;
-        }
-#else
-        readThroughEod(ifP);
-#endif
+        doPlainTextExtension(ifP);
         break;
     case 0xff:              /* Application Extension */
         str = "Application";
@@ -479,21 +561,20 @@ doExtension(FILE * const ifP, int const label, struct gif89 * const gif89P) {
         doGraphicControlExtension(ifP, gif89P);
         break;
     default: {
-        static char buf[256];
-        str = buf;
+        char buf[256];
         sprintf(buf, "UNKNOWN (0x%02x)", label);
+        str = buf;
         pm_message("Ignoring unrecognized extension (type 0x%02x)", label);
         readThroughEod(ifP);
-        }
-        break;
+    } break;
     }
     if (verbose)
-        pm_message(" got a '%s' extension", str );
+        pm_message(" got a '%s' extension", str);
 }
 
 
 
-struct getCodeState {
+struct GetCodeState {
     unsigned char buf[280];
         /* This is the buffer through which we read the data from the 
            stream.  We must buffer it because we have to read whole data
@@ -519,11 +600,11 @@ struct getCodeState {
 
 static void
 getAnotherBlock(FILE *                const ifP, 
-                struct getCodeState * const gsP,
+                struct GetCodeState * const gsP,
                 const char **         const errorP) {
 
     unsigned int count;
-    unsigned int assumed_count;
+    unsigned int assumedCount;
     bool eof;
 
     /* Shift buffer down so last two bytes are now the
@@ -548,22 +629,22 @@ getAnotherBlock(FILE *                const ifP,
                        "file is malformed, but we are proceeding "
                        "anyway as if an EOD marker were at the end "
                        "of the file.");
-            assumed_count = 0;
+            assumedCount = 0;
         } else
-            assumed_count = count;
+            assumedCount = count;
 
-        gsP->streamExhausted = (assumed_count == 0);
+        gsP->streamExhausted = (assumedCount == 0);
         
-        gsP->bufCount += assumed_count;
+        gsP->bufCount += assumedCount;
     }
 }
 
 
 
-static struct getCodeState getCodeState;
+static struct GetCodeState getCodeState;
 
 static void
-getCode_init(struct getCodeState * const getCodeStateP) {
+getCode_init(struct GetCodeState * const getCodeStateP) {
     
     /* Fake a previous data block */
     getCodeStateP->buf[0] = 0;
@@ -615,7 +696,7 @@ bitsOfLeBuffer(const unsigned char * const buf,
 
 
 static void
-getCode_get(struct getCodeState * const gsP,
+getCode_get(struct GetCodeState * const gsP,
             FILE *                const ifP, 
             int                   const codeSize,
             bool *                const eofP,
@@ -668,6 +749,9 @@ getCode_get(struct getCodeState * const gsP,
         } else {
             *codeP = bitsOfLeBuffer(gsP->buf, gsP->curbit, codeSize);
 
+            if (verbose && wantLzwCodes)
+                pm_message("LZW code=0x%03x [%d]", *codeP, codeSize);
+
             gsP->curbit += codeSize;
             *eofP = FALSE;
         }
@@ -676,18 +760,17 @@ getCode_get(struct getCodeState * const gsP,
 
 
 
-
-struct stack {
+struct Stack {
     /* Stack grows from low addresses to high addresses */
-    int * stack;  /* malloc'ed array */
-    int * sp;     /* stack pointer */
-    int * top;    /* next word above top of stack */
+    unsigned char * stack;  /* malloc'ed array */
+    unsigned char * sp;     /* stack pointer */
+    unsigned char * top;    /* next word above top of stack */
 };
 
 
 
 static void 
-initStack(struct stack * const stackP, unsigned int const size) {
+initStack(struct Stack * const stackP, unsigned int const size) {
 
     MALLOCARRAY(stackP->stack, size);
     if (stackP->stack == NULL)
@@ -699,7 +782,7 @@ initStack(struct stack * const stackP, unsigned int const size) {
 
 
 static void
-pushStack(struct stack * const stackP, int const value) {
+pushStack(struct Stack * const stackP, unsigned char const value) {
 
     if (stackP->sp >= stackP->top)
         pm_error("stack overflow");
@@ -710,14 +793,14 @@ pushStack(struct stack * const stackP, int const value) {
 
 
 static bool
-stackIsEmpty(const struct stack * const stackP) {
+stackIsEmpty(const struct Stack * const stackP) {
     return stackP->sp == stackP->stack;
 }
 
 
 
-static int
-popStack(struct stack * const stackP) {
+static unsigned char
+popStack(struct Stack * const stackP) {
 
     if (stackP->sp <= stackP->stack)
         pm_error("stack underflow");
@@ -728,7 +811,7 @@ popStack(struct stack * const stackP) {
 
 
 static void
-termStack(struct stack * const stackP) {
+termStack(struct Stack * const stackP) {
     free(stackP->stack);
     stackP->stack = NULL;
 }
@@ -774,98 +857,164 @@ termStack(struct stack * const stackP) {
 
 -----------------------------------------------------------------------------*/
 
+static int const maxLzwCodeCt = (1<<MAX_LZW_BITS);
 
-struct decompressor {
-    struct stack stack;
-    int      fresh;
+struct Decompressor {
+    struct Stack stack;
+    bool fresh;
         /* The stream is right after a clear code or at the very beginning */
-    int      codeSize;
+    unsigned int codeSize;
         /* The current code size -- each LZW code in this part of the image
            is this many bits.  Ergo, we read this many bits at a time from
            the stream.
         */
-    int      maxnum_code;
+    unsigned int maxCodeCt;
         /* The maximum number of LZW codes that can be represented with the 
            current code size.  (1 << codeSize)
         */
-    int      next_tableSlot;
+    unsigned int nextTableSlot;
         /* Index in the code translation table of the next free entry */
     unsigned int firstcode;
         /* This is always a true data element code */
-    int      prevcode;
+    unsigned int prevcode;
         /* The code just before, in the image, the one we're processing now */
-    int      table[2][(1 << MAX_LZW_BITS)];
 
     /* The following are constant for the life of the decompressor */
     FILE * ifP;
-    int init_codeSize;
-    int max_dataVal;
-    int clear_code;
-    int end_code; 
+    unsigned int initCodeSize;
+    unsigned int cmapSize;
+    unsigned int maxDataVal;
+    unsigned int clearCode;
+    unsigned int endCode;
+    bool haveTransColor;
+    unsigned int transparentIndex;
+        /* meaningful only when 'haveTransColor' is true */
+    bool tolerateBadInput; 
+        /* We are to tolerate bad input data as best we can, rather than
+           just declaring an error and bailing out.
+        */
+    unsigned int table[(1 << MAX_LZW_BITS)][2];   /* LZW code table */  
 };
 
 
 
 static void
-resetDecompressor(struct decompressor * const decompP) {
+resetDecompressor(struct Decompressor * const decompP) {
+
+    decompP->codeSize      = decompP->initCodeSize+1;
+    decompP->maxCodeCt     = 1 << decompP->codeSize;
+    decompP->nextTableSlot = decompP->maxDataVal + 3;
+    decompP->fresh         = TRUE;
+}
 
-    decompP->codeSize = decompP->init_codeSize+1;
-    decompP->maxnum_code = 1 << decompP->codeSize;
-    decompP->next_tableSlot = decompP->max_dataVal + 3;
-    decompP->fresh = 1;
+
+
+static void
+validateTransparentIndex(unsigned int const transparentIndex,
+                         bool         const tolerateBadInput,
+                         unsigned int const cmapSize,
+                         unsigned int const maxDataVal) {
+
+    if (transparentIndex >= cmapSize) {
+        if (tolerateBadInput) {
+            if (transparentIndex > maxDataVal)
+                pm_error("Invalid transparent index value: %d",
+                         transparentIndex);
+        } else {
+            pm_error("Invalid transparent index value %d in image with "
+                     "only %u colors.  %s",
+                     transparentIndex, cmapSize,
+                     transparentIndex <= maxDataVal ?
+                     "" :
+                     "Use the -repair option to try to render the "
+                     "image overriding this error.");
+        }
+    }
 }
 
 
 
 static void
-lzwInit(struct decompressor * const decompP, 
+lzwInit(struct Decompressor * const decompP, 
         FILE *                const ifP,
-        int                   const init_codeSize) {
+        int                   const initCodeSize,
+        unsigned int          const cmapSize,
+        bool                  const haveTransColor,
+        unsigned int          const transparentIndex,
+        bool                  const tolerateBadInput) {
 
+    unsigned int const maxDataVal = (1 << initCodeSize) - 1;
+    
     if (verbose)
         pm_message("Image says the initial compression code size is "
                    "%d bits", 
-                   init_codeSize);
+                   initCodeSize);
     
-    decompP->ifP = ifP;
-    decompP->init_codeSize = init_codeSize;
-
-    assert(decompP->init_codeSize < sizeof(decompP->max_dataVal) * 8);
-
-    decompP->max_dataVal = (1 << init_codeSize) - 1;
-    decompP->clear_code = decompP->max_dataVal + 1;
-    decompP->end_code = decompP->max_dataVal + 2;
+    decompP->ifP              = ifP;
+    decompP->initCodeSize     = initCodeSize;
+    decompP->cmapSize         = cmapSize;
+    decompP->tolerateBadInput = tolerateBadInput;
+    decompP->maxDataVal       = maxDataVal;
+    decompP->clearCode        = maxDataVal + 1;
+    decompP->endCode          = maxDataVal + 2;
 
     if (verbose)
-        pm_message("Initial code size is %u bits; clear code = 0x%x, "
-                   "end code = 0x%x",
-                   decompP->init_codeSize, 
-                   decompP->clear_code, decompP->end_code);
+        pm_message("Initial code size is %u bits; clear code = 0x%03x, "
+                   "end code = 0x%03x",
+                   decompP->initCodeSize, 
+                   decompP->clearCode, decompP->endCode);
     
     /* The entries in the translation table for true data codes are
-       constant throughout the stream.  We set them now and they never
-       change.
+       constant throughout the image.  For PBM output we make an
+       adjustment later.  Once set entries never change.
     */
     {
         unsigned int i;
-        for (i = 0; i <= decompP->max_dataVal; ++i) {
-            decompP->table[0][i] = 0;
-            decompP->table[1][i] = i;
+        for (i = 0; i <= maxDataVal; ++i) {
+            decompP->table[i][0] = 0;
+            decompP->table[i][1] = i < cmapSize ? i : 0;
         }
     }
+    decompP->haveTransColor   = haveTransColor;
+    decompP->transparentIndex = transparentIndex;
+
+    if (haveTransColor)
+        validateTransparentIndex(transparentIndex, tolerateBadInput,
+                                 cmapSize, maxDataVal);
+
     resetDecompressor(decompP);
 
     getCode_init(&getCodeState);
     
     decompP->fresh = TRUE;
     
-    initStack(&decompP->stack, maxnum_lzwCode * 2);
+    initStack(&decompP->stack, maxLzwCodeCt);
+
+    assert(decompP->initCodeSize < sizeof(decompP->maxDataVal) * 8);
 }
 
 
 
 static void
-lzwTerm(struct decompressor * const decompP) {
+lzwAdjustForPBM(struct Decompressor * const decompP,
+                GifColorMap           const cmap) {
+/*----------------------------------------------------------------------------
+  In the PBM case we use the table index value directly instead of looking up
+  the color map for each pixel.
+
+  Note that cmap.size is not always 2.
+
+  Similar logic should work for PGM.
+----------------------------------------------------------------------------*/
+    unsigned int i;
+    for (i = 0; i < cmap.size; ++i)
+        decompP->table[i][1] = cmap.map[i][0] == 0 ? PBM_BLACK : PBM_WHITE;
+}
+
+
+
+static void
+lzwTerm(struct Decompressor * const decompP) {
 
     termStack(&decompP->stack);
 }
@@ -873,8 +1022,32 @@ lzwTerm(struct decompressor * const decompP) {
 
 
 static void
-expandCodeOntoStack(struct decompressor * const decompP,
-                    int                   const incode,
+pushWholeStringOnStack(struct Decompressor * const decompP,
+                       unsigned int          const code0) {
+/*----------------------------------------------------------------------------
+  Get the whole string that compression code 'code0' 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 code;
+    unsigned int stringCount;
+
+    for (stringCount = 0, code = code0;
+         code > decompP->maxDataVal;
+         ++stringCount, code = decompP->table[code][0]
+        ) {
+
+        pushStack(&decompP->stack, decompP->table[code][1]);
+    }
+    decompP->firstcode = decompP->table[code][1];
+    pushStack(&decompP->stack, decompP->firstcode);
+}
+
+
+
+static void
+expandCodeOntoStack(struct Decompressor * const decompP,
+                    unsigned int          const incode,
                     const char **         const errorP) {
 /*----------------------------------------------------------------------------
    'incode' is an LZW string code.  It represents a string of true data
@@ -889,119 +1062,125 @@ expandCodeOntoStack(struct decompressor * const decompP,
    from which it was built is invalid), fail (return text explanation
    as *errorP).
 -----------------------------------------------------------------------------*/
-    int code;
-    const char * error;
-
-    error = NULL; /* Initial value */
-
-    if (incode < decompP->next_tableSlot) 
-        code = incode;
-    else {
-        /* It's a code that isn't in our translation table yet
-        
-           The only thing it could legally be is one higher than the
-           highest one we've seen so far.
-        */
-        if (code > decompP->next_tableSlot) {
-            /* We just abort because we added this to stable code to fix
-               a bug and we don't want to disturb stable code more than we
-               have to.
-            */
-            pm_error("Error in GIF image: LZW string code %u "
-                     "is neither a previously defined one nor the "
-                     "next in sequence to define (%u)",
-                     code, decompP->next_tableSlot);
-        }
-        pushStack(&decompP->stack, decompP->firstcode);
-        code = decompP->prevcode;
+    unsigned int code;
+
+    *errorP = NULL; /* Initial value */
+
+    if (incode <= decompP->maxDataVal) {
+        if (incode < decompP->cmapSize)
+            code = incode;      /* Direct index */
+        else if (decompP->tolerateBadInput &&
+                 decompP->haveTransColor &&
+                 decompP->table[incode][1] == decompP->transparentIndex)
+            /* transparent code outside cmap   exceptional case */
+            code = incode;
+        else
+            pm_asprintf(errorP, "Error in GIF image: invalid color code %u. "
+                        "Valid color values are 0 - %u",
+                        incode, decompP->cmapSize - 1);
     }
-
-    {
-        /* Get the whole string that this compression code
-           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.
+    else if (incode < decompP->nextTableSlot)  
+        /* LZW string, defined */
+        code = incode;
+    else if  (incode == decompP->nextTableSlot && !decompP->fresh) {
+        /* It's a code that isn't in our translation table yet.
+           This does not happen with the decoder in a fresh state.
         */
+        if (wantLzwCodes && verbose)
+            pm_message ("LZW code valid, but not in decoder table");
 
-        unsigned int stringCount;
-        stringCount = 0;
-
-        while (code > decompP->max_dataVal && !error) {
-            if (stringCount > maxnum_lzwCode) {
-                asprintfN(&error,
-                          "Error in GIF image: contains LZW string loop");
-            } else {
-                ++stringCount;
-                pushStack(&decompP->stack, decompP->table[1][code]);
-                code = decompP->table[0][code];
-            }
-        }
-        decompP->firstcode = decompP->table[1][code];
         pushStack(&decompP->stack, decompP->firstcode);
-    }
+        code = decompP->prevcode;
+    } else
+        pm_asprintf(errorP, "Error in GIF image: invalid LZW code");
 
-    if (decompP->next_tableSlot < maxnum_lzwCode) {
-        decompP->table[0][decompP->next_tableSlot] = decompP->prevcode;
-        decompP->table[1][decompP->next_tableSlot] = decompP->firstcode;
-        ++decompP->next_tableSlot;
-        if (decompP->next_tableSlot >= decompP->maxnum_code) {
-            /* We've used up all the codes of the current code size.
-               Future codes in the stream will have codes one bit longer.
-               But there's an exception if we're already at the LZW
-               maximum, in which case the codes will simply continue
-               the same size.
-            */
-            if (decompP->codeSize < MAX_LZW_BITS) {
-                ++decompP->codeSize;
-                decompP->maxnum_code = 1 << decompP->codeSize;
+    if (!*errorP) {
+        pushWholeStringOnStack(decompP, code);
+
+        if (decompP->nextTableSlot < maxLzwCodeCt) {
+            decompP->table[decompP->nextTableSlot][0] = decompP->prevcode;
+            decompP->table[decompP->nextTableSlot][1] = decompP->firstcode;
+            ++decompP->nextTableSlot;
+            if (decompP->nextTableSlot >= decompP->maxCodeCt) {
+                /* We've used up all the codes of the current code size.
+                   Future codes in the stream will have codes one bit longer.
+                   But there's an exception if we're already at the LZW
+                   maximum, in which case the codes will simply continue
+                   the same size.
+                */
+                if (decompP->codeSize < MAX_LZW_BITS) {
+                    ++decompP->codeSize;
+                    decompP->maxCodeCt = 1 << decompP->codeSize;
+                }
             }
         }
+        decompP->prevcode = incode;
     }
-
-    *errorP = error;
-
-    decompP->prevcode = incode;
 }
 
 
 
 static void
-lzwReadByteFresh(struct getCodeState * const getCodeStateP,
-                 struct decompressor * const decompP,
+lzwReadByteFresh(struct GetCodeState * const getCodeStateP,
+                 struct Decompressor * const decompP,
                  bool *                const endOfImageP,
-                 unsigned int *        const dataReadP,
+                 unsigned char *       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.
-    */
+/*----------------------------------------------------------------------------
+  Read off all initial clear codes, read the first non-clear code, and return
+  it as *dataReadP.
+
+  Iff we hit end of image in so doing, return *endOfImageP true and nothing as
+  *dataReadP.  One way we hit end of image is for that first non-clear code to
+  be an end code.
+
+  Assume the decompressor is fresh, i.e. there are no strings in the table
+  yet, so the next code must be a direct true data code.
+-----------------------------------------------------------------------------*/
+    unsigned int code;
     bool eof;
+
+    assert(decompP->fresh);  /* Entry requirement */
+
+    decompP->fresh = FALSE;
+
     do {
         getCode_get(getCodeStateP, decompP->ifP, decompP->codeSize,
-                    &eof, &decompP->firstcode, errorP);
-        decompP->prevcode = decompP->firstcode;
-    } while (decompP->firstcode == decompP->clear_code && !*errorP && !eof);
+                    &eof, &code, errorP);
+    } while (!*errorP && !eof && code == decompP->clearCode);
 
     if (!*errorP) {
         if (eof)
             *endOfImageP = TRUE;
-        else if (decompP->firstcode == decompP->end_code) {
+        else if (code == decompP->endCode) {
             if (!zeroDataBlock)
                 readThroughEod(decompP->ifP);
             *endOfImageP = TRUE;
-        } else {
+        } else if (code >= decompP->cmapSize) { 
+            pm_asprintf(errorP, "Error in GIF image: invalid color code %u. "
+                        "Valid color values are: 0 - %u",
+                        code, decompP->cmapSize-1);
+            /* Set these values in order to avoid errors in the -repair
+               case
+            */
+            decompP->prevcode = decompP->firstcode = 0;
+
             *endOfImageP = FALSE;
+        } else {    /* valid code */
+            decompP->prevcode  = code;
+            decompP->firstcode = decompP->table[code][1];
             *dataReadP = decompP->firstcode;
+            *endOfImageP = FALSE;
         }
     }
 }
 
 
 
+
 static void
-lzwReadByte(struct decompressor * const decompP,
-            unsigned int *        const dataReadP,
+lzwReadByte(struct Decompressor * const decompP,
+            unsigned char *       const dataReadP,
             bool *                const endOfImageP,
             const char **         const errorP) {
 /*----------------------------------------------------------------------------
@@ -1025,8 +1204,6 @@ lzwReadByte(struct decompressor * const decompP,
         *endOfImageP = FALSE;
         *dataReadP = popStack(&decompP->stack);
     } else if (decompP->fresh) {
-        decompP->fresh = FALSE;
-
         lzwReadByteFresh(&getCodeState, decompP, endOfImageP, dataReadP,
                          errorP);
     } else {
@@ -1036,14 +1213,15 @@ lzwReadByte(struct decompressor * const decompP,
                     &eof, &code, errorP);
         if (!*errorP) {
             if (eof)
-                asprintfN(errorP,
-                          "Premature end of file; no proper GIF closing");
+                pm_asprintf(errorP,
+                            "Premature end of file; no proper GIF closing");
             else {
-                if (code == decompP->clear_code) {
+                if (code == decompP->clearCode) {
                     resetDecompressor(decompP);
-                    lzwReadByte(decompP, dataReadP, endOfImageP, errorP);
+                    lzwReadByteFresh(&getCodeState, decompP, endOfImageP,
+                    dataReadP, errorP);
                 } else {
-                    if (code == decompP->end_code) {
+                    if (code == decompP->endCode) {
                         if (!zeroDataBlock)
                             readThroughEod(decompP->ifP);
                         *endOfImageP = TRUE;
@@ -1065,8 +1243,8 @@ lzwReadByte(struct decompressor * const decompP,
 enum pass {MULT8PLUS0, MULT8PLUS4, MULT4PLUS2, MULT2PLUS1};
 
 static void
-bumpRowInterlace(unsigned int * const rowP,
-                 unsigned int   const rows,
+bumpRowInterlace(unsigned int   const rows,
+                 unsigned int * const rowP,
                  enum pass *    const passP) {
 /*----------------------------------------------------------------------------
    Move *pixelCursorP to the next row in the interlace pattern.
@@ -1119,47 +1297,57 @@ bumpRowInterlace(unsigned int * const rowP,
 }
 
 
+static void
+renderRow(unsigned char *    const cmapIndexRow,
+          unsigned int       const cols,
+          GifColorMap        const cmap, 
+          bool               const haveTransColor,
+          unsigned int       const transparentIndex,
+          FILE *             const imageOutfile,
+          int                const format,
+          xel *              const xelrow,
+          FILE *             const alphaFileP,
+          bit *              const alphabits) {
+/*----------------------------------------------------------------------------
+  Convert one row of cmap indexes to PPM/PGM/PBM output.
+
+  Render the alpha row to *alphaFileP iff 'alphabits' is non-NULL.  If
+  'haveTransColor' is false, render all white (i.e. the row is
+  opaque).  'alphabits' is otherwise just a one-row buffer for us to use
+  in rendering the alpha row.
+  
+  imageOutfile is NULL if user wants only the alpha file.
+----------------------------------------------------------------------------*/
+    if (alphabits) {
+        unsigned int col;
+
+        for (col=0; col < cols; ++col) {
+            alphabits[col] =
+                (haveTransColor && cmapIndexRow[col] == transparentIndex) ?
+                PBM_BLACK : PBM_WHITE;
+        }
+        pbm_writepbmrow(alphaFileP, alphabits, cols, false);
+    }
 
-struct pnmBuffer {
-    xel ** xels;
-    unsigned int col;
-    unsigned int row;
-};
+    if (imageOutfile) {
+        if (useFastPbmRender && format == PBM_FORMAT && !haveTransColor) {
 
-static void
-addPixelToRaster(unsigned int       const cmapIndex,
-                 struct pnmBuffer * const pnmBufferP,
-                 unsigned int       const cols,
-                 unsigned int       const rows,
-                 gifColorMap              cmap, 
-                 unsigned int       const cmapSize,
-                 bool               const interlace,
-                 int                const transparentIndex,
-                 bit **             const alphabits,
-                 enum pass *        const passP) {
-
-    if (cmapIndex >= cmapSize)
-        pm_error("Invalid color index %u in an image that has only "
-                 "%u colors in the color map.", cmapIndex, cmapSize);
-    
-    assert(cmapIndex < MAXCOLORMAPSIZE);
-    
-    PPM_ASSIGN(pnmBufferP->xels[pnmBufferP->row][pnmBufferP->col], 
-               cmap[CM_RED][cmapIndex],
-               cmap[CM_GRN][cmapIndex],
-               cmap[CM_BLU][cmapIndex]);
-    
-    if (alphabits) 
-        alphabits[pnmBufferP->row][pnmBufferP->col] =
-            (cmapIndex == transparentIndex) ? PBM_BLACK : PBM_WHITE;
-    
-    ++pnmBufferP->col;
-    if (pnmBufferP->col == cols) {
-        pnmBufferP->col = 0;
-        if (interlace)
-            bumpRowInterlace(&pnmBufferP->row, rows, passP);
-        else
-            ++pnmBufferP->row;
+            bit * const bitrow = cmapIndexRow; 
+
+            pbm_writepbmrow(imageOutfile, bitrow, cols, false);
+        } else {
+            /* PPM, PGM and PBM with transparent */
+            unsigned int col;
+            for (col = 0; col < cols; ++col) {
+                unsigned char const cmapIndex = cmapIndexRow[col];
+                const unsigned char * const color = cmap.map[cmapIndex];
+                assert(cmapIndex < cmap.size);
+                PPM_ASSIGN(xelrow[col],
+                           color[CM_RED], color[CM_GRN],color[CM_BLU]);
+            }
+            pnm_writepnmrow(imageOutfile, xelrow, cols,
+                            GIFMAXVAL, format, false);
+        }
     }
 }
 
@@ -1170,19 +1358,18 @@ 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);
+        *errorP = pm_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);
+            pm_asprintf(errorP,
+                        "Error in GIF image: Not enough raster data to fill "
+                        "%u x %u dimensions.  "
+                        "The image has proper ending sequence, so "
+                        "this is not just a truncated file.",
+                        cols, rows);
         else
             *errorP = NULL;
     }
@@ -1190,93 +1377,212 @@ verifyPixelRead(bool          const endOfImage,
 
 
 
+static int
+pnmFormat(bool const hasGray,
+          bool const hasColor) {
+/*----------------------------------------------------------------------------
+  The proper PNM format (PBM, PGM, or PPM) for an image described
+  by 'hasGray' and 'hasColor'.
+-----------------------------------------------------------------------------*/
+    int format;
+    const char * formatName;
+           
+    if (hasColor) {
+        format = PPM_FORMAT;
+        formatName = "PPM";
+    } else if (hasGray) {
+        format = PGM_FORMAT;
+        formatName = "PGM";
+    } else {
+        format = PBM_FORMAT;
+        formatName = "PBM";
+    }
+    if (verbose) 
+        pm_message("writing a %s file", formatName);
+ 
+    return format;
+}
+
+
+
 static void
-readRaster(struct decompressor * const decompP,
-           xel **                const xels, 
+makePnmRow(struct Decompressor * const decompP,
            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;
+           bool                  const fillWithZero,
+           unsigned char *       const cmapIndexRow,
+           const char **         const errorP) {
 
-    pass = MULT8PLUS0;
-    pnmBuffer.xels = xels;
-    pnmBuffer.col  = 0;
-    pnmBuffer.row  = 0;
-    fillingMissingPixels = false;  /* initial value */
+    bool fillingWithZero;
+    unsigned int col;
 
-    while (pnmBuffer.row < rows) {
-        unsigned int colorIndex;
+    *errorP = NULL;  /* initial value */
 
-        if (fillingMissingPixels)
-            colorIndex = 0;
-        else {
-            const char * error;
+    for (col = 0, fillingWithZero = fillWithZero;
+         col < cols;
+         ++col) {
 
-            const char * readError;
-            unsigned int readColorIndex;
-            bool endOfImage;
+        unsigned char colorIndex;
+
+        if (fillingWithZero)
+            colorIndex = 0;
+        else { 
+            const char *  readError;
+            unsigned char readColorIndex;
+            bool          endOfImage;
 
             lzwReadByte(decompP, &readColorIndex, &endOfImage, &readError);
 
-            verifyPixelRead(endOfImage, readError, cols, rows, pnmBuffer.row,
-                            &error);
+            verifyPixelRead(endOfImage, readError, cols, rows, errorP);
 
             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);
-
+                pm_strfree(readError);
+
+            if (*errorP) {
+                /* Caller may want to try to ignore this error, so we
+                   fill out the row with zeroes.  Note that we can't possibly
+                   have another error while doing that.
+                */
+                fillingWithZero = true;
                 colorIndex = 0;
             } else
                 colorIndex = readColorIndex;
         }
-        addPixelToRaster(colorIndex, &pnmBuffer, cols, rows, cmap, cmapSize,
-                         interlace, transparentIndex, alphabits, &pass);
+        cmapIndexRow[col] = colorIndex;
     }
 }
 
 
 
 static void
-skipExtraneousData(struct decompressor * const decompP) {
+convertRaster(struct Decompressor * const decompP,
+              unsigned int          const cols,
+              unsigned int          const rows,
+              GifColorMap           const cmap, 
+              bool                  const interlace,
+              FILE *                const imageOutFileP,
+              FILE *                const alphaFileP,
+              bool                  const hasGray,
+              bool                  const hasColor) {
+/*----------------------------------------------------------------------------
+   Read the raster from the GIF decompressor *decompP, and write it as a
+   complete PNM stream (starting with the header) on *imageOutFileP and
+   *alphaFileP.
+
+   Assume that raster is 'cols' x 'rows', refers to colormap 'cmap', and is
+   interlaced iff 'interlace' is true.
+
+   Assume the image has gray levels and/or color per 'hasGray' and 'hasColor'.
+-----------------------------------------------------------------------------*/
+    int const format = pnmFormat(hasGray, hasColor);
+
+    enum pass pass;
+    bool fillingMissingPixels;
+    unsigned int row;
+    unsigned char ** cmapIndexArray;
+    bit * alphabits;
+    xel * xelrow;
+    unsigned int outrow;
+        /* Non-interlace: outrow is always 0: cmapIndexRow keeps pointing
+           to the single row in array.
+
+           Interlace: outrow is modified with each call to bumpRowInterface().
+        */
+
+    MALLOCARRAY2(cmapIndexArray, interlace ? rows : 1 , cols);
+
+    if (imageOutFileP)
+        pnm_writepnminit(imageOutFileP, cols, rows, GIFMAXVAL, format, FALSE);
+    if (alphaFileP)
+        pbm_writepbminit(alphaFileP, cols, rows, FALSE);
+
+    xelrow = pnm_allocrow(cols);  
+    if (!xelrow)
+        pm_error("couldn't alloc space for image" );
 
-    unsigned int byteRead;
+    if (alphaFileP) {
+        alphabits = pbm_allocrow(cols);
+        if (!alphabits)
+            pm_error("couldn't alloc space for alpha image" );
+    } else
+        alphabits = NULL;
+
+    fillingMissingPixels = false;  /* initial value */
+    pass = MULT8PLUS0;
+    outrow = 0;
+
+    for (row = 0; row < rows; ++row) {
+        const char * problem;
+        makePnmRow(decompP, cols, rows, fillingMissingPixels,
+                   cmapIndexArray[outrow], &problem);
+
+        if (problem) {
+            /* makePnmRow() recovered from the problem and produced an output
+               row, stuffed with zeroes as necessary
+            */
+            if (decompP->tolerateBadInput) {
+                pm_message("WARNING: %s.  "
+                           "Filling bottom %u rows with arbitrary color",
+                           problem, rows - row);
+                fillingMissingPixels = true;
+            } else
+                pm_error("Unable to read input image.  %s "
+                         "(Output row: %u).  "
+                         "Use the -repair option to try to salvage "
+                         "some of the image",
+                         problem, interlace ? outrow : row);
+        }
+
+        if (interlace)
+            bumpRowInterlace(rows, &outrow, &pass);
+        else
+            renderRow(cmapIndexArray[outrow], cols, cmap,
+                      decompP->haveTransColor, decompP->transparentIndex,
+                      imageOutFileP, format, xelrow, alphaFileP, alphabits);
+    }
+    /* All rows decompressed (and rendered and output if non-interlaced) */  
+    if (interlace) {
+        unsigned int row;
+        for (row = 0; row < rows; ++row) 
+            renderRow(cmapIndexArray[row], cols, cmap,
+                      decompP->haveTransColor, decompP->transparentIndex,
+                      imageOutFileP, format, xelrow, alphaFileP, alphabits);
+    }
+
+    pnm_freerow(xelrow);
+    if (alphabits)
+        pbm_freerow(alphabits);
+    pm_freearray2((void **)cmapIndexArray);
+}
+
+
+
+static void
+skipExtraneousData(struct Decompressor * const decompP) {
+
+    unsigned char byteRead;
     bool endOfImage;
     const char * error;
 
+    endOfImage = FALSE;  /* initial value */
+
     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");
+        pm_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);
+            while (!endOfImage && !error)
+                lzwReadByte(decompP, &byteRead, &endOfImage, &error);
 
-        if (error) {
-            pm_message("Error encountered skipping to end of image: %s",
-                       error);
-            strfree(error);
+            if (error) {
+                pm_message("Error encountered skipping to end of image: %s",
+                           error);
+                pm_strfree(error);
+            }
         }
     }
 }
@@ -1284,36 +1590,89 @@ skipExtraneousData(struct decompressor * const decompP) {
 
 
 static void
+issueTransparencyMessage(bool         const haveTransColor,
+                         unsigned int const transparentIndex, 
+                         GifColorMap  const cmap) {
+/*----------------------------------------------------------------------------
+   If user wants verbose output, tell him whether there is a transparent
+   background color ('haveTransColor') and if so what it is
+   ('transparentIndex').
+   
+   Some GIFs put transparentIndex outside the color map.  Allow this only
+   with "-repair", checked in lzwInit().  Here we issue a warning and report
+   the substitute color.
+-----------------------------------------------------------------------------*/
+    if (verbose) {
+        if (haveTransColor) {
+            if (transparentIndex >= cmap.size) {
+                const unsigned char * const color = cmap.map[0];
+                pm_message("WARNING: Transparent index %u "
+                           "is outside color map. "
+                           "substitute background color: rgb:%02x/%02x/%02x ",
+                           transparentIndex,
+                           color[CM_RED],
+                           color[CM_GRN],
+                           color[CM_BLU]
+                    );
+            } else {
+                const unsigned char * const color = cmap.map[transparentIndex];
+                pm_message("transparent background color: rgb:%02x/%02x/%02x "
+                           "Index %u",
+                           color[CM_RED],
+                           color[CM_GRN],
+                           color[CM_BLU],
+                           transparentIndex
+                    );
+            }
+        } else
+            pm_message("no transparency");
+    }
+}
+
+
+
+static void
 readImageData(FILE *       const ifP, 
-              xel **       const xels, 
               unsigned int const cols,
               unsigned int const rows,
-              gifColorMap        cmap, 
-              unsigned int const cmapSize,
+              GifColorMap  const cmap, 
               bool         const interlace,
-              int          const transparentIndex,
-              bit **       const alphabits,
+              bool         const haveTransColor,
+              unsigned int const transparentIndex,
+              FILE *       const imageOutFileP,
+              FILE *       const alphaFileP,
+              bool         const hasGray,
+              bool         const hasColor,
               bool         const tolerateBadInput) {
 
     unsigned char lzwMinCodeSize;      
-    struct decompressor decomp;
-    bool gotMinCodeSize;
+    struct Decompressor decomp;
+    const char * error;
 
-    gotMinCodeSize =  ReadOK(ifP, &lzwMinCodeSize, 1);
-    if (!gotMinCodeSize)
-        pm_error("GIF stream ends (or read error) "
+    readFile(ifP, &lzwMinCodeSize, sizeof(lzwMinCodeSize), &error);
+    if (error)
+        pm_error("Can't read GIF stream "
                  "right after an image separator; no "
-                 "image data follows.");
+                 "image data follows.  %s", error);
 
     if (lzwMinCodeSize > MAX_LZW_BITS)
         pm_error("Invalid minimum code size value in image data: %u.  "
                  "Maximum allowable code size in GIF is %u", 
                  lzwMinCodeSize, MAX_LZW_BITS);
 
-    lzwInit(&decomp, ifP, lzwMinCodeSize);
+    lzwInit(&decomp, ifP, lzwMinCodeSize, cmap.size,
+            haveTransColor, transparentIndex, tolerateBadInput);
+
+    issueTransparencyMessage(haveTransColor, transparentIndex, cmap);
 
-    readRaster(&decomp, xels, cols, rows, cmap, cmapSize, interlace,
-               transparentIndex, alphabits, tolerateBadInput);
+    if (useFastPbmRender && !hasGray && ! hasColor && !haveTransColor) {
+        if (verbose)
+            pm_message("Using fast PBM rendering");
+        lzwAdjustForPBM(&decomp, cmap);
+    }
+    convertRaster(&decomp, cols, rows, cmap, interlace,
+                  imageOutFileP, alphaFileP,
+                  hasGray, hasColor);
 
     skipExtraneousData(&decomp);
 
@@ -1323,80 +1682,47 @@ readImageData(FILE *       const ifP,
 
 
 static void
-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 *outfileP with
-   dimensions 'cols' x 'rows' and raster 'xels'.
-   
-   Make it PBM, PGM, or PBM according to 'hasGray' and 'hasColor'.
------------------------------------------------------------------------------*/
-    int format;
-    const char * formatName;
-           
-    if (hasColor) {
-        format = PPM_FORMAT;
-        formatName = "PPM";
-    } else if (hasGray) {
-        format = PGM_FORMAT;
-        formatName = "PGM";
-    } else {
-        format = PBM_FORMAT;
-        formatName = "PBM";
-    }
-    if (verbose) 
-        pm_message("writing a %s file", formatName);
-    
-    if (outfileP) 
-        pnm_writepnm(outfileP, xels, cols, rows,
-                     (xelval) GIFMAXVAL, format, FALSE);
-}
+warnUserNotSquare(unsigned int const aspectRatio) {
 
+    const char * baseMsg =
+        "warning - input pixels are not square, "
+        "but we are rendering them as square pixels "
+        "in the output";
 
+    if (pm_have_float_format()) {
+        float const r = ((float)aspectRatio + 15.0 ) / 64.0;
 
-static void
-transparencyMessage(int const transparentIndex, 
-                    gifColorMap cmap) {
-/*----------------------------------------------------------------------------
-   If user wants verbose output, tell him that the color with index
-   'transparentIndex' is supposed to be a transparent background color.
-   
-   If transparentIndex == -1, tell him there is no transparent background
-   color.
------------------------------------------------------------------------------*/
-    if (verbose) {
-        if (transparentIndex == -1)
-            pm_message("no transparency");
-        else
-            pm_message("transparent background color: rgb:%02x/%02x/%02x "
-                       "Index %d",
-                       cmap[CM_RED][transparentIndex],
-                       cmap[CM_GRN][transparentIndex],
-                       cmap[CM_BLU][transparentIndex],
-                       transparentIndex
-                );
-    }
+        pm_message("%s.  To fix the output, run it through "
+                   "'pamscale -%cscale %g'",
+                   baseMsg,
+                   r < 1.0 ? 'x' : 'y',
+                   r < 1.0 ? 1.0 / r : r );
+    } else
+        pm_message("%s", baseMsg);
 }
 
+
+
 static void
-readGifHeader(FILE * const gifFile, struct gifScreen * const gifScreenP) {
+readGifHeader(FILE *             const gifFileP,
+              struct GifScreen * const gifScreenP) {
 /*----------------------------------------------------------------------------
-   Read the GIF stream header off the file gifFile, which is present
+   Read the GIF stream header off the file *gifFileP, which is present
    positioned to the beginning of a GIF stream.  Return the info from it
    as *gifScreenP.
 -----------------------------------------------------------------------------*/
-    unsigned char   buf[16];
-    char     version[4];
+#define GLOBALCOLORMAP  0x80
 
+    unsigned char buf[16];
+    char version[4];
+    unsigned int cmapSize;
+    const char * error;
 
-    if (! ReadOK(gifFile,buf,6))
-        pm_error("error reading magic number" );
+    readFile(gifFileP, buf, 6, &error);
+    if (error)
+        pm_error("Error reading magic number.  %s", error);
     
-    if (strncmp((char *)buf,"GIF",3) != 0)
+    if (!strneq((char *)buf, "GIF", 3))
         pm_error("File does not contain a GIF stream.  It does not start "
                  "with 'GIF'.");
     
@@ -1407,56 +1733,52 @@ readGifHeader(FILE * const gifFile, struct gifScreen * const gifScreenP) {
         pm_message("GIF format version is '%s'", version);
     
     if ((!streq(version, "87a")) && (!streq(version, "89a")))
-        pm_error("bad version number, not '87a' or '89a'" );
+        pm_error("Bad version number, not '87a' or '89a'" );
     
-    if (! ReadOK(gifFile,buf,7))
-        pm_error("failed to read screen descriptor" );
+    readFile(gifFileP, buf, 7, &error);
+    if (error)
+        pm_error("Failed to read screen descriptor.  %s", error);
     
-    gifScreenP->Width           = LM_to_uint(buf[0],buf[1]);
-    gifScreenP->Height          = LM_to_uint(buf[2],buf[3]);
-    gifScreenP->ColorMapSize    = 1 << ((buf[4] & 0x07) + 1);
-    gifScreenP->ColorResolution = (buf[4] & 0x70 >> 3) + 1;
-    gifScreenP->Background      = buf[5];
-    gifScreenP->AspectRatio     = buf[6];
+    gifScreenP->width           = LM_to_uint(buf[0],buf[1]);
+    gifScreenP->height          = LM_to_uint(buf[2],buf[3]);
+    cmapSize                    = 1 << ((buf[4] & 0x07) + 1);
+    gifScreenP->colorResolution = (buf[4] & 0x70 >> 3) + 1;
+    gifScreenP->background      = buf[5];
+    gifScreenP->aspectRatio     = buf[6];
 
     if (verbose) {
-        pm_message("GIF Width = %d GIF Height = %d "
-                   "Pixel aspect ratio = %d (%f:1)",
-                   gifScreenP->Width, gifScreenP->Height, 
-                   gifScreenP->AspectRatio, 
-                   gifScreenP->AspectRatio == 0 ? 
-                   1 : (gifScreenP->AspectRatio + 15) / 64.0);
-        pm_message("Colors = %d   Color Resolution = %d",
-                   gifScreenP->ColorMapSize, gifScreenP->ColorResolution);
+        pm_message("GIF Width = %u GIF Height = %u "
+                   "Pixel aspect ratio = %u (%f:1)",
+                   gifScreenP->width, gifScreenP->height, 
+                   gifScreenP->aspectRatio, 
+                   gifScreenP->aspectRatio == 0 ? 
+                   1 : (gifScreenP->aspectRatio + 15) / 64.0);
+        pm_message("Global color count = %u   Color Resolution = %u",
+                   cmapSize, gifScreenP->colorResolution);
     }           
-    if (BitSet(buf[4], LOCALCOLORMAP)) {    /* Global Colormap */
-        readColorMap(gifFile, gifScreenP->ColorMapSize, gifScreenP->ColorMap,
+    if (buf[4] & GLOBALCOLORMAP) {
+        gifScreenP->hasGlobalColorMap = true;
+        readColorMap(gifFileP, cmapSize, &gifScreenP->colorMap,
                      &gifScreenP->hasGray, &gifScreenP->hasColor);
         if (verbose) {
-            pm_message("Color map %s grays, %s colors", 
+            pm_message("Global color map %s grays, %s colors", 
                        gifScreenP->hasGray ? "contains" : "doesn't contain",
                        gifScreenP->hasColor ? "contains" : "doesn't contain");
         }
-    }
+    } else
+        gifScreenP->hasGlobalColorMap = false;
     
-    if (gifScreenP->AspectRatio != 0 && gifScreenP->AspectRatio != 49) {
-        float   r;
-        r = ( (float) gifScreenP->AspectRatio + 15.0 ) / 64.0;
-        pm_message("warning - input pixels are not square, "
-                   "but we are rendering them as square pixels "
-                   "in the output.  "
-                   "To fix the output, run it through "
-                   "'pnmscale -%cscale %g'",
-                   r < 1.0 ? 'x' : 'y',
-                   r < 1.0 ? 1.0 / r : r );
-    }
+    if (gifScreenP->aspectRatio != 0 && gifScreenP->aspectRatio != 49)
+        warnUserNotSquare(gifScreenP->aspectRatio);
+
+#undef GLOBALCOLORMAP
 }
 
 
 
 static void
 readExtensions(FILE*          const ifP, 
-               struct gif89 * const gif89P,
+               struct Gif89 * const gif89P,
                bool *         const eodP,
                const char **  const errorP) {
 /*----------------------------------------------------------------------------
@@ -1482,13 +1804,13 @@ readExtensions(FILE*          const ifP,
         unsigned char c;
         const char * error;
 
-        readFile(ifP, &c, 1, &error);
+        readFile(ifP, &c, sizeof(c), &error);
 
         if (error) {
-            asprintfN(errorP, "File read error where start of image "
-                      "descriptor or end of GIF expected.  %s",
-                      error);
-            strfree(error);
+            pm_asprintf(errorP, "File read error where start of image "
+                        "descriptor or end of GIF expected.  %s",
+                        error);
+            pm_strfree(error);
         } else {
             if (c == ';') {         /* GIF terminator */
                 eod = TRUE;
@@ -1499,17 +1821,18 @@ readExtensions(FILE*          const ifP,
                 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);
+                    pm_asprintf(errorP, "Failed to read function code "
+                                "of GIF extension (immediately after the '!' "
+                                "extension delimiter) from input.  %s", error);
+                    pm_strfree(error);
                 } else {
                     doExtension(ifP, functionCode, gif89P);
                 }
             } else if (c == ',') 
                 imageStart = TRUE;
             else 
-                pm_message("bogus character 0x%02x, ignoring", (int)c);
+                pm_message("Encountered invalid character 0x%02x while "
+                           "seeking extension block, ignoring", (int)c);
         }
     }
     *eodP = eod;
@@ -1517,20 +1840,111 @@ readExtensions(FILE*          const ifP,
 
 
 
+struct GifImageHeader {
+/*----------------------------------------------------------------------------
+   Information in the header (first 9 bytes) of a GIF image.
+-----------------------------------------------------------------------------*/
+    bool hasLocalColormap;
+        /* The image has its own color map.  Its size is 'localColorMapSize' */
+        /* (If an image does not have its own color map, the image uses the 
+           global color map for the GIF stream)
+        */
+    unsigned int localColorMapSize;
+        /* Meaningful only if 'hasLocalColormap' is true. */
+
+    /* Position of the image (max 65535) */
+    unsigned int lpos;
+    unsigned int tpos;
+
+    /* Dimensions of the image (max 65535) */
+    unsigned int cols;
+    unsigned int rows;
+
+    bool interlaced;
+};
+
+
+
 static void
-reportImageInfo(unsigned int const cols,
-                unsigned int const rows,
-                bool         const useGlobalColormap,
-                unsigned int const localColorMapSize,
-                bool         const interlaced) {
+reportImageHeader(struct GifImageHeader const imageHeader) {
 
     pm_message("reading %u by %u%s GIF image",
-               cols, rows, interlaced ? " interlaced" : "" );
+               imageHeader.cols, imageHeader.rows,
+               imageHeader.interlaced ? " interlaced" : "" );
 
-    if (useGlobalColormap)
-        pm_message("  Uses global colormap");
+    if (imageHeader.lpos > 0 || imageHeader.tpos > 0)
+        pm_message("  Image left position: %u top position: %u",
+                   imageHeader.lpos, imageHeader.tpos);
+    
+    if (imageHeader.hasLocalColormap)
+        pm_message("  Uses local colormap of %u colors",
+                   imageHeader.localColorMapSize);
     else
-        pm_message("  Uses local colormap of %u colors", localColorMapSize);
+        pm_message("  Uses global colormap");
+}
+
+
+
+static void
+readImageHeader(FILE *                  const ifP,
+                struct GifImageHeader * const imageHeaderP) {
+
+#define LOCALCOLORMAP  0x80
+#define INTERLACE      0x40
+
+    unsigned char buf[16];
+    const char * error;
+
+    readFile(ifP, buf, 9, &error);
+    if (error)
+        pm_error("couldn't read left/top/width/height.  %s", error);
+
+    imageHeaderP->hasLocalColormap  = !!(buf[8] & LOCALCOLORMAP);
+    imageHeaderP->localColorMapSize = 1u << ((buf[8] & 0x07) + 1);
+    imageHeaderP->lpos              = LM_to_uint(buf[0], buf[1]);
+    imageHeaderP->tpos              = LM_to_uint(buf[2], buf[3]);
+    imageHeaderP->cols              = LM_to_uint(buf[4], buf[5]);
+    imageHeaderP->rows              = LM_to_uint(buf[6], buf[7]);
+    imageHeaderP->interlaced        = !!(buf[8] & INTERLACE);
+
+    if (verbose)
+        reportImageHeader(*imageHeaderP);
+
+#undef INTERLACE
+#undef LOCALCOLORMAP
+}
+
+
+
+static void
+validateWithinGlobalScreen(struct GifImageHeader const imageHeader,
+                           struct GifScreen      const gifScreen) {
+
+    unsigned long int const rpos = imageHeader.lpos + imageHeader.cols;
+    unsigned long int const bpos = imageHeader.tpos + imageHeader.rows; 
+
+    if (rpos > gifScreen.width)
+        pm_error("Image right end (%lu) is outside global screen: %u x %u",
+                 rpos, gifScreen.width, gifScreen.height);
+    if (bpos > gifScreen.height)
+        pm_error("Image bottom end (%lu) is outside global screen: "
+                 "%u x %u",
+                 bpos, gifScreen.width, gifScreen.height);
+}
+
+
+
+static void
+skipImageData(FILE * const ifP) {
+    unsigned char lzwMinCodeSize;
+    const char * error;
+
+    readFile(ifP, &lzwMinCodeSize, sizeof(lzwMinCodeSize), &error);
+    if (error) {
+        pm_message("Unable to read file to skip image DataBlock.  %s", error);
+        pm_strfree(error);
+    }
+    readThroughEod(ifP);
 }
 
 
@@ -1538,88 +1952,51 @@ reportImageInfo(unsigned int const cols,
 static void
 convertImage(FILE *           const ifP, 
              bool             const skipIt, 
-             FILE *           const imageout_file, 
-             FILE *           const alphafile, 
-             struct gifScreen       gifScreen,
-             struct gif89     const gif89,
+             FILE *           const imageoutFileP, 
+             FILE *           const alphafileP, 
+             struct GifScreen const gifScreen,
+             struct Gif89     const gif89,
              bool             const tolerateBadInput) {
 /*----------------------------------------------------------------------------
    Read a single GIF image from the current position of file 'ifP'.
 
    If 'skipIt' is TRUE, don't do anything else.  Otherwise, write the
-   image to the current position of files 'imageout_file' and 'alphafile'.
-   If 'alphafile' is NULL, though, don't write any alpha information.
+   image to the current position of files *imageoutFileP and *alphafileP.
+   If *alphafileP is NULL, though, don't write any alpha information.
 -----------------------------------------------------------------------------*/
-    unsigned char buf[16];
-    bool useGlobalColormap;
-    xel **xels;  /* The image raster, in libpnm format */
-    bit **alphabits;  
-        /* The image alpha mask, in libpbm format.  NULL if we aren't computing
-           an alpha mask.
-        */
-    unsigned int cols, rows;  /* Dimensions of the image */
-    gifColorMap localColorMap;
-    unsigned int localColorMapSize;
-    bool interlaced;
-
-    if (! ReadOK(ifP,buf,9))
-        pm_error("couldn't read left/top/width/height");
-
-    useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP);
-    localColorMapSize = 1u << ((buf[8] & 0x07) + 1);
-    cols = LM_to_uint(buf[4], buf[5]);
-    rows = LM_to_uint(buf[6], buf[7]);
-    interlaced = !!BitSet(buf[8], INTERLACE);
+    struct GifImageHeader imageHeader;
+    GifColorMap localColorMap;
+    const GifColorMap * currentColorMapP;
+    bool hasGray, hasColor;
 
-    if (verbose)
-        reportImageInfo(cols, rows, useGlobalColormap, localColorMapSize,
-                        interlaced);
-
-    if (cols == 0)
-        pm_error("Invalid GIF - width is zero");
-        
-    xels = pnm_allocarray(cols, rows);
-    if (!xels)
-        pm_error("couldn't alloc space for image" );
+    readImageHeader(ifP, &imageHeader);
 
-    if (alphafile) {
-        alphabits = pbm_allocarray(cols, rows);
-        if (!alphabits)
-            pm_error("couldn't alloc space for alpha image" );
-    } else
-        alphabits = NULL;
-    
-    if (!useGlobalColormap) {
-        int hasGray, hasColor;
+    validateWithinGlobalScreen(imageHeader, gifScreen);
 
-        readColorMap(ifP, localColorMapSize, localColorMap, 
+    if (imageHeader.hasLocalColormap) {
+        readColorMap(ifP, imageHeader.localColorMapSize, &localColorMap, 
                      &hasGray, &hasColor);
-        transparencyMessage(gif89.transparent, localColorMap);
-        readImageData(ifP, xels, cols, rows, localColorMap, localColorMapSize,
-                      interlaced, gif89.transparent, alphabits,
-                      tolerateBadInput);
-        if (!skipIt) {
-            writePnm(imageout_file, xels, cols, rows,
-                     hasGray, hasColor);
-        }
+        currentColorMapP = &localColorMap;
+    } else if (gifScreen.hasGlobalColorMap) {
+        currentColorMapP = &gifScreen.colorMap;
+        hasGray  = gifScreen.hasGray;
+        hasColor = gifScreen.hasColor;
     } else {
-        transparencyMessage(gif89.transparent, gifScreen.ColorMap);
-        readImageData(ifP, xels, cols, rows, 
-                      gifScreen.ColorMap, gifScreen.ColorMapSize,
-                      interlaced, gif89.transparent, alphabits,
-                      tolerateBadInput);
-        if (!skipIt) {
-            writePnm(imageout_file, xels, cols, rows,
-                     gifScreen.hasGray, gifScreen.hasColor);
-        }
+        pm_error("Invalid GIF: "
+                 "Image has no local color map and stream has no global "
+                 "color map either.");
     }
 
-    if (!skipIt && alphafile && alphabits)
-        pbm_writepbm(alphafile, alphabits, cols, rows, FALSE);
-
-    pnm_freearray(xels, rows);
-    if (alphabits)
-        pbm_freearray(alphabits, rows);
+    if (!skipIt) {
+        readImageData(ifP, imageHeader.cols, imageHeader.rows,
+                      *currentColorMapP,
+                      imageHeader.interlaced,
+                      gif89.haveTransColor, gif89.transparentIndex,
+                      imageoutFileP, alphafileP,
+                      hasGray, hasColor,
+                      tolerateBadInput);
+    } else
+        skipImageData(ifP);
 }
 
 
@@ -1637,7 +2014,7 @@ disposeOfReadExtensionsError(const char * const error,
         else
             pm_error("Error accessing Image %u of stream.  %s",
                      imageSeq, error);
-        strfree(error);
+        pm_strfree(error);
         *eodP = TRUE;
     }
 }
@@ -1645,17 +2022,17 @@ disposeOfReadExtensionsError(const char * const error,
 
 
 static void
-convertImages(FILE * const ifP, 
-              bool   const allImages,
-              int    const requestedImageSeq, 
-              bool   const drainStream,
-              FILE * const imageout_file, 
-              FILE * const alphafile,
-              bool   const tolerateBadInput) {
+convertImages(FILE *       const ifP, 
+              bool         const allImages,
+              unsigned int const requestedImageSeq, 
+              bool         const drainStream,
+              FILE *       const imageOutFileP, 
+              FILE *       const alphaFileP,
+              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
-   and 'alphafile' is non-NULL, write PGM alpha masks to file 'alphafile'.
+   it as PNM images to file 'imageOutFileP'.  If the images have transparency
+   and 'alphafile' is non-NULL, write PGM alpha masks to file 'alphaFileP'.
 
    'allImages' means Caller wants all the images in the stream.  
 
@@ -1669,21 +2046,24 @@ convertImages(FILE * const ifP,
    format in the tail of the stream and there may yet be more stuff in
    the file when we return.
 -----------------------------------------------------------------------------*/
-    int imageSeq;
+    unsigned int imageSeq;
         /* Sequence within GIF stream of image we are currently processing.
            First is 0.
         */
-    struct gifScreen gifScreen;
-    struct gif89 gif89;
+    struct GifScreen gifScreen;
+    struct Gif89 gif89;
     bool eod;
         /* We've read through the GIF terminator character */
 
+    /* Set 'gif89' to initial values, to be updated as we encounter the
+       relevant extensions in the GIF stream.
+    */
     initGif89(&gif89);
 
     readGifHeader(ifP, &gifScreen);
 
     for (imageSeq = 0, eod = FALSE;
-         !eod && (imageSeq <= requestedImageSeq || allImages || drainStream);
+         !eod && (allImages || imageSeq <= requestedImageSeq || drainStream);
          ++imageSeq) {
 
         const char * error;
@@ -1701,9 +2081,10 @@ convertImages(FILE * const ifP,
                          imageSeq, imageSeq > 1 ? "s" : "");
         } else {
             if (verbose)
-                pm_message("Reading Image Sequence %d", imageSeq);
+                pm_message("Reading Image Sequence %u", imageSeq);
+
             convertImage(ifP, !allImages && (imageSeq != requestedImageSeq), 
-                         imageout_file, alphafile, gifScreen, gif89,
+                         imageOutFileP, alphaFileP, gifScreen, gif89,
                          tolerateBadInput);
         }
     }
@@ -1714,9 +2095,10 @@ convertImages(FILE * const ifP,
 int
 main(int argc, char **argv) {
 
-    struct cmdlineInfo cmdline;
-    FILE *ifP;
-    FILE *alpha_file, *imageout_file;
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    FILE * alphaFileP;
+    FILE * imageOutFileP;
 
     pnm_init(&argc, argv);
 
@@ -1724,27 +2106,30 @@ main(int argc, char **argv) {
     verbose = cmdline.verbose;
     showComment = cmdline.comments;
    
-    ifP = pm_openr(cmdline.input_filespec);
+    ifP = pm_openr(cmdline.inputFilespec);
 
-    if (cmdline.alpha_filename == NULL)
-        alpha_file = NULL;
+    if (cmdline.alphaFileName == NULL)
+        alphaFileP = NULL;
     else
-        alpha_file = pm_openw(cmdline.alpha_filename);
+        alphaFileP = pm_openw(cmdline.alphaFileName);
 
-    if (alpha_file && streq(cmdline.alpha_filename, "-"))
-        imageout_file = NULL;
+    if (alphaFileP && streq(cmdline.alphaFileName, "-"))
+        imageOutFileP = NULL;
     else
-        imageout_file = stdout;
+        imageOutFileP = stdout;
 
-    convertImages(ifP, cmdline.all_images, cmdline.image_no, 
-                  !cmdline.quitearly, imageout_file, alpha_file,
+    convertImages(ifP, cmdline.allImages, cmdline.imageNum, 
+                  !cmdline.quitearly, imageOutFileP, alphaFileP,
                   cmdline.repair);
 
     pm_close(ifP);
-    if (imageout_file != NULL) 
-        pm_close(imageout_file);
-    if (alpha_file != NULL)
-        pm_close(alpha_file);
+    if (imageOutFileP != NULL) 
+        pm_close(imageOutFileP);
+    if (alphaFileP != NULL)
+        pm_close(alphaFileP);
 
     return 0;
 }
+
+
+
diff --git a/converter/other/hdifftopam.c b/converter/other/hdifftopam.c
index 7bfeed9b..c9363040 100644
--- a/converter/other/hdifftopam.c
+++ b/converter/other/hdifftopam.c
@@ -33,7 +33,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -47,7 +47,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 < 1)
diff --git a/converter/other/infotopam.c b/converter/other/infotopam.c
index 21fa8ee2..f2e35827 100644
--- a/converter/other/infotopam.c
+++ b/converter/other/infotopam.c
@@ -114,7 +114,7 @@ typedef struct DiskObject_ { /* 78 bytes (including Gadget struct) */
     unsigned char version[2];       /* Object version number */
     unsigned char gadget[44];       /* Copy of in memory gadget (44 by */
     unsigned char type;             /* ??? */
-    unsigned char pad;              /* Pad it out to the next word boundry */
+    unsigned char pad;              /* Pad it out to the next word boundary */
     unsigned char pDefaultTool[4];  /* Pointer  to default tool */
     unsigned char ppToolTypes[4];   /* Pointer pointer to tool types */
     unsigned char currentX[4];      /* Current X position (?) */
@@ -175,7 +175,7 @@ parseCommandLine( int              argc,
     opt.allowNegNum   = FALSE;  /* No negative number parameters */
 
     /* Parse the command line */
-    optParseOptions3( &argc, argv, opt, sizeof( opt ), 0 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof( opt ), 0 );
 
     infoP->forceColor = forceColorSpec;
     infoP->selected = selectedSpec;
@@ -233,33 +233,33 @@ getDiskObject( IconInfo * const infoP ) {
     /* Read the disk object header */
     bytesRead = fread( &dobj, 1, sizeof(dobj), infoP->fp );
     if (ferror(infoP->fp))
-        pm_error( "Cannot read disk object header for file '%s'.  "
-                  "fread() errno = %d (%s)",
-                  infoP->name, errno, strerror( errno ) );
-    else if ( bytesRead != sizeof(dobj) )
-        pm_error( "Cannot read entire disk object header for file '%s'.  "
-                  "Only read 0x%X of 0x%X bytes",
-                  infoP->name, bytesRead, sizeof(dobj) );
+        pm_error("Cannot read disk object header for file '%s'.  "
+                 "fread() errno = %d (%s)",
+                 infoP->name, errno, strerror(errno));
+    else if (bytesRead != sizeof(dobj))
+        pm_error("Cannot read entire disk object header for file '%s'.  "
+                 "Only read 0x%X of 0x%X bytes",
+                 infoP->name, (unsigned)bytesRead, (unsigned)sizeof(dobj));
 
     /* Check magic number */
-    if ( ( dobj.magic[0] != 0xE3 ) && ( dobj.magic[1] != 0x10 ) )
-        pm_error( "Wrong magic number for file '%s'.  "
-                  "Expected 0xE310, but got 0x%X%X",
-                  infoP->name, dobj.magic[0], dobj.magic[1] );
+    if ((dobj.magic[0] != 0xE3) && (dobj.magic[1] != 0x10))
+        pm_error("Wrong magic number for file '%s'.  "
+                 "Expected 0xE310, but got 0x%X%X",
+                 infoP->name, dobj.magic[0], dobj.magic[1]);
 
     /* Set version info and have drawer data flag */
-    infoP->version     = ( dobj.version[0]     <<  8 ) +
-        ( dobj.version[1]           );
-    infoP->drawerData  = ( dobj.pDrawerData[0] << 24 ) +
-        ( dobj.pDrawerData[1] << 16 ) +
-        ( dobj.pDrawerData[2] <<  8 ) +
-        ( dobj.pDrawerData[3]       ) ? TRUE : FALSE;
+    infoP->version     = (dobj.version[0]     <<  8) +
+        (dobj.version[1]           );
+    infoP->drawerData  = (dobj.pDrawerData[0] << 24) +
+        (dobj.pDrawerData[1] << 16) +
+        (dobj.pDrawerData[2] <<  8) +
+        (dobj.pDrawerData[3]      ) ? TRUE : FALSE;
 }
 
 
 
 static void 
-getIconHeader( IconInfo * const infoP ) {
+getIconHeader(IconInfo * const infoP) {
 /*-------------------------------------------------------------------------
  * Get fields from icon header portion of info file
  *-------------------------------------------------------------------------*/
@@ -267,25 +267,25 @@ getIconHeader( IconInfo * const infoP ) {
     size_t      bytesRead;
 
     /* Read icon header */
-    bytesRead = fread( &ihead, 1, sizeof(ihead), infoP->fp );
-    if ( ferror(infoP->fp ) )
-         pm_error( "Cannot read icon header for file '%s'.  "
-                   "fread() errno = %d (%s)",
-                   infoP->name, errno, strerror( errno ) );
-    else if ( bytesRead != sizeof(ihead) )
-        pm_error( "Cannot read the entire icon header for file '%s'.  "
-                  "Only read 0x%X of 0x%X bytes",
-                  infoP->name, bytesRead, sizeof(ihead) );
+    bytesRead = fread(&ihead, 1, sizeof(ihead), infoP->fp);
+    if (ferror(infoP->fp))
+        pm_error("Cannot read icon header for file '%s'.  "
+                 "fread() errno = %d (%s)",
+                 infoP->name, errno, strerror(errno));
+    else if (bytesRead != sizeof(ihead))
+        pm_error("Cannot read the entire icon header for file '%s'.  "
+                 "Only read 0x%X of 0x%X bytes",
+                 infoP->name, (unsigned)bytesRead, (unsigned)sizeof(ihead));
 
     /* Get icon width, heigh, and bitplanes */
-    infoP->width  = ( ihead.iconWidth[0]  << 8 ) + ihead.iconWidth[1];
-    infoP->height = ( ihead.iconHeight[0] << 8 ) + ihead.iconHeight[1];
-    infoP->depth  = ( ihead.bpp[0]        << 8 ) + ihead.bpp[1];
+    infoP->width  = (ihead.iconWidth[0]  << 8) + ihead.iconWidth[1];
+    infoP->height = (ihead.iconHeight[0] << 8) + ihead.iconHeight[1];
+    infoP->depth  = (ihead.bpp[0]        << 8) + ihead.bpp[1];
 
     /* Check number of bit planes */
-    if ( ( infoP->depth > 2 ) || ( infoP->depth < 1 ) )
-        pm_error( "We don't know how to interpret %u bitplanes file '%s'.  ",
-                  infoP->depth, infoP->name );
+    if ((infoP->depth > 2) || (infoP->depth < 1))
+        pm_error("We don't know how to interpret %u bitplanes file '%s'.  ",
+                 infoP->depth, infoP->name);
 }
 
 
diff --git a/converter/other/ipdb.c b/converter/other/ipdb.c
new file mode 100644
index 00000000..7ee37872
--- /dev/null
+++ b/converter/other/ipdb.c
@@ -0,0 +1,384 @@
+/*
+ *
+ * Copyright (C) 1997 Eric A. Howe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *   Authors:  Eric A. Howe (mu@trends.net)
+ *             Bryan Henderson, 2010
+ */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+#define _BSD_SOURCE   /* Ensure strdup() is in <string.h> */
+#include <assert.h>
+#include <time.h>
+#include <string.h>
+
+#include "mallocvar.h"
+#include "nstring.h"
+#include "ipdb.h"
+
+typedef uint32_t pilot_time_t;
+
+
+
+
+static unsigned int
+imgPpb(IMAGE * const imgP) {
+/*----------------------------------------------------------------------------
+   Pixels per byte
+-----------------------------------------------------------------------------*/
+    return
+        imgP->type == IMG_GRAY   ? 4 :
+        imgP->type == IMG_GRAY16 ? 2 :
+        8;
+}
+
+
+
+unsigned int
+ipdb_img_ppb(IMAGE * const imgP) {
+/*----------------------------------------------------------------------------
+   Pixels per byte
+-----------------------------------------------------------------------------*/
+    return imgPpb(imgP);
+}
+
+
+
+size_t
+ipdb_img_size(IMAGE * const imgP) {
+/*----------------------------------------------------------------------------
+  Size (in bytes) of an image's data.
+-----------------------------------------------------------------------------*/
+    return (size_t)(imgP->width / imgPpb(imgP) * imgP->height);
+}
+
+
+
+/*
+ * Return the start of row `r'.
+ */
+ uint8_t *
+ ipdb_img_row(IMAGE *      const imgP,
+              unsigned int const row) {
+
+     return &imgP->data[(row) * imgP->width / imgPpb(imgP)];
+ }
+
+
+
+ #define img_row(i, r)   
+
+ static pilot_time_t const unixepoch = (66*365+17)*24*3600;
+     /* The unix epoch in Mac time (the Mac epoch is 00:00 UTC 1904.01.01).
+        The 17 is the number of leap years.
+     */
+
+ static const char * const errorDesc[] = {
+     /* E_BADCOLORS      */
+     "Invalid palette, only {0x00, 0x55, 0xAA, 0xFF} allowed.",
+
+     /* E_NOTIMAGE       */
+     "Not an image file.",
+
+     /* E_IMAGETHERE     */
+     "Image record already present, logic error.",
+
+     /* E_IMAGENOTTHERE  */
+     "Image record required before text record, logic error.",
+
+     /* E_TEXTTHERE      */
+     "Text record already present, logic error.",
+
+     /* E_NOTRECHDR      */
+     "Invalid record header encountered.",
+
+     /* E_UNKNOWNRECHDR  */
+     "Unknown record header.",
+
+     /* E_TOOBIGG        */
+     "Image too big, maximum size approx. 640*400 gray pixels.",
+
+     /* E_TOOBIGM        */
+     "Image too big, maximum size approx. 640*800 monochrome pixels.",
+ };
+
+
+
+ const char *
+ ipdb_err(int const e) {
+
+     if (e < 0)
+         return e >= E_LAST ? errorDesc[-e - 1] : "unknown error";
+     else
+         return strerror(e);
+ }
+
+
+
+ static void
+ rechdr_free(RECHDR * const recP) {
+
+     if (recP) {
+         free(recP->extra);
+         free(recP);
+     }
+ }
+
+
+
+ void
+ ipdb_image_free(IMAGE * const imgP) {
+
+     if (imgP) {
+         rechdr_free(imgP->r);
+         free(imgP->data);
+         free(imgP);
+     }
+ }
+
+
+
+ void
+ ipdb_text_free(TEXT * const textP) {
+
+     if (textP) {
+         rechdr_free(textP->r);
+         free(textP->data);
+         free(textP);
+     }
+ }
+
+
+
+ void
+ ipdb_pdbhead_free(PDBHEAD * const headP) {
+
+     free(headP);
+ }
+
+
+
+ void
+ ipdb_clear(IPDB * const pdbP) {
+     
+     if (pdbP) {
+         ipdb_image_free(pdbP->i);
+         ipdb_text_free(pdbP->t);
+         ipdb_pdbhead_free(pdbP->p);
+    }
+}
+
+
+
+void
+ipdb_free(IPDB * const pdbP) {
+
+    ipdb_clear(pdbP);
+    free(pdbP);
+}
+
+
+
+PDBHEAD *
+ipdb_pdbhead_alloc(const char * const name) {
+
+    PDBHEAD * pdbHeadP;
+
+    MALLOCVAR(pdbHeadP);
+
+    if (pdbHeadP) {
+        MEMSZERO(pdbHeadP);
+
+        STRSCPY(pdbHeadP->name, name == NULL ? "unnamed" : name);
+
+        /*
+         * All of the Image Viewer pdb files that I've come across have
+         * 3510939142U (1997.08.16 14:38:22 UTC) here.  I don't know where
+         * this bizarre date comes from but the real date works fine so
+         * I'm using it.
+         */
+        pdbHeadP->ctime =
+            pdbHeadP->mtime = (pilot_time_t)time(NULL) + unixepoch;
+        
+        MEMSCPY(&pdbHeadP->type, IPDB_vIMG);
+        MEMSCPY(&pdbHeadP->id,   IPDB_View);
+    }
+    return pdbHeadP;
+}
+
+
+
+static RECHDR *
+rechdr_alloc(int      const type,
+             uint32_t const offset) {
+
+    /*
+     * We never produce the `extra' bytes (we only read them from a file)
+     * so there is no point allocating them here.
+     */
+
+    RECHDR  * recHdrP;
+
+    MALLOCVAR(recHdrP);
+    
+    if (recHdrP) {
+        MEMSSET(recHdrP, 0);
+
+        recHdrP->offset   = offset;
+        recHdrP->rec_type = (uint8_t)(0xff & type);
+        MEMSCPY(&recHdrP->unknown, IPDB_MYST);
+    }
+    return recHdrP;
+}
+
+
+
+/*
+ * The offset will be patched up as needed elsewhere.
+ */
+#define IMGOFFSET   (PDBHEAD_SIZE + 8)
+
+
+
+IMAGE *
+ipdb_image_alloc(const char * const name,
+            int          const type,
+            int          const w,
+            int          const h) {
+
+    bool failed;
+    IMAGE * imgP;
+
+    failed = false;
+
+    MALLOCVAR(imgP);
+
+    if (imgP) {
+        MEMSZERO(imgP);
+
+        STRSCPY(imgP->name, name == NULL ? "unnamed" : name);
+        imgP->type     = type;
+        imgP->x_anchor = 0xffff;
+        imgP->y_anchor = 0xffff;
+        imgP->width    = w;
+        imgP->height   = h;
+
+        imgP->r = rechdr_alloc(IMG_REC, IMGOFFSET);
+
+        if (imgP->r) {
+            if (w != 0 && h != 0) {
+                MALLOCARRAY(imgP->data, w * h);
+
+                if (imgP->data) {
+                    memset(imgP->data, 0, sizeof(*(imgP->data)) * w * h);
+                } else
+                    failed = true;
+            }
+            if (failed)
+                rechdr_free(imgP->r);
+        } else
+            failed = true;
+        
+        if (failed)
+            ipdb_image_free(imgP);
+    } else 
+        failed = true;
+
+    return failed ? NULL : imgP;
+}
+
+
+
+TEXT *
+ipdb_text_alloc(const char * const content) {
+
+    TEXT * textP;
+    bool failed;
+
+    failed = false;
+    /*
+     * The offset will be patched up later on when we know what it
+     * should be.
+     */
+
+    MALLOCVAR(textP);
+
+    if (textP) {
+        MEMSZERO(textP);
+
+        textP->r = rechdr_alloc(TEXT_REC, 0);
+
+        if (textP->r) {
+            if (content) {
+                textP->data = strdup(content);
+
+                if (!textP->data)
+                    failed = true;
+            }
+            if (failed)
+                rechdr_free(textP->r);
+        } else
+            failed = true;
+
+        if (failed)
+            ipdb_text_free(textP);
+    } else
+        failed = true;
+
+    return failed ? NULL : textP;
+}
+
+
+
+IPDB *
+ipdb_alloc(const char * const name) {
+
+    IPDB * pdbP;
+    bool failed;
+
+    failed = false;
+
+    MALLOCVAR(pdbP);
+
+    if (pdbP) {
+        MEMSZERO(pdbP);
+
+        if (name) {
+            pdbP->p = ipdb_pdbhead_alloc(name);
+
+            if (!pdbP->p)
+                failed = true;
+        }
+        if (failed)
+            ipdb_free(pdbP);
+    } else
+        failed = true;
+
+    return failed ? NULL : pdbP;
+}
+
+
+
+const char *
+ipdb_typeName(uint8_t const type) {
+
+    switch (type) {
+    case IMG_GRAY16: return "16 Bit Grayscale"; break;
+    case IMG_GRAY: return "Grayscale"; break;
+    case IMG_MONO: return "Monochrome"; break;
+    default: return "???";
+    }
+}
diff --git a/converter/other/ipdb.h b/converter/other/ipdb.h
new file mode 100644
index 00000000..6af5fc44
--- /dev/null
+++ b/converter/other/ipdb.h
@@ -0,0 +1,243 @@
+/*
+ * ipdb.h
+ *  Image Viewer PDB file functions.
+ *
+ * Copyright (C) 1997 Eric A. Howe
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *   Authors: Eric A. Howe (mu@trends.net)
+ *            Bryan Henderson 2010
+ */
+#ifndef IPDB_H_INCLUDED
+#define IPDB_H_INCLUDED
+
+#include <stdio.h>
+#include <errno.h>
+
+/*
+ * Extra error numbers, feed these (or errno values) to ipdb_err()
+ * to get strings.
+ */
+#define E_BADCOLORS -1
+#define E_NOTIMAGE  -2
+#define E_IMAGETHERE    -3
+#define E_IMAGENOTTHERE -4
+#define E_TEXTTHERE -5
+#define E_NOTRECHDR -6
+#define E_UNKNOWNRECHDR -7
+#define E_TOOBIGG   -8
+#define E_TOOBIGM   -9
+#define E_LAST      -9
+
+/*
+ * The standard pdb header.
+ */
+typedef struct {
+    char      name[32];       /* nul terminated   */
+    uint16_t  flags;          /* 0            */
+    uint16_t  version;        /* 0            */
+    uint32_t  ctime;          /* mac time     */
+    uint32_t  mtime;          /* mac time     */
+    uint32_t  btime;          /* mac time     */
+    uint32_t  mod_num;        /* 0            */
+    uint32_t  app_info;       /* 0            */
+    uint32_t  sort_info;      /* 0            */
+    uint8_t   type[4];        /* vIMG         */
+    uint8_t   id[4];          /* View         */
+    uint32_t  uniq_seed;      /* 0            */
+    uint32_t  next_rec;       /* 0            */
+    uint16_t  num_recs;       /* 1            */
+} PDBHEAD;
+#define PDBHEAD_SIZE    (32 + 2*2 + 6*4 + 4 + 4 + 2*4 + 2)
+
+/*
+ * Between the pdb header and the image header we find some "mystery" bytes,
+ * these are supposed to be eight byte record headers but sometimes there
+ * are ten bytes.  Version zero files use eight bytes, version 1 files appear
+ * to use ten bytes, files with attached notes (version 2?) use two sets of
+ * eight bytes.  Note that this version isn't the same as the `version' field
+ * in IMAGE, that version only indicates if the file is compressed or not.
+ *
+ * The first four bytes of each piece are a four byte offset to the start
+ * of the corresponding image header or text record; the next three bytes
+ * (40 6f 80) are some kind of magic (they are always the same); the next
+ * byte is zero for image records and 1 for text records; any remaining
+ * mystery bytes (zero or two) are always zero.
+ */
+typedef struct {
+    uint32_t offset;     /* offset, from zero, to the image  */
+    uint8_t  unknown[3]; /* 40 6f 80             */
+    uint8_t  rec_type;   /* byte seven, TEXT_REC || IMG_REC  */
+    size_t   n_extra;    /* bytes in extra           */
+    uint8_t  *extra;     /* extra unknown end bytes      */
+} RECHDR;
+#define IMG_REC     (uint8_t)(0x00)
+#define TEXT_REC    (uint8_t)(0x01)
+
+/*
+ * The image headers.
+ */
+typedef struct {
+    RECHDR  * r;
+
+    /*
+     * Whether the image was originally compressed.  Since compressed
+     * data can cross row boundaries we have to uncompress the whole
+     * thing during reads so `data' is always in the uncompressed
+     * (but packed) format.  I think we can just use the `version'
+     * field for this but a little extra paranoia is worth a couple
+     * of bytes. This is also set after a write to indicate if
+     * compression was used.
+     */
+    int       compressed;
+
+    /*
+     * The actual image header, this starts at `m->offset'.
+     */
+    char      name[32];   /* nul terminated           */
+    uint8_t   version;    /* 0 => uncompressed, 1 => compressed   */
+    uint8_t   type;       /* GRAYSCALE || MONOCHROME      */
+    uint8_t   reserved1[4];   /* zero                 */
+    uint8_t   note[4];    /* zero                 */
+    uint16_t  x_last;     /* zero                 */
+    uint16_t  y_last;     /* zero                 */
+    uint8_t   reserved2[4];   /* zero                 */
+    uint16_t  x_anchor;   /* 0xffff               */
+    uint16_t  y_anchor;   /* 0xffff               */
+    uint16_t  width;      /* pixels (must be 0 mod 16)        */
+    uint16_t  height;     /* pixels               */
+
+    /*
+     * And finally, the actual image data.  We always store the
+     * image data as 4 pixels per byte uncompressed.  Any compression
+     * or decompression is done at I/O time.
+     */
+    uint8_t  * data;
+} IMAGE;
+
+#define IMAGESIZE   (32 + 1 + 1 + 4 + 4 + 2*2 + 4 + 2*2 + 2*2)
+
+/*
+ * Image types for IMAGE.type.
+ */
+#define IMG_GRAY16  ((uint8_t)2)
+#define IMG_GRAY    ((uint8_t)0)
+#define IMG_MONO    ((uint8_t)0xff)
+
+const char *
+ipdb_typeName(uint8_t const type);
+
+
+/*
+ * Compression constants for IMAGE.version.
+ */
+#define IMG_COMPRESSED      ((uint8_t)0x01)
+#define IMG_UNCOMPRESSED    ((uint8_t)0x00)
+
+/*
+ * The notes record.  If this exists, it will follow the image record.
+ */
+typedef struct {
+    RECHDR  *r;
+    char    *data;      /* the actual text as a normal string   */
+} TEXT;
+
+/*
+ * One PDB file.  The `t' field will be NULL if there is no note.
+ */
+typedef struct {
+    PDBHEAD * p;
+    IMAGE   * i;
+    TEXT    * t;
+} IPDB;
+
+/*
+ * Only use four bytes of these.
+ */
+#define IPDB_vIMG   "vIMG"
+#define IPDB_View   "View"
+/*
+ * Only use three bytes of this.
+ */
+#define IPDB_MYST   "\x40\x6f\x80"
+
+/*
+ * Flags for ipdb_write().
+ */
+#define IPDB_COMPMAYBE  0       /* compress if it does any good */
+#define IPDB_NOCOMPRESS (1 << 1)    /* don't compress       */
+#define IPDB_COMPRESS   (1 << 2)    /* compress         */
+
+#define ipdb_width(pdb)     ((pdb)->i->width)
+#define ipdb_height(pdb)    ((pdb)->i->height)
+#define ipdb_text(pdb)      ((pdb)->t == NULL ? NULL : (pdb)->t->data)
+#define ipdb_compressed(pdb)    ((pdb)->i->compressed)
+#define ipdb_ctime(pdb)     ((time_t)((pdb)->p->ctime - UNIXEPOCH))
+#define ipdb_mtime(pdb)     ((time_t)((pdb)->p->mtime - UNIXEPOCH))
+#define ipdb_btime(pdb)     ((time_t)((pdb)->p->btime - UNIXEPOCH))
+#define ipdb_iname(pdb)     ((pdb)->i->name)
+#define ipdb_pname(pdb)     ((pdb)->p->name)
+#define ipdb_version(pdb)   ((pdb)->i->version)
+#define ipdb_type(pdb)      ((pdb)->i->type)
+#define ipdb_xlast(pdb)     ((pdb)->i->x_last)
+#define ipdb_ylast(pdb)     ((pdb)->i->y_last)
+#define ipdb_xanchor(pdb)   ((pdb)->i->x_anchor)
+#define ipdb_yanchor(pdb)   ((pdb)->i->y_anchor)
+
+const char *
+ipdb_err(int error);
+
+size_t
+ipdb_img_size(IMAGE * const imgP);
+
+unsigned int
+ipdb_img_ppb(IMAGE * const imgP);
+
+uint8_t *
+ipdb_img_row(IMAGE *      const imgP,
+             unsigned int const row);
+
+void
+ipdb_free(IPDB *);
+
+IPDB *
+ipdb_alloc(const char *);
+
+void
+ipdb_clear(IPDB * const pdbP);
+
+PDBHEAD *
+ipdb_pdbhead_alloc(const char * const name);
+
+void
+ipdb_pdbhead_free(PDBHEAD * const headP);
+
+IMAGE *
+ipdb_image_alloc(const char * const name,
+                 int          const type,
+                 int          const w,
+                 int          const h);
+
+void
+ipdb_image_free(IMAGE * const imgP);
+
+void
+ipdb_text_free(TEXT * const textP);
+
+TEXT *
+ipdb_text_alloc(const char * const content);
+
+#endif
diff --git a/converter/other/jbig/ANNOUNCE b/converter/other/jbig/ANNOUNCE
deleted file mode 100644
index edbcc3f8..00000000
--- a/converter/other/jbig/ANNOUNCE
+++ /dev/null
@@ -1,243 +0,0 @@
-
-Version 1.2 of the JBIG-KIT lossless image compression library available
-------------------------------------------------------------------------
-
-Markus Kuhn -- 2000-04-08
-
-
-The latest release of JBIG-KIT can be downloaded over the Internet
-with anonymous ftp from
-
-  ftp://ftp.informatik.uni-erlangen.de/pub/doc/ISO/JBIG/jbigkit-1.2.tar.gz
-  http://www.cl.cam.ac.uk/~mgk25/download/jbigkit-1.2.tar.gz
-
-and from a number of other servers.
-
-JBIG-KIT implements a highly effective data compression algorithm for
-bi-level high-resolution images such as fax pages or scanned
-documents.
-
-JBIG-KIT provides a portable library of compression and decompression
-functions with a documented interface that you can very easily include
-into your image or document processing software. In addition, JBIG-KIT
-provides ready-to-use compression and decompression programs with a
-simple command line interface (similar to the converters found in Jef
-Poskanzer's PBM graphics file conversion package).
-
-JBIG-KIT implements the specification
-
-  International Standard ISO/IEC 11544:1993 and ITU-T Recommendation
-  T.82(1993), "Information technology - Coded representation of picture
-  and audio information - progressive bi-level image compression",
-  <http://www.itu.ch/itudoc/itu-t/rec/t/t82_23822.html>,
-
-which is commonly referred to as the "JBIG standard". JBIG (Joint
-Bi-level Image experts Group) is the committee which developed this
-international standard for the lossless compression of images using
-arithmetic coding. Like the well-known compression algorithms JPEG and
-MPEG, also JBIG has been developed and published by the International
-Organization for Standardization (ISO) and the International
-Telecommunication Union (ITU). See also
-
-  http://www.jpeg.org/public/jbighomepage.htm
-  http://www.iso.ch/
-  http://www.itu.ch/
-
-The JBIG compression algorithm offers the following features:
-
-  - Close to state-of-the-art lossless compression ratio for high
-    resolution bi-level images.
-
-  - Around 1.1 to 1.5 times better compression ratio on typical
-    scanned documents compared to G4 fax compression (ITU-T T.6),
-    which has been the best compression algorithm for scanned
-    documents available prior to JBIG.
-
-  - Up to 30 times better compression of scanned images with dithered
-    images compared to G4 fax compression.
-
-  - Around 2 times better compression on typical 300 dpi documents
-    compared to 'gzip -9' on raw bitmaps.
-    
-  - Around 3-4 times better compression than GIF on typical 300 dpi
-    documents.
-
-  - Even much better competitive compression results on computer
-    generated images which are free of scanning distortions.
-
-  - JBIG supports hierarchical "progressive" encoding, that means it is
-    possible to encode a low resolution image first, followed by
-    resolution enhancement data. This allows for instance a document
-    browser to display already a good 75 dpi low resolution version of
-    an image, while the data necessary to reconstruct the full 300 dpi
-    version for laser printer reproduction is still arriving (say
-    over a slow network link or mass storage medium).
-
-  - The various resolution layers of a JBIG image in progressive
-    encoding mode together require not much more space than a
-    normal non-progressive mode encoded image (which JBIG also
-    supports).
-
-  - The progressive encoding mode utilizes a very sophisticated
-    resolution reduction algorithm which offers highest quality
-    low resolution versions that preserve the shape of characters as
-    well as the integrity of thin lines and dithered images.
-
-  - JBIG supports multiple bit planes and can this way also be used
-    for greyscale and color images, although the main field of
-    application is compression of bi-level images, i.e. images with
-    only two different pixel values. For greyscale images with up to
-    6 bit per pixel, JBIG performs superior to JPEG's lossless
-    mode.
-
-JBIG-KIT is free software under the GNU General Public License. For
-other license arrangements contact the author. JBIG-KIT provides a
-portable library implemented in ANSI/ISO C for encoding and decoding
-JBIG data streams together with documentation. The library is not
-intended for 8-bit or 16-bit machine architectures (e.g., old MS-DOS C
-compilers) on which a number of very efficient optimization techniques
-used in this software are not possible. For maximum performance, a
-32-bit processor is required (64-bit systems work too, of course). On
-architectures with 16-bit pointer arithmetic, only very small images
-can be processed.
-
-Special features of the JBIG-KIT implementation are:
-
-  - Fully reentrant multithread-capable design (no global or static
-    variables, isolated malloc()/free() calls, etc.).
-
-  - Capable of handling incomplete and growing JBIG data streams in
-    order to allow earliest display of low resolution versions.
-
-  - Capable of handling several incoming data streams simultaneously
-    in one single process and task.
-
-  - Especially designed with applications in mind that want to display
-    incoming data as early as possible (e.g., similar to the way in
-    which Netscape Navigator handles incoming GIF images).
-
-  - Implements all JBIG features and options including progressive and
-    sequential encoding, multiple bit planes, user specified
-    resolution reduction and deterministic prediction tables, adaptive
-    template changes for optimal performance on half-tone images,
-    deterministic prediction, typical prediction in lowest and
-    differential layers, various stripe orderings, etc. Only the SEQ
-    and HITOLO options are currently not supported by the decoder
-    (they are normally never required, but could be added later in
-    case of user requirements).
-
-  - Efficient code, optimized utilization of 32-bit processor
-    registers.
-
-  - Very easy to use documented C library interface.
-
-  - Included Gray code conversion routines for efficient encoding
-    of greyscale images.
-
-  - Ready-to-use pbmtojbg and jbgtopbm converters.
-
-
-Changes in version 1.2 (2000-04-08):
-
-  - bug in the decoder fixed, which caused the rest of the input file
-    to be skipped whenever a comment marker was encountered (special
-    thanks to Ben Rudiak-Gould <benrg@math.berkeley.edu> for
-    reporting this one)
-
-Changes in version 1.1 (1999-11-16):
-
-  - serious bug in the encoder fixed, which for a very small
-    percentage of images has caused an unterminated linked list to be
-    created internally that could have been responsible for
-    segmentation violations or non-terminating encoders
-    (special thanks to Hisashi Saiga <saiga@itl.tnr.sharp.co.jp> for
-    tracking that one down)
-
-  - minor bug in the "jbgtopbm -d" diagnostic output fixed
-
-Changes in version 1.0 (1998-04-11):
-
-  - two bugs fixed that caused the encoder and decoder to fail
-    under certain modes of operation with several bit planes
-
-  - added new functions jbg_split_planes(), jbg_dec_merge_planes(),
-    and jbg_dec_getsize_merged() for easy handling of greyscale
-    images
-
-  - added support for compressing greyscale PGM files to pbmtojbg
-    and jbgtopbm
-
-  - more changes to avoid paranoid compiler warnings
-
-Changes in version 0.9 (1996-01-09):
-
-  - encoder won't break any more on input bitmap data with incorrect
-    zero padding
-
-  - pbmtojbg displays a warning if input file has incorrect zero
-    padding
-
-  - various minor improvements suggested by Stefan Willer
-    <Stefan.Willer@unnet.wupper.DE>
-
-  - many minor changes in order to avoid warnings from paranoid
-    compilers
-
-Changes in version 0.8 (1995-09-20):
-
-  - namespace cleared up, all names externally visible from the library
-    start now with jbg_ or JBG_
-
-  - minor non-critical bug fixed which caused library to fail compatibility
-    test and showed up especially on DEC Alpha systems
-
-  - jbg_dec_gethight() is now called jbg_dec_getheight()
-
-  - filenames conform now to MS-DOS limits
-
-  - Bug in pbmtojbg fixed (handling of ASCII PBM files)
-
-Changes in version 0.7 (1995-06-10):
-
-  - more problems on 16-bit int systems and on Macintosh systems fixed
-    (special thanks to Jean-Pierre Gachen <jpg11@calvanet.calvacom.fr>)
-
-  - global Makefile
-
-Changes in version 0.6 (1995-06-08):
-
-  - memory leak fixed
-
-  - should now also work on systems where int is only 16-bit large
-
-  - changes of the JBIG "Technical Corrigendum 1" included (special
-    thanks to Dr. Sebestyen from Siemens AG for sending me a copy
-    of the draft)
-
-First release: version 0.5 (1995-05-28)
-
-
-Please send all questions, problem reports, patches, suggestions,
-success stories, comments, etc. to
-
-  mkuhn at acm.org
-
-I will try to provide free support and maintenance for this software
-at least for the next few months depending on my available time.
-
-Y2K statement: JBIG-KIT does not handle any date and time related
-data, therefore if JBIG-KIT causes you any problems related to date
-and time overflows, this would indeed be most surprising.
-
-This library has been published in the hope that it will encourage the
-development of good freely available scanned document handling and
-transmission systems for the Internet so that large amounts of scanned
-text can be made available to the global village easily.
-
-Happy compressing ...
-
-Markus Kuhn
-
---
-Markus G. Kuhn, Security Group, Computer Lab, Cambridge University, UK
-email: mkuhn at acm.org,  home page: <http://www.cl.cam.ac.uk/~mgk25/>
diff --git a/converter/other/jbig/Makefile b/converter/other/jbig/Makefile
index b5f4e14a..c4d7e9d6 100644
--- a/converter/other/jbig/Makefile
+++ b/converter/other/jbig/Makefile
@@ -5,43 +5,54 @@ endif
 SUBDIR = converter/other/jbig
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
+SUBDIRS =
+
 include $(BUILDDIR)/config.mk
 
-LIBJBIG_OBJECTS = jbig.o jbig_tab.o
+# INTERNAL_JBIGLIB must be relative to the current directory, because it
+# may end up in MERGE_OBJECTS, which must be relative.
+INTERNAL_JBIGLIB = libjbig/libjbig.a
+INTERNAL_JBIGHDR_DIR = $(SRCDIR)/$(SUBDIR)/libjbig/include
 
 EXTERN_INCLUDES =
 ifneq ($(JBIGHDR_DIR),NONE)
-  EXTERN_INCLUDES += -I$(JBIGHDR_DIR)
+  ifneq ($(JBIGHDR_DIR)x,x)
+    EXTERN_INCLUDES += -I$(JBIGHDR_DIR)
+  endif
 endif
 
 ifneq ($(JBIGHDR_DIR),NONE)
   ifneq ($(JBIGLIB),NONE)
-    BINARIES = jbigtopnm pnmtojbig
+    PORTBINARIES = jbigtopnm pnmtojbig
   endif
 endif
 
+BINARIES = $(PORTBINARIES)
+
+MERGEBINARIES = $(BINARIES)
+
 SCRIPTS =
 
-ifeq ($(JBIGLIB),$(BUILDDIR)/$(SUBDIR)/libjbig.a)
+ifeq ($(JBIGLIB),$(INTERNAL_JBIGLIB))
   JBIGLIB_DEP = $(JBIGLIB)
+  SUBDIRS += libjbig
 else
   # It's not our internal version; user's on his own to make sure it's built
 endif
 
-OBJECTS = $(BINARIES:%=%.o) $(LIBJBIG_OBJECTS)
-MERGE_OBJECTS = $(BINARIES:%=%.o2) $(LIBJBIG_OBJECTS)
+OBJECTS = $(BINARIES:%=%.o)
+MERGE_OBJECTS = $(BINARIES:%=%.o2)
 
 all: $(BINARIES)
 
 include $(SRCDIR)/common.mk
 
-$(BINARIES): %: %.o $(JBIGLIB_DEP) $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $< \
-	  $(shell $(LIBOPT) $(NETPBMLIB) $(JBIGLIB)) $(MATHLIB) \
-	  $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
-
+$(BINARIES): %: %.o $(JBIGLIB_DEP) $(LIBOPT)
+$(BINARIES): LDFLAGS_TARGET = $(shell $(LIBOPT) $(JBIGLIB))
 
-$(BUILDDIR)/$(SUBDIR)/libjbig.a: $(LIBJBIG_OBJECTS)
-	$(AR) -rc $@ $^
-	$(RANLIB) $@
+$(INTERNAL_JBIGLIB): $(BUILDDIR)/$(SUBDIR)/libjbig FORCE
+	$(MAKE) -f $(SRCDIR)/$(SUBDIR)/libjbig/Makefile \
+	  -C $(dir $@) $(notdir $@)
 
+.PHONY: FORCE
+FORCE:
diff --git a/converter/other/jbig/README b/converter/other/jbig/README
new file mode 100644
index 00000000..bd7a4745
--- /dev/null
+++ b/converter/other/jbig/README
@@ -0,0 +1,20 @@
+The jbig tools are derived from the JBIG-KIT package by Marcus Kuhn,
+by Bryan Henderson on 2000.05.11.
+
+The file ANNOUNCE in the 'libjbig' subdirectory is from that package and gives
+details.
+
+The Netpbm tools jbigtopbm and pbmtojbig were adapted from JBIG-KIT's
+jbgtopbm and pbmtojbg.  The main difference is that the Netpbm
+versions use the Netpbm libraries.
+
+The code in subdirectory 'libjbig' is straight from the JBIG_KIT package and
+generates essentially the same libjbig.a that one gets from that package.
+
+The code is based on JBIG-KIT version 2.1, adapted to Netpbm by Bryan
+Henderson in June 2014.
+
+The reason Netpbm contains a copy of the library is convenience of users.  For
+many people, the only reason to have a jbig library is to be able to convert
+images to and from the format, which they do with Netpbm.  Requiring a Netpbm
+user to find and install this esoteric library is impractical.
\ No newline at end of file
diff --git a/converter/other/jbig/README.Netpbm b/converter/other/jbig/README.Netpbm
deleted file mode 100644
index 3d593b92..00000000
--- a/converter/other/jbig/README.Netpbm
+++ /dev/null
@@ -1,12 +0,0 @@
-The jbig tools are derived from the JBIG-KIT package by Marcus Kuhn,
-by Bryan Henderson on 2000.05.11.
-
-The file ANNOUNCE in this directory is from that package and gives
-details.
-
-The Netpbm tools jbigtopbm and pbmtojbig were adapted from JBIG-KIT's
-jbgtopbm and pbmtojbg.  The main difference is that the Netpbm
-versions use the Netpbm libraries.
-
-The jbig.c and jbig_table.c modules are straight from the JBIG_KIT 
-package.  They are what normally are packaged as libjbig.a.
diff --git a/converter/other/jbig/jbig_tab.c b/converter/other/jbig/jbig_tab.c
deleted file mode 100644
index 55183503..00000000
--- a/converter/other/jbig/jbig_tab.c
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- *  Probability estimation tables for the arithmetic encoder/decoder
- *  given by ITU T.82 Table 24.
- *
- *  $Id: jbig_tab.c,v 1.6 1998-04-05 18:36:19+01 mgk25 Rel $
- */
-
-short jbg_lsz[113] = {
-  0x5a1d, 0x2586, 0x1114, 0x080b, 0x03d8, 0x01da, 0x00e5, 0x006f,
-  0x0036, 0x001a, 0x000d, 0x0006, 0x0003, 0x0001, 0x5a7f, 0x3f25,
-  0x2cf2, 0x207c, 0x17b9, 0x1182, 0x0cef, 0x09a1, 0x072f, 0x055c,
-  0x0406, 0x0303, 0x0240, 0x01b1, 0x0144, 0x00f5, 0x00b7, 0x008a,
-  0x0068, 0x004e, 0x003b, 0x002c, 0x5ae1, 0x484c, 0x3a0d, 0x2ef1,
-  0x261f, 0x1f33, 0x19a8, 0x1518, 0x1177, 0x0e74, 0x0bfb, 0x09f8,
-  0x0861, 0x0706, 0x05cd, 0x04de, 0x040f, 0x0363, 0x02d4, 0x025c,
-  0x01f8, 0x01a4, 0x0160, 0x0125, 0x00f6, 0x00cb, 0x00ab, 0x008f,
-  0x5b12, 0x4d04, 0x412c, 0x37d8, 0x2fe8, 0x293c, 0x2379, 0x1edf,
-  0x1aa9, 0x174e, 0x1424, 0x119c, 0x0f6b, 0x0d51, 0x0bb6, 0x0a40,
-  0x5832, 0x4d1c, 0x438e, 0x3bdd, 0x34ee, 0x2eae, 0x299a, 0x2516,
-  0x5570, 0x4ca9, 0x44d9, 0x3e22, 0x3824, 0x32b4, 0x2e17, 0x56a8,
-  0x4f46, 0x47e5, 0x41cf, 0x3c3d, 0x375e, 0x5231, 0x4c0f, 0x4639,
-  0x415e, 0x5627, 0x50e7, 0x4b85, 0x5597, 0x504f, 0x5a10, 0x5522,
-  0x59eb
-};
-
-unsigned char jbg_nmps[113] = {
-    1,   2,   3,   4,   5,   6,   7,   8,
-    9,  10,  11,  12,  13,  13,  15,  16,
-   17,  18,  19,  20,  21,  22,  23,  24,
-   25,  26,  27,  28,  29,  30,  31,  32,
-   33,  34,  35,   9,  37,  38,  39,  40,
-   41,  42,  43,  44,  45,  46,  47,  48,
-   49,  50,  51,  52,  53,  54,  55,  56,
-   57,  58,  59,  60,  61,  62,  63,  32,
-   65,  66,  67,  68,  69,  70,  71,  72,
-   73,  74,  75,  76,  77,  78,  79,  48,
-   81,  82,  83,  84,  85,  86,  87,  71,
-   89,  90,  91,  92,  93,  94,  86,  96,
-   97,  98,  99, 100,  93, 102, 103, 104,
-   99, 106, 107, 103, 109, 107, 111, 109,
-  111
-};
-
-/*
- * least significant 7 bits (mask 0x7f) of jbg_nlps[] contain NLPS value,
- * most significant bit (mask 0x80) contains SWTCH bit
- */
-unsigned char jbg_nlps[113] = {
-  129,  14,  16,  18,  20,  23,  25,  28,
-   30,  33,  35,   9,  10,  12, 143,  36,
-   38,  39,  40,  42,  43,  45,  46,  48,
-   49,  51,  52,  54,  56,  57,  59,  60,
-   62,  63,  32,  33, 165,  64,  65,  67,
-   68,  69,  70,  72,  73,  74,  75,  77,
-   78,  79,  48,  50,  50,  51,  52,  53,
-   54,  55,  56,  57,  58,  59,  61,  61,
-  193,  80,  81,  82,  83,  84,  86,  87,
-   87,  72,  72,  74,  74,  75,  77,  77,
-  208,  88,  89,  90,  91,  92,  93,  86,
-  216,  95,  96,  97,  99,  99,  93, 223,
-  101, 102, 103, 104,  99, 105, 106, 107,
-  103, 233, 108, 109, 110, 111, 238, 112,
-  240
-};
-
-/*
- * Resolution reduction table given by ITU-T T.82 Table 17
- */
-
-char jbg_resred[4096] = {
-  0,0,0,1,0,0,0,1,0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
-  0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
-  0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
-  1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,1,0,0,1,1,1,0,1,1,
-  0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,1,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
-  1,0,0,1,0,0,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,
-  0,0,1,1,0,0,0,1,0,0,0,1,0,0,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,0,1,1,1,0,
-  0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
-  0,0,0,1,0,0,0,1,0,1,1,0,1,0,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
-  1,1,1,0,1,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
-  1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,
-  0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,1,1,0,1,0,1,1,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,1,0,1,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,1,1,1,1,0,1,1,
-  1,0,0,1,0,0,1,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,
-  0,0,1,0,1,1,1,1,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,
-  0,0,0,0,1,0,0,1,0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,
-  0,0,1,0,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1,
-  0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,1,0,0,1,0,0,1,1,
-  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,
-  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,
-  0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,1,0,1,0,1,1,0,0,0,1,0,0,1,1,
-  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,0,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,
-  0,0,1,0,0,1,1,1,0,0,0,0,1,0,0,1,0,0,0,1,1,1,1,0,1,0,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,1,0,
-  0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,1,1,0,1,1,1,
-  0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,1,1,0,1,0,0,0,1,1,0,1,0,0,0,0,1,1,1,1,0,0,1,1,1,0,1,1,0,0,1,1,
-  0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,1,0,0,0,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
-  0,0,0,1,0,0,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
-  0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
-  0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
-  0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,1,0,1,1,
-  0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,1,0,1,1,0,1,1,1,0,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,1,0,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,
-  0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,
-  1,0,1,0,1,0,0,1,1,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,0,1,1,0,1,1,1,
-  0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,0,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,1,0,1,0,1,0,1,1,0,1,0,1,0,0,0,1,1,1,1,1,1,1,1,1,
-  1,1,1,0,1,0,0,0,1,1,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
-  1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,1,0,1,1,
-  0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0,0,1,1,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,1,0,0,1,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,1,0,1,0,0,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
-  1,0,0,0,1,0,0,0,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,1,1,0,1,1,0,
-  0,0,1,1,1,1,1,1,0,0,0,0,1,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,
-  0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
-  0,0,1,0,1,0,1,1,0,0,1,0,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,
-  0,0,1,0,1,0,1,1,0,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1,
-  0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,
-  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,
-  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,
-  0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,1,
-  0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,
-  1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,
-  0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,1,0,0,1,1,
-  0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,
-  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,
-  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,
-  0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,1,1,0,0,0,1,0,1,1,1,0,1,1,1
-};
-
-/*
- * Deterministic prediction tables given by ITU-T T.82 tables
- * 19 to 22. The table below is organized differently, the
- * index bits are permutated for higher efficiency.
- */
-
-char jbg_dptable[256 + 512 + 2048 + 4096] = {
-  /* phase 0: offset=0 */
-  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,0,2,2,2,2,2,2,2,
-  0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,0,2,0,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  /* phase 1: offset=256 */
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,0,2,0,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  0,2,2,2,2,1,2,1,2,2,2,2,1,1,1,1,2,0,2,0,2,2,2,2,0,2,0,2,2,2,2,2,
-  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,0,2,2,2,2,2,2,2,
-  0,2,0,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,0,0,2,2,2,2,2,0,0,2,2,2,2,2,
-  0,2,2,2,2,1,2,1,2,2,2,2,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,
-  1,2,1,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,1,1,2,2,2,2,2,0,2,2,2,2,2,2,
-  2,2,2,2,2,0,2,0,2,2,2,2,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2,2,2,2,2,2,
-  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,2,0,2,0,2,2,2,2,2,
-  2,2,2,2,2,1,1,1,2,2,2,2,1,1,1,1,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,0,1,2,0,2,0,2,2,2,2,2,0,2,0,2,2,2,2,1,
-  0,2,0,2,2,1,2,1,2,2,2,2,1,1,1,1,0,0,0,0,2,2,2,2,0,2,0,2,2,2,2,1,
-  2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,0,0,0,2,2,2,2,2,
-  2,2,2,2,2,1,2,1,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,1,2,1,2,2,2,2,1,
-  2,2,2,2,2,2,2,2,0,2,0,2,2,1,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,
-  /* phase 2: offset=768 */
-  2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,1,1,1,1,
-  0,2,2,2,2,1,2,1,2,2,2,2,1,2,1,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
-  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,1,2,1,2,2,2,2,2,1,1,1,
-  2,0,2,2,2,1,2,1,0,2,2,2,1,2,1,2,2,2,2,0,2,2,2,2,0,2,0,2,2,2,2,2,
-  0,2,0,0,1,1,1,1,2,2,2,2,1,1,1,1,0,2,0,2,1,1,1,1,2,2,2,2,1,1,1,1,
-  2,2,0,2,2,2,1,2,2,2,2,2,1,2,1,2,2,2,0,2,2,1,2,1,0,2,0,2,1,1,1,1,
-  2,0,0,2,2,2,2,2,0,2,0,2,2,0,2,0,2,0,2,0,2,2,2,1,2,2,0,2,1,1,2,1,
-  2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,1,1,1,1,
-  0,0,0,0,2,2,2,2,0,0,0,0,2,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,0,2,2,2,2,1,0,2,2,2,1,1,1,1,2,0,2,2,2,2,2,2,0,2,0,2,2,1,2,1,
-  2,0,2,0,2,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,
-  0,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,
-  2,2,0,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,2,2,1,2,1,0,2,2,2,1,1,1,1,
-  2,2,2,0,2,2,2,2,2,2,0,2,2,0,2,0,2,1,2,2,2,2,2,2,1,2,1,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,1,
-  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,1,1,1,2,2,2,2,1,1,1,1,
-  2,2,2,1,2,2,2,2,2,2,1,2,0,0,0,0,2,2,0,2,2,1,2,2,2,2,2,2,1,1,1,1,
-  2,0,0,0,2,2,2,2,0,2,2,2,2,2,2,0,2,2,2,0,2,2,2,2,2,0,0,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,0,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,1,
-  0,2,0,2,2,1,1,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,
-  2,0,2,0,2,1,2,1,0,2,0,2,2,2,1,2,2,0,2,0,2,2,2,2,0,2,0,2,2,2,1,2,
-  2,2,2,0,2,2,2,2,2,2,0,2,2,2,2,2,2,2,1,2,2,2,2,2,2,0,1,2,2,2,2,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  0,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,
-  2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,1,2,1,0,2,2,2,1,1,1,1,
-  2,0,2,0,2,1,2,2,0,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,1,2,2,
-  2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,0,2,2,2,2,0,0,0,0,2,1,2,1,
-  2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,0,0,2,2,2,1,2,2,2,
-  0,0,2,0,2,2,2,2,0,2,0,2,2,0,2,0,1,1,1,2,2,2,2,2,2,2,2,2,2,1,1,1,
-  2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,1,
-  2,2,0,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,1,1,1,1,
-  0,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,
-  2,0,0,2,2,2,2,2,0,2,0,2,2,2,2,2,1,0,1,2,2,2,2,1,0,2,2,2,1,1,1,1,
-  2,2,2,2,2,2,2,2,2,2,0,2,2,0,2,0,2,1,2,2,2,2,2,2,2,2,0,2,2,1,2,2,
-  0,2,0,0,1,1,1,1,0,2,2,2,1,1,1,1,2,2,2,2,2,2,2,2,2,0,2,2,1,2,1,1,
-  2,2,0,2,2,1,2,2,2,2,2,2,1,2,2,2,2,0,2,2,2,2,2,2,0,2,0,2,1,2,1,1,
-  2,0,2,0,2,2,2,2,0,2,0,2,2,1,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,1,
-  2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  0,2,2,2,2,0,2,0,2,2,2,2,0,0,0,0,2,2,2,2,2,1,1,2,2,2,2,2,1,2,2,2,
-  2,0,2,2,2,1,2,1,0,2,2,2,2,2,1,2,2,0,2,0,2,2,2,2,0,2,0,2,2,1,2,2,
-  0,2,0,0,2,2,2,2,1,2,2,2,2,2,2,0,2,1,2,2,2,2,2,2,1,2,2,2,2,2,2,2,
-  0,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,1,0,2,2,
-  0,0,0,2,2,1,1,1,2,2,2,2,1,2,2,2,2,0,2,0,2,2,2,1,2,2,2,2,1,2,1,2,
-  0,0,0,0,2,2,2,2,2,2,0,2,2,1,2,2,2,1,2,1,2,2,2,2,1,2,1,2,0,2,2,2,
-  2,0,2,0,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  0,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,1,2,2,2,2,2,0,2,2,1,2,2,0,0,0,2,2,2,2,2,1,2,2,0,2,2,2,1,2,1,2,
-  2,0,2,0,2,2,2,2,0,2,0,2,2,1,2,2,0,2,0,0,2,2,2,2,2,2,2,2,2,1,2,2,
-  2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,1,
-  1,2,0,2,2,1,2,1,2,2,2,2,1,2,2,2,2,0,2,0,2,2,2,2,2,0,2,2,1,1,1,1,
-  0,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,1,2,1,
-  2,2,0,0,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,
-  2,2,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,
-  2,0,2,0,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,1,0,2,0,2,2,2,1,2,
-  2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
-  2,0,2,0,2,2,2,2,2,0,2,0,2,2,2,2,2,0,2,0,2,2,2,2,0,0,0,0,2,1,2,1,
-  2,2,2,2,2,1,2,1,0,2,0,2,2,2,2,2,2,0,2,0,2,2,2,2,0,2,0,2,2,2,2,1,
-  2,0,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,0,
-  2,0,2,0,2,2,2,1,2,2,2,0,2,2,2,1,2,0,2,0,2,2,2,2,0,0,0,2,2,2,2,1,
-  2,0,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,
-  /* phase 3: offset=2816 */
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
-  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,0,2,2,2,1,2,0,2,2,2,1,2,2,2,2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,
-  2,2,2,1,2,2,2,0,1,1,1,1,0,0,0,0,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,0,0,0,0,1,1,1,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,
-  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,2,0,2,0,2,1,2,1,
-  2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,
-  2,0,2,2,2,1,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,0,0,2,0,1,1,2,1,
-  2,2,2,0,2,2,2,1,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
-  0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,
-  2,0,0,2,2,1,1,2,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,1,1,1,2,0,0,0,
-  2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,2,2,2,0,2,2,2,1,2,0,2,0,2,1,2,1,
-  2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
-  2,2,2,2,2,2,2,2,2,0,0,0,2,1,1,1,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
-  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
-  2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,2,0,2,0,2,1,2,1,0,0,2,0,1,1,2,1,
-  2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,0,0,0,2,1,1,1,
-  2,2,2,1,2,2,2,0,2,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,
-  2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,
-  2,0,2,2,2,1,2,2,0,0,2,0,1,1,2,1,2,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,
-  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,0,0,0,0,1,1,1,1,
-  2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,2,1,0,2,2,0,1,2,
-  2,2,2,1,2,2,2,0,2,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,
-  2,1,2,1,2,0,2,0,1,2,1,1,0,2,0,0,0,0,2,1,1,1,2,0,0,0,0,0,1,1,1,1,
-  2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,0,2,1,2,1,2,0,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,0,2,2,2,1,2,2,2,0,0,2,2,1,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
-  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,
-  2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,0,0,0,0,1,1,1,1,
-  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,0,0,0,2,1,1,1,
-  2,2,2,0,2,2,2,1,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
-  2,0,2,2,2,1,2,2,2,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,1,2,1,2,0,2,0,1,2,1,1,0,2,0,0,2,0,2,2,2,1,2,2,0,2,1,2,1,2,0,2,
-  2,2,2,1,2,2,2,0,2,2,1,2,2,2,0,2,2,1,2,2,2,0,2,2,2,2,0,2,2,2,1,2,
-  0,0,2,0,1,1,2,1,0,0,1,0,1,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,0,2,2,2,1,1,2,2,2,0,2,2,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
-  2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,0,0,2,2,1,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
-  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,
-  2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,2,2,2,1,2,2,2,0,2,1,2,1,2,0,2,0,
-  2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,0,2,0,0,1,2,1,1,2,0,0,0,2,1,1,1,
-  2,2,2,2,2,2,2,2,2,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,1,2,1,2,0,2,0,2,0,2,2,2,1,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,1,1,1,2,0,0,0,
-  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,0,0,1,2,1,1,
-  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,
-  2,1,2,1,2,0,2,0,2,1,2,2,2,0,2,2,2,2,2,0,2,2,2,1,2,0,2,0,2,1,2,1,
-  2,0,2,0,2,1,2,1,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
-  2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,0,1,0,0,1,0,1,1,
-  2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,1,2,2,1,0,2,0,2,2,2,1,2,2,2,
-  2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,
-  2,0,2,0,2,1,2,1,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
-  0,2,0,0,1,2,1,1,2,0,0,0,2,1,1,1,2,2,2,2,2,2,2,2,1,0,1,2,0,1,0,2,
-  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,1,2,2,2,0,2,2,1,1,2,2,0,0,2,2,
-  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,1,2,1,2,0,2,0,2,1,2,2,2,0,2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,
-  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,1,2,2,2,0,2,2,2,
-  2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,2,
-  0,0,0,0,1,1,1,1,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,2,0,2,2,2,1,2,
-  2,0,2,0,2,1,2,1,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
-  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,1,2,2,2,0,1,1,2,1,0,0,2,0,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,0,2,2,2,1,2,
-  2,0,2,0,2,1,2,1,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,
-  0,2,0,0,1,2,1,1,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,0,0,0,2,1,1,1,2,
-  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,0,0,2,1,1,1,2,0,0,2,2,2,1,2,2,2,
-  2,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,0,1,2,1,1,
-  0,0,2,2,1,1,2,2,0,2,1,2,1,2,0,2,2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,
-  2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
-  2,2,0,0,2,2,1,1,2,2,0,0,2,2,1,1,2,2,2,2,2,2,2,2,2,2,0,0,2,2,1,1,
-  2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,0,0,1,2,1,1,
-  2,2,2,0,2,2,2,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,1,1,1,2,0,0,0,2,
-  2,2,2,2,2,2,2,2,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,
-  2,0,2,0,2,1,2,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,0,0,0,2,1,1,1,
-  2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,2,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,
-  2,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,0,2,0,2,1,2,1,2,1,2,0,2,0,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,0,2,0,2,1,2,1,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,2,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,
-  2,0,2,1,2,1,2,0,0,2,1,2,1,2,0,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  2,0,2,0,2,1,2,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
-  2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,1,2,1,2,0,2,0,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
-  2,0,2,0,2,1,2,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-  2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
-};
diff --git a/converter/other/jbig/jbigtopnm.c b/converter/other/jbig/jbigtopnm.c
index 7a6e95c1..733ba227 100644
--- a/converter/other/jbig/jbigtopnm.c
+++ b/converter/other/jbig/jbigtopnm.c
@@ -231,7 +231,7 @@ int main (int argc, char **argv)
         pm_error("Problem while reading input file '%s", fnin);
     if (result != JBG_EOK && result != JBG_EOK_INTR) 
         pm_error("Problem with input file '%s': %s\n", 
-                 fnin, jbg_strerror(result, JBG_EN));
+                 fnin, jbg_strerror(result));
     if (plane >= 0 && jbg_dec_getplanes(&s) <= plane) 
         pm_error("Image has only %d planes!\n", jbg_dec_getplanes(&s));
 
diff --git a/converter/other/jbig/libjbig/ANNOUNCE b/converter/other/jbig/libjbig/ANNOUNCE
new file mode 100644
index 00000000..15ce550d
--- /dev/null
+++ b/converter/other/jbig/libjbig/ANNOUNCE
@@ -0,0 +1,172 @@
+
+JBIG-KIT lossless image compression library
+-------------------------------------------
+
+by Markus Kuhn
+
+
+The latest release of JBIG-KIT can be downloaded from
+
+  http://www.cl.cam.ac.uk/~mgk25/jbigkit/
+
+JBIG-KIT implements a highly effective data compression algorithm for
+bi-level high-resolution images such as fax pages or scanned
+documents.
+
+JBIG-KIT provides two variants of a portable library of compression
+and decompression functions with a documented interface. You can very
+easily include into your image or document processing software. In
+addition, JBIG-KIT provides ready-to-use compression and decompression
+programs with a simple command line interface (similar to the
+converters found in Jef Poskanzer's PBM graphics file conversion
+package).
+
+JBIG-KIT implements the specification
+
+  International Standard ISO/IEC 11544:1993 and ITU-T Recommendation
+  T.82(1993), "Information technology - Coded representation of picture
+  and audio information - progressive bi-level image compression",
+  <http://www.itu.int/rec/T-REC-T.82>,
+
+which is commonly referred to as the "JBIG1 standard". JBIG (Joint
+Bi-level Image experts Group) is the committee which developed this
+international standard for the lossless compression of images using
+arithmetic coding. Like the well-known compression algorithms JPEG and
+MPEG, JBIG has also been developed and published by the International
+Organization for Standardization (ISO) and the International
+Telecommunication Union (ITU). See also
+
+  http://www.jpeg.org/jbig/
+  http://www.iso.ch/
+  http://www.itu.int/
+
+The JBIG compression algorithm offers the following features:
+
+  - Close to state-of-the-art lossless compression ratio for high
+    resolution bi-level images.
+
+  - About 1.1 to 1.5 times better compression ratio on typical
+    scanned documents compared to G4 fax compression (ITU-T T.6),
+    which has been the best compression algorithm for scanned
+    documents available prior to JBIG.
+
+  - Up to 30 times better compression of scanned images with dithered
+    images compared to G4 fax compression.
+
+  - About 2 times better compression on typical 300 dpi documents
+    compared to 'gzip -9' on raw bitmaps.
+    
+  - About 3-4 times better compression than GIF on typical 300 dpi
+    documents.
+
+  - Even much better competitive compression results on computer
+    generated images which are free of scanning distortions.
+
+  - JBIG supports hierarchical "progressive" encoding, that means it is
+    possible to encode a low resolution image first, followed by
+    resolution enhancement data. This allows, for instance, a document
+    browser to display already a good 75 dpi low resolution version of
+    an image, while the data necessary to reconstruct the full 300 dpi
+    version for laser printer reproduction is still arriving (say
+    over a slow network link or mass storage medium).
+
+  - The various resolution layers of a JBIG image in progressive
+    encoding mode together require not much more space than a
+    normal non-progressive mode encoded image (which JBIG also
+    supports).
+
+  - The progressive encoding mode utilizes a quite sophisticated
+    resolution reduction algorithm which offers high quality low
+    resolution versions that preserve the shape of characters as well
+    as the integrity of thin lines and dithered images.
+
+  - JBIG supports multiple bit planes and can this way also be used
+    for grayscale and color images, although the main field of
+    application is compression of bi-level images, i.e. images with
+    only two different pixel values. For grayscale images with up to
+    6 bit per pixel, JBIG performs superior to JPEG's lossless
+    mode.
+
+JBIG-KIT can be used as free software under the GNU General Public
+License. Other license arrangements more suitable for commercial
+applications are available as well, please contact the author for
+details. JBIG-KIT provides two portable libraries implemented in
+ANSI/ISO C for encoding and decoding JBIG data streams, along with
+documentation. The first library, jbig.c, implements nearly all of the
+options that the JBIG standard provides, but keeps the entire
+uncompressed image in memory. The second library, jbig85.c, implements
+only the ITU-R T.85 subset of the standard that black/white fax
+machines use (single bit per pixel, no "progressive" encoding), and
+keeps only three lines of the uncompressed image in memory, making it
+particularly attractive for low-memory embedded applications.
+
+The libraries are not intended for 8-bit or 16-bit machine
+architectures (e.g., old MS-DOS C compilers). For maximum performance,
+a 32-bit processor is required (64-bit systems work too, of course).
+On architectures with 16-bit pointer arithmetic, the full-featured
+jbig.c library can process only very small images.
+
+Special features of the full-featured jbig.c variant:
+
+  - Fully reentrant multithread-capable design (no global or static
+    variables, isolated malloc()/free() calls, etc.)
+
+  - Capable of handling incomplete and growing JBIG data streams in
+    order to allow earliest display of low resolution versions
+
+  - Capable of handling several incoming data streams simultaneously
+    in one single process and thread
+
+  - Especially designed with applications in mind that want to display
+    incoming data as early as possible (e.g., similar to the way in
+    which Netscape Navigator handles incoming GIF images)
+
+  - Implements all JBIG features and options including progressive and
+    sequential encoding, multiple bit planes, user specified
+    resolution reduction and deterministic prediction tables, adaptive
+    template changes for optimal performance on half-tone images,
+    deterministic prediction, typical prediction in lowest and
+    differential layers, various stripe orderings, etc; only the SEQ
+    and HITOLO options are currently not supported by the decoder
+    (they are normally never required, but could be added later in
+    case of user requirements)
+
+  - Suitable for fax applications, satisfies ITU-T T.85 profile
+
+  - Efficient code, optimized utilization of 32-bit processor
+    registers
+
+  - Very easy to use documented C library interface
+
+  - Included Gray code conversion routines for efficient encoding
+    of grayscale images
+
+  - Ready-to-use pbmtojbg and jbgtopbm converters.
+
+Special features of the light-weight jbig85.c variant:
+
+  - Suitable for low-memory embedded applications
+
+  - Implements only the JBIG1 subset defined in the ITU-T T.85
+    profile (single bit plane, no differential layers)
+
+  - Requires only three pixel rows of the uncompressed image to be
+    kept in memory
+
+  - Handles all NEWLEN modes of operation required by ITU-T T.85 with
+    just a single pass over the data, automatically performing the
+    necessary lookahead after the last stripe
+
+  - Codec buffers only a few bytes of arithmetic-codec data and outputs
+    resulting bytes or lines as soon as they are available.
+
+I will try to provide free support and maintenance for this software
+for the foreseeable future, depending on my available time.
+
+Happy compressing ...
+
+Markus Kuhn
+
+--
+Markus Kuhn, Computer Laboratory, University of Cambridge
+http://www.cl.cam.ac.uk/~mgk25/ || CB3 0FD, Great Britain
diff --git a/converter/other/jbig/libjbig/COPYING b/converter/other/jbig/libjbig/COPYING
new file mode 100644
index 00000000..a43ea212
--- /dev/null
+++ b/converter/other/jbig/libjbig/COPYING
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/converter/other/jbig/libjbig/Makefile b/converter/other/jbig/libjbig/Makefile
new file mode 100644
index 00000000..2e574903
--- /dev/null
+++ b/converter/other/jbig/libjbig/Makefile
@@ -0,0 +1,24 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../../../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = converter/other/jbig/libjbig
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/config.mk
+
+LIBJBIG_OBJECTS = jbig.o jbig_ar.o
+
+OBJECTS = $(LIBJBIG_OBJECTS)
+MERGE_OBJECTS = $(LIBJBIG_OBJECTS)
+
+COMP_INCLUDES = -I$(SRCDIR)/$(SUBDIR)/include
+
+all: libjbig.a
+
+include $(SRCDIR)/common.mk
+
+libjbig.a: $(LIBJBIG_OBJECTS)
+	$(AR) -rc $@ $^
+	$(RANLIB) $@
+
diff --git a/converter/other/jbig/jbig.h b/converter/other/jbig/libjbig/include/jbig.h
index dd9a76f3..67994107 100644
--- a/converter/other/jbig/jbig.h
+++ b/converter/other/jbig/libjbig/include/jbig.h
@@ -1,21 +1,32 @@
 /*
- *  Header file for the portable free JBIG compression library
+ *  Header file for the portable JBIG compression library
  *
- *  Markus Kuhn -- mkuhn@acm.org
- *
- *  $Id: jbig.h,v 1.9 1999-11-16 15:58:45+00 mgk25 Rel $
+ *  Copyright 1995-2014 -- Markus Kuhn -- http://www.cl.cam.ac.uk/~mgk25/
  */
 
 #ifndef JBG_H
 #define JBG_H
 
 #include <stddef.h>
+#include "jbig_ar.h"
 
 /*
  * JBIG-KIT version number
  */
 
-#define JBG_VERSION    "1.1"
+#define JBG_VERSION    "2.1"
+#define JBG_VERSION_MAJOR 2
+#define JBG_VERSION_MINOR 1
+
+/*
+ * JBIG-KIT licence agreement reference code:
+ * If you use JBIG-KIT under a commercial licence, please replace
+ * below the letters GPL with the reference code that you received
+ * with your licence agreement. (This code is typically a letter "A"
+ * followed by four decimal digits, e.g. "A1234".)
+ */
+
+#define JBG_LICENCE    "GPL"
 
 /*
  * Buffer block for SDEs which are temporarily stored by encoder
@@ -34,7 +45,7 @@ struct jbg_buf {
 };
 
 /*
- * Maximum number of allowed ATMOVEs per stripe
+ * Maximum number of ATMOVEs per stripe that decoder can handle
  */
 
 #define JBG_ATMOVES_MAX  64
@@ -56,82 +67,30 @@ struct jbg_buf {
 #define JBG_DPPRIV     0x02
 #define JBG_DPLAST     0x01
 
-#define JBG_DELAY_AT   0x100  /* delay ATMOVE until the first line of the next
+/* encoding options that will not be indicated in the header */
+
+#define JBG_DELAY_AT   0x100  /* Delay ATMOVE until the first line of the next
 			       * stripe. Option available for compatibility
-			       * with conformance test example in clause 7.2.*/
+			       * with conformance test example in clause 7.2. */
 
+#define JBG_SDRST      0x200  /* Use SDRST instead of SDNORM. This option is
+			       * there for anyone who needs to generate
+			       * test data that covers the SDRST cases. */
 
 /*
  * Possible error code return values
  */
 
-#define JBG_EOK        0
-#define JBG_EOK_INTR   1
-#define JBG_EAGAIN     2
-#define JBG_ENOMEM     3
-#define JBG_EABORT     4
-#define JBG_EMARKER    5
-#define JBG_ENOCONT    6
-#define JBG_EINVAL     7
-#define JBG_EIMPL      8
-
-/*
- * Language code for error message strings (based on ISO 639 2-letter
- * standard language name abbreviations).
- */ 
-
-#define JBG_EN         0        /* English */
-#define JBG_DE_8859_1  1        /* German in ISO Latin 1 character set */
-#define JBG_DE_UTF_8   2        /* German in Unicode UTF-8 encoding */
-
-/*
- * Status description of an arithmetic encoder
- */
-
-struct jbg_arenc_state {
-  unsigned char st[4096];    /* probability status for contexts, MSB = MPS */
-  unsigned long c;                /* C register, base of coding intervall, *
-                                   * layout as in Table 23                 */
-  unsigned long a;      /* A register, normalized size of coding intervall */
-  long sc;        /* counter for buffered 0xff values which might overflow */
-  int ct;  /* bit shift counter, determines when next byte will be written */
-  int buffer;                /* buffer for most recent output byte != 0xff */
-  void (*byte_out)(int, void *); /* function which receives all PSCD bytes */
-  void *file;                              /* parameter passed to byte_out */
-};
-
-
-/*
- * Status description of an arithmetic decoder
- */
-
-struct jbg_ardec_state {
-  unsigned char st[4096];    /* probability status for contexts, MSB = MPS */
-  unsigned long c;                /* C register, base of coding intervall, *
-                                   * layout as in Table 25                 */
-  unsigned long a;      /* A register, normalized size of coding intervall */
-  int ct;     /* bit shift counter, determines when next byte will be read */
-  unsigned char *pscd_ptr;               /* pointer to next PSCD data byte */
-  unsigned char *pscd_end;                   /* pointer to byte after PSCD */
-  enum {
-    JBG_OK,                        /* symbol has been successfully decoded */
-    JBG_READY,             /* no more bytes of this PSCD required, marker  *
-		            * encountered, probably more symbols available */
-    JBG_MORE,          /* more PSCD data bytes required to decode a symbol */
-    JBG_MARKER   /* more PSCD data bytes required, ignored final 0xff byte */
-  } result;                              /* result of previous decode call */
-  int startup;                            /* controls initial fill of s->c */
-};
-
-#ifdef TEST_CODEC
-void arith_encode_init(struct jbg_arenc_state *s, int reuse_st);
-void arith_encode_flush(struct jbg_arenc_state *s);
-void arith_encode(struct jbg_arenc_state *s, int cx, int pix);
-void arith_decode_init(struct jbg_ardec_state *s, int reuse_st);
-int arith_decode(struct jbg_ardec_state *s, int cx);
-#endif
+#define JBG_EOK        (0 << 4)
+#define JBG_EOK_INTR   (1 << 4)
+#define JBG_EAGAIN     (2 << 4)
+#define JBG_ENOMEM     (3 << 4)
+#define JBG_EABORT     (4 << 4)
+#define JBG_EMARKER    (5 << 4)
+#define JBG_EINVAL     (6 << 4)
+#define JBG_EIMPL      (7 << 4)
+#define JBG_ENOCONT    (8 << 4)
 
- 
 /*
  * Status of a JBIG encoder
  */
@@ -139,6 +98,8 @@ int arith_decode(struct jbg_ardec_state *s, int cx);
 struct jbg_enc_state {
   int d;                            /* resolution layer of the input image */
   unsigned long xd, yd;    /* size of the input image (resolution layer d) */
+  unsigned long yd1;    /* BIH announced height of image, use yd1 != yd to
+                        emulate T.85-style NEWLEN height updates for tests */
   int planes;                         /* number of different bitmap planes */
   int dl;                       /* lowest resolution layer in the next BIE */
   int dh;                      /* highest resolution layer in the next BIE */
@@ -161,6 +122,10 @@ struct jbg_enc_state {
                                                     /* data write callback */
   void *file;                            /* parameter passed to data_out() */
   char *tp;    /* buffer for temp. values used by diff. typical prediction */
+  unsigned char *comment; /* content of comment marker segment to be added
+                             at next opportunity (will be reset to NULL
+                             as soon as comment has been written)          */
+  unsigned long comment_len;       /* length of data pointed to by comment */
 };
 
 
@@ -236,7 +201,7 @@ int jbg_enc_lrlmax(struct jbg_enc_state *s, unsigned long mwidth,
 void jbg_enc_layers(struct jbg_enc_state *s, int d);
 int  jbg_enc_lrange(struct jbg_enc_state *s, int dl, int dh);
 void jbg_enc_options(struct jbg_enc_state *s, int order, int options,
-		     long l0, int mx, int my);
+		     unsigned long l0, int mx, int my);
 void jbg_enc_out(struct jbg_enc_state *s);
 void jbg_enc_free(struct jbg_enc_state *s);
 
@@ -245,17 +210,17 @@ void jbg_dec_maxsize(struct jbg_dec_state *s, unsigned long xmax,
 		     unsigned long ymax);
 int  jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
 		size_t *cnt);
-long jbg_dec_getwidth(const struct jbg_dec_state *s);
-long jbg_dec_getheight(const struct jbg_dec_state *s);
+unsigned long jbg_dec_getwidth(const struct jbg_dec_state *s);
+unsigned long jbg_dec_getheight(const struct jbg_dec_state *s);
 unsigned char *jbg_dec_getimage(const struct jbg_dec_state *s, int plane);
-long jbg_dec_getsize(const struct jbg_dec_state *s);
+unsigned long jbg_dec_getsize(const struct jbg_dec_state *s);
 void jbg_dec_merge_planes(const struct jbg_dec_state *s, int use_graycode,
 			  void (*data_out)(unsigned char *start, size_t len,
 					   void *file), void *file);
-long jbg_dec_getsize_merged(const struct jbg_dec_state *s);
+unsigned long jbg_dec_getsize_merged(const struct jbg_dec_state *s);
 void jbg_dec_free(struct jbg_dec_state *s);
 
-const char *jbg_strerror(int errnum, int language);
+const char *jbg_strerror(int errnum);
 void jbg_int2dppriv(unsigned char *dptable, const char *internal);
 void jbg_dppriv2int(char *internal, const unsigned char *dptable);
 unsigned long jbg_ceil_half(unsigned long x, int n);
@@ -263,5 +228,6 @@ void jbg_split_planes(unsigned long x, unsigned long y, int has_planes,
 		      int encode_planes,
 		      const unsigned char *src, unsigned char **dest,
 		      int use_graycode);
+int jbg_newlen(unsigned char *bie, size_t len);
 
 #endif /* JBG_H */
diff --git a/converter/other/jbig/libjbig/include/jbig_ar.h b/converter/other/jbig/libjbig/include/jbig_ar.h
new file mode 100644
index 00000000..d58b1ae0
--- /dev/null
+++ b/converter/other/jbig/libjbig/include/jbig_ar.h
@@ -0,0 +1,53 @@
+/*
+ *  Header file for the arithmetic encoder and decoder of
+ *  the portable JBIG compression library
+ *
+ *  Markus Kuhn -- http://www.cl.cam.ac.uk/~mgk25/jbigkit/
+ */
+
+#ifndef JBG_AR_H
+#define JBG_AR_H
+
+/*
+ * Status of arithmetic encoder
+ */
+
+struct jbg_arenc_state {
+  unsigned char st[4096];    /* probability status for contexts, MSB = MPS */
+  unsigned long c;                /* register C: base of coding intervall, *
+                                   * layout as in Table 23                 */
+  unsigned long a;       /* register A: normalized size of coding interval */
+  long sc;     /* number of buffered 0xff values that might still overflow */
+  int ct;  /* bit shift counter, determines when next byte will be written */
+  int buffer;                /* buffer for most recent output byte != 0xff */
+  void (*byte_out)(int, void *);  /* function that receives all PSCD bytes */
+  void *file;                              /* parameter passed to byte_out */
+};
+
+/*
+ * Status of arithmetic decoder
+ */
+
+struct jbg_ardec_state {
+  unsigned char st[4096];    /* probability status for contexts, MSB = MPS */
+  unsigned long c;                /* register C: base of coding intervall, *
+                                   * layout as in Table 25                 */
+  unsigned long a;       /* register A: normalized size of coding interval */
+  unsigned char *pscd_ptr;               /* pointer to next PSCD data byte */
+  unsigned char *pscd_end;                   /* pointer to byte after PSCD */
+  int ct;    /* bit-shift counter, determines when next byte will be read;
+              * special value -1 signals that zero-padding has started     */
+  int startup;          /* boolean flag that controls initial fill of s->c */
+  int nopadding;        /* boolean flag that triggers return -2 between
+			 * reaching PSCD end and decoding the first symbol
+			 * that might never have been encoded in the first
+			 * place */
+};
+
+void arith_encode_init(struct jbg_arenc_state *s, int reuse_st);
+void arith_encode_flush(struct jbg_arenc_state *s);
+void arith_encode(struct jbg_arenc_state *s, int cx, int pix);
+void arith_decode_init(struct jbg_ardec_state *s, int reuse_st);
+int  arith_decode(struct jbg_ardec_state *s, int cx);
+
+#endif /* JBG_AR_H */
diff --git a/converter/other/jbig/jbig.c b/converter/other/jbig/libjbig/jbig.c
index f33f80be..d7141a75 100644
--- a/converter/other/jbig/jbig.c
+++ b/converter/other/jbig/libjbig/jbig.c
@@ -1,15 +1,13 @@
 /*
- *  Portable Free JBIG image compression library
+ *  Portable JBIG image compression library
  *
- *  Markus Kuhn -- mkuhn@acm.org
- *
- *  $Id: jbig.c,v 1.12 2000-04-08 11:42:18+01 mgk25 Rel $
+ *  Copyright 1995-2014 -- Markus Kuhn -- http://www.cl.cam.ac.uk/~mgk25/
  *
  *  This module implements a portable standard C encoder and decoder
- *  using the JBIG lossless bi-level image compression algorithm as
- *  specified in International Standard ISO 11544:1993 or equivalently
- *  as specified in ITU-T Recommendation T.82. See the file jbig.doc
- *  for usage instructions and application examples.
+ *  using the JBIG1 lossless bi-level image compression algorithm
+ *  specified in International Standard ISO 11544:1993 and
+ *  ITU-T Recommendation T.82. See the file jbig.txt for usage
+ *  instructions and application examples.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -27,42 +25,22 @@
  * 
  *  If you want to use this program under different license conditions,
  *  then contact the author for an arrangement.
- *
- *  It is possible that certain products which can be built using this
- *  software module might form inventions protected by patent rights in
- *  some countries (e.g., by patents about arithmetic coding algorithms
- *  owned by IBM and AT&T in the USA). Provision of this software by the
- *  author does NOT include any licences for any patents. In those
- *  countries where a patent licence is required for certain applications
- *  of this software module, you will have to obtain such a licence
- *  yourself.
  */
 
 #ifdef DEBUG
 #include <stdio.h>
+#else
+#define NDEBUG
 #endif
 
 #include <stdlib.h>
+#include <string.h>
 #include <assert.h>
 
 #include "jbig.h"
 
-
-/* optional export of arithmetic coder functions for test purposes */
-#ifdef TEST_CODEC
-#define ARITH
-#define ARITH_INL
-#else
-#define ARITH      static
-#ifdef __GNUC__
-#define ARITH_INL  static __inline__
-#else
-#define ARITH_INL  static
-#endif
-#endif
-
-#define MX_MAX  23     /* maximal supported mx offset for
-			* adaptive template in the encoder */
+#define MX_MAX  127
+   /* maximal mx offset for adaptive template in the encoder */
 
 #define TPB2CX  0x195  /* contexts for TP special pixels */
 #define TPB3CX  0x0e5
@@ -91,15 +69,15 @@
 /* object code version id */
 
 const char jbg_version[] = 
-" JBIG-KIT " JBG_VERSION " -- Markus Kuhn -- "
-"$Id: jbig.c,v 1.12 2000-04-08 11:42:18+01 mgk25 Rel $ ";
+  "JBIG-KIT " JBG_VERSION " -- (c) 1995-2014 Markus Kuhn -- "
+  "Licence: " JBG_LICENCE "\n";
 
 /*
- * the following array specifies for each combination of the 3
+ * The following array specifies for each combination of the 3
  * ordering bits, which ii[] variable represents which dimension
  * of s->sde.
  */
-static const int index[8][3] = {
+static const int iindex[8][3] = {
   { 2, 1, 0 },    /* no ordering bit set */
   { -1, -1, -1},  /* SMID -> illegal combination */
   { 2, 0, 1 },    /* ILEAVE */
@@ -110,96 +88,90 @@ static const int index[8][3] = {
   { -1, -1, -1 }  /* SEQ + SMID + ILEAVE -> illegal combination */
 };
 
+#define _(String) String  /* to mark translatable string for GNU gettext */
 
 /*
- * Array [language][message] with text string error messages that correspond
+ * Array with English ASCII error messages that correspond
  * to return values from public functions in this library.
  */
-#define NEMSG         9  /* number of error codes */
-#define NEMSG_LANG    3  /* number of supported languages */
-static const char *errmsg[NEMSG_LANG][NEMSG] = {
-  /* English (JBG_EN) */
-  {
-    "Everything is ok",                                     /* JBG_EOK */
-    "Reached specified maximum size",                       /* JBG_EOK_INTR */
-    "Unexpected end of data",                               /* JBG_EAGAIN */
-    "Not enough memory available",                          /* JBG_ENOMEM */
-    "ABORT marker found",                                   /* JBG_EABORT */
-    "Unknown marker segment encountered",                   /* JBG_EMARKER */
-    "Incremental BIE does not fit to previous one",         /* JBG_ENOCONT */
-    "Invalid data encountered",                             /* JBG_EINVAL */
-    "Unimplemented features used"                           /* JBG_EIMPL */
-  },
-  /* German (JBG_DE_8859_1) */
-  {
-    "Kein Problem aufgetreten",                             /* JBG_EOK */
-    "Angegebene maximale Bildgr\366\337e erreicht",         /* JBG_EOK_INTR */
-    "Unerwartetes Ende der Daten",                          /* JBG_EAGAIN */
-    "Nicht gen\374gend Speicher vorhanden",                 /* JBG_ENOMEM */
-    "Es wurde eine Abbruch-Sequenz gefunden",               /* JBG_EABORT */
-    "Eine unbekannte Markierungssequenz wurde gefunden",    /* JBG_EMARKER */
-    "Neue Daten passen nicht zu vorangegangenen Daten",     /* JBG_ENOCONT */
-    "Es wurden ung\374ltige Daten gefunden",                /* JBG_EINVAL */
-    "Noch nicht implementierte Optionen wurden benutzt"     /* JBG_EIMPL */
-  },
-  /* German (JBG_DE_UTF_8) */
-  {
-    "Kein Problem aufgetreten",                             /* JBG_EOK */
-    "Angegebene maximale Bildgr\303\266\303\237e erreicht", /* JBG_EOK_INTR */
-    "Unerwartetes Ende der Daten",                          /* JBG_EAGAIN */
-    "Nicht gen\303\274gend Speicher vorhanden",             /* JBG_ENOMEM */
-    "Es wurde eine Abbruch-Sequenz gefunden",               /* JBG_EABORT */
-    "Eine unbekannte Markierungssequenz wurde gefunden",    /* JBG_EMARKER */
-    "Neue Daten passen nicht zu vorangegangenen Daten",     /* JBG_ENOCONT */
-    "Es wurden ung\303\274ltige Daten gefunden",            /* JBG_EINVAL */
-    "Noch nicht implementierte Optionen wurden benutzt"     /* JBG_EIMPL */
-  }
+static const char *errmsg[] = {
+  _("All OK"),                                               /* JBG_EOK */
+  _("Reached specified image size"),                         /* JBG_EOK_INTR */
+  _("Unexpected end of input data stream"),                  /* JBG_EAGAIN */
+  _("Not enough memory available"),                          /* JBG_ENOMEM */
+  _("ABORT marker segment encountered"),                     /* JBG_EABORT */
+  _("Unknown marker segment encountered"),                   /* JBG_EMARKER */
+  _("Input data stream contains invalid data"),              /* JBG_EINVAL */
+  _("Input data stream uses unimplemented JBIG features"),   /* JBG_EIMPL */
+  _("Incremental BIE does not continue previous one")        /* JBG_ENOCONT */
 };
 
 
-
 /*
  * The following three functions are the only places in this code, were
  * C library memory management functions are called. The whole JBIG
  * library has been designed in order to allow multi-threaded
- * execution. no static or global variables are used, so all fuctions
+ * execution. No static or global variables are used, so all fuctions
  * are fully reentrant. However if you want to use this multi-thread
  * capability and your malloc, realloc and free are not reentrant,
  * then simply add the necessary semaphores or mutex primitives below.
+ * In contrast to C's malloc() and realloc(), but like C's calloc(),
+ * these functions take two parameters nmemb and size that are multiplied
+ * before being passed on to the corresponding C function. 
+ * This we can catch all overflows during a size_t multiplication a
+ * a single place.
  */
 
-static void *checked_malloc(size_t size)
+#ifndef SIZE_MAX
+#define SIZE_MAX ((size_t) -1)     /* largest value of size_t */
+#endif
+
+static void *checked_malloc(size_t nmemb, size_t size)
 {
   void *p;
-  
-  p = malloc(size);
+
   /* Full manual exception handling is ugly here for performance
    * reasons. If an adequate handling of lack of memory is required,
-   * then use C++ and throw a C++ exception here. */
+   * then use C++ and throw a C++ exception instead of abort(). */
+
+  /* assert that nmemb * size <= SIZE_MAX */
+  if (size > SIZE_MAX / nmemb)
+    abort();
+  
+  p = malloc(nmemb * size);
+
   if (!p)
     abort();
 
 #if 0
-  fprintf(stderr, "%p = malloc(%ld)\n", p, (long) size);
+  fprintf(stderr, "%p = malloc(%lu * %lu)\n", p,
+	  (unsigned long) nmemb, (unsigned long) size);
 #endif
 
   return p;
 }
 
 
-static void *checked_realloc(void *ptr, size_t size)
+static void *checked_realloc(void *ptr, size_t nmemb, size_t size)
 {
   void *p;
 
-  p = realloc(ptr, size);
   /* Full manual exception handling is ugly here for performance
    * reasons. If an adequate handling of lack of memory is required,
-   * then use C++ and throw a C++ exception here. */
+   * then use C++ and throw a C++ exception here instead of abort(). */
+
+  /* assert that nmemb * size <= SIZE_MAX */
+  if (size > SIZE_MAX / nmemb)
+    abort();
+  
+  p = realloc(ptr, nmemb * size);
+
   if (!p)
     abort();
 
 #if 0
-  fprintf(stderr, "%p = realloc(%p, %ld)\n", p, ptr, (long) size);
+  fprintf(stderr, "%p = realloc(%p, %lu * %lu)\n", p, ptr,
+	  (unsigned long) nmemb, (unsigned long) size);
 #endif
 
   return p;
@@ -218,291 +190,6 @@ static void checked_free(void *ptr)
 
 
 
-/*
- * The next functions implement the arithmedic encoder and decoder
- * required for JBIG. The same algorithm is also used in the arithmetic
- * variant of JPEG.
- */
-
-#ifdef DEBUG
-static long encoded_pixels = 0;
-#endif
-
-ARITH void arith_encode_init(struct jbg_arenc_state *s, int reuse_st)
-{
-  int i;
-  
-  if (!reuse_st)
-    for (i = 0; i < 4096; s->st[i++] = 0);
-  s->c = 0;
-  s->a = 0x10000L;
-  s->sc = 0;
-  s->ct = 11;
-  s->buffer = -1;    /* empty */
-  
-  return;
-}
-
-
-ARITH void arith_encode_flush(struct jbg_arenc_state *s)
-{
-  unsigned long temp;
-
-#ifdef DEBUG
-  fprintf(stderr, "  encoded pixels = %ld, a = %05lx, c = %08lx\n",
-	  encoded_pixels, s->a, s->c);
-#endif
-
-  /* find the s->c in the coding interval with the largest
-   * number of trailing zero bits */
-  if ((temp = (s->a - 1 + s->c) & 0xffff0000L) < s->c)
-    s->c = temp + 0x8000;
-  else
-    s->c = temp;
-  /* send remaining bytes to output */
-  s->c <<= s->ct;
-  if (s->c & 0xf8000000L) {
-    /* one final overflow has to be handled */
-    if (s->buffer >= 0) {
-      s->byte_out(s->buffer + 1, s->file);
-      if (s->buffer + 1 == MARKER_ESC)
-	s->byte_out(MARKER_STUFF, s->file);
-    }
-    /* output 0x00 bytes only when more non-0x00 will follow */
-    if (s->c & 0x7fff800L)
-      for (; s->sc; --s->sc)
-	s->byte_out(0x00, s->file);
-  } else {
-    if (s->buffer >= 0)
-      s->byte_out(s->buffer, s->file); 
-    /* T.82 figure 30 says buffer+1 for the above line! Typo? */
-    for (; s->sc; --s->sc) {
-      s->byte_out(0xff, s->file);
-      s->byte_out(MARKER_STUFF, s->file);
-    }
-  }
-  /* output final bytes only if they are not 0x00 */
-  if (s->c & 0x7fff800L) {
-    s->byte_out((s->c >> 19) & 0xff, s->file);
-    if (((s->c >> 19) & 0xff) == MARKER_ESC)
-      s->byte_out(MARKER_STUFF, s->file);
-    if (s->c & 0x7f800L) {
-      s->byte_out((s->c >> 11) & 0xff, s->file);
-      if (((s->c >> 11) & 0xff) == MARKER_ESC)
-	s->byte_out(MARKER_STUFF, s->file);
-    }
-  }
-
-  return;
-}
-
-
-ARITH_INL void arith_encode(struct jbg_arenc_state *s, int cx, int pix) 
-{
-  extern short jbg_lsz[];
-  extern unsigned char jbg_nmps[], jbg_nlps[];
-  register unsigned lsz, ss;
-  register unsigned char *st;
-  long temp;
-
-#ifdef DEBUG
-  ++encoded_pixels;
-#endif
-
-  assert(cx >= 0 && cx < 4096);
-  st = s->st + cx;
-  ss = *st & 0x7f;
-  assert(ss < 113);
-  lsz = jbg_lsz[ss];
-
-#if 0
-  fprintf(stderr, "pix = %d, cx = %d, mps = %d, st = %3d, lsz = 0x%04x, "
-	  "a = 0x%05lx, c = 0x%08lx, ct = %2d, buf = 0x%02x\n",
-	  pix, cx, !!(s->st[cx] & 0x80), ss, lsz, s->a, s->c, s->ct,
-	  s->buffer);
-#endif
-
-  if (((pix << 7) ^ s->st[cx]) & 0x80) {
-    /* encode the less probable symbol */
-    if ((s->a -= lsz) >= lsz) {
-      /* If the interval size (lsz) for the less probable symbol (LPS)
-       * is larger than the interval size for the MPS, then exchange
-       * the two symbols for coding efficiency, otherwise code the LPS
-       * as usual: */
-      s->c += s->a;
-      s->a = lsz;
-    }
-    /* Check whether MPS/LPS exchange is necessary
-     * and chose next probability estimator status */
-    *st &= 0x80;
-    *st ^= jbg_nlps[ss];
-  } else {
-    /* encode the more probable symbol */
-    if ((s->a -= lsz) & 0xffff8000L)
-      return;   /* A >= 0x8000 -> ready, no renormalization required */
-    if (s->a < lsz) {
-      /* If the interval size (lsz) for the less probable symbol (LPS)
-       * is larger than the interval size for the MPS, then exchange
-       * the two symbols for coding efficiency: */
-      s->c += s->a;
-      s->a = lsz;
-    }
-    /* chose next probability estimator status */
-    *st &= 0x80;
-    *st |= jbg_nmps[ss];
-  }
-
-  /* renormalization of coding interval */
-  do {
-    s->a <<= 1;
-    s->c <<= 1;
-    --s->ct;
-    if (s->ct == 0) {
-      /* another byte is ready for output */
-      temp = s->c >> 19;
-      if (temp & 0xffffff00L) {
-	/* handle overflow over all buffered 0xff bytes */
-	if (s->buffer >= 0) {
-	  ++s->buffer;
-	  s->byte_out(s->buffer, s->file);
-	  if (s->buffer == MARKER_ESC)
-	    s->byte_out(MARKER_STUFF, s->file);
-	}
-	for (; s->sc; --s->sc)
-	  s->byte_out(0x00, s->file);
-	s->buffer = temp & 0xff;  /* new output byte, might overflow later */
-	assert(s->buffer != 0xff);
-	/* can s->buffer really never become 0xff here? */
-      } else if (temp == 0xff) {
-	/* buffer 0xff byte (which might overflow later) */
-	++s->sc;
-      } else {
-	/* output all buffered 0xff bytes, they will not overflow any more */
-	if (s->buffer >= 0)
-	  s->byte_out(s->buffer, s->file);
-	for (; s->sc; --s->sc) {
-	  s->byte_out(0xff, s->file);
-	  s->byte_out(MARKER_STUFF, s->file);
-	}
-	s->buffer = temp;   /* buffer new output byte (can still overflow) */
-      }
-      s->c &= 0x7ffffL;
-      s->ct = 8;
-    }
-  } while (s->a < 0x8000);
- 
-  return;
-}
-
-
-ARITH void arith_decode_init(struct jbg_ardec_state *s, int reuse_st)
-{
-  int i;
-  
-  if (!reuse_st)
-    for (i = 0; i < 4096; s->st[i++] = 0);
-  s->c = 0;
-  s->a = 1;
-  s->ct = 0;
-  s->result = JBG_OK;
-  s->startup = 1;
-  return;
-}
-
-
-ARITH_INL int arith_decode(struct jbg_ardec_state *s, int cx)
-{
-  extern short jbg_lsz[];
-  extern unsigned char jbg_nmps[], jbg_nlps[];
-  register unsigned lsz, ss;
-  register unsigned char *st;
-  int pix;
-
-  /* renormalization */
-  while (s->a < 0x8000 || s->startup) {
-    if (s->ct < 1 && s->result != JBG_READY) {
-      /* first we have to move a new byte into s->c */
-      if (s->pscd_ptr >= s->pscd_end) {
-	s->result = JBG_MORE;
-	return -1;
-      }
-      if (*s->pscd_ptr == 0xff) 
-	if (s->pscd_ptr + 1 >= s->pscd_end) {
-	  s->result = JBG_MARKER;
-	  return -1;
-	} else {
-	  if (*(s->pscd_ptr + 1) == MARKER_STUFF) {
-	    s->c |= 0xffL << (8 - s->ct);
-	    s->ct += 8;
-	    s->pscd_ptr += 2;
-	    s->result = JBG_OK;
-	  } else
-	    s->result = JBG_READY;
-	}
-      else {
-	s->c |= (long)*(s->pscd_ptr++) << (8 - s->ct);
-	s->ct += 8;
-	s->result = JBG_OK;
-      }
-    }
-    s->c <<= 1;
-    s->a <<= 1;
-    --s->ct;
-    if (s->a == 0x10000L)
-      s->startup = 0;
-  }
-
-  st = s->st + cx;
-  ss = *st & 0x7f;
-  assert(ss < 113);
-  lsz = jbg_lsz[ss];
-
-#if 0
-  fprintf(stderr, "cx = %d, mps = %d, st = %3d, lsz = 0x%04x, a = 0x%05lx, "
-	  "c = 0x%08lx, ct = %2d\n",
-	  cx, !!(s->st[cx] & 0x80), ss, lsz, s->a, s->c, s->ct);
-#endif
-
-  if ((s->c >> 16) < (s->a -= lsz))
-    if (s->a & 0xffff8000L)
-      return *st >> 7;
-    else {
-      /* MPS_EXCHANGE */
-      if (s->a < lsz) {
-	pix = 1 - (*st >> 7);
-	/* Check whether MPS/LPS exchange is necessary
-	 * and chose next probability estimator status */
-	*st &= 0x80;
-	*st ^= jbg_nlps[ss];
-      } else {
-	pix = *st >> 7;
-	*st &= 0x80;
-	*st |= jbg_nmps[ss];
-      }
-    }
-  else {
-    /* LPS_EXCHANGE */
-    if (s->a < lsz) {
-      s->c -= s->a << 16;
-      s->a = lsz;
-      pix = *st >> 7;
-      *st &= 0x80;
-      *st |= jbg_nmps[ss];
-    } else {
-      s->c -= s->a << 16;
-      s->a = lsz;
-      pix = 1 - (*st >> 7);
-      /* Check whether MPS/LPS exchange is necessary
-       * and chose next probability estimator status */
-      *st &= 0x80;
-      *st ^= jbg_nlps[ss];
-    }
-  }
-
-  return pix;
-}
-
-
 
 /*
  * Memory management for buffers which are used for temporarily
@@ -532,7 +219,7 @@ static struct jbg_buf *jbg_buf_init(struct jbg_buf **free_list)
     *free_list = new_block->next;
   } else {
     /* request a new memory block */
-    new_block = (struct jbg_buf *) checked_malloc(sizeof(struct jbg_buf));
+    new_block = (struct jbg_buf *) checked_malloc(1, sizeof(struct jbg_buf));
   }
   new_block->len = 0;
   new_block->next = NULL;
@@ -668,7 +355,8 @@ static void jbg_buf_output(struct jbg_buf **head,
 
 
 /*
- * Calculate y = ceil(x/2) applied n times. This function is used to
+ * Calculate y = ceil(x/2) applied n times, which is equivalent to
+ * y = ceil(x/(2^n)). This function is used to
  * determine the number of pixels per row or column after n resolution
  * reductions. E.g. X[d-1] = jbg_ceil_half(X[d], 1) and X[0] =
  * jbg_ceil_half(X[d], d) as defined in clause 6.2.3 of T.82.
@@ -677,12 +365,405 @@ unsigned long jbg_ceil_half(unsigned long x, int n)
 {
   unsigned long mask;
   
+  assert(n >= 0 && n < 32);
   mask = (1UL << n) - 1;     /* the lowest n bits are 1 here */
   return (x >> n) + ((mask & x) != 0);
 }
 
 
 /*
+ * Set L0 (the number of lines in a stripe at lowest resolution)
+ * to a default value, such that there are about 35 stripes, as
+ * suggested in Annex C of ITU-T T.82, without exceeding the
+ * limit 128/2^D suggested in Annex A.
+ */
+static void jbg_set_default_l0(struct jbg_enc_state *s)
+{
+  s->l0 = jbg_ceil_half(s->yd, s->d) / 35;   /* 35 stripes/image */
+  while ((s->l0 << s->d) > 128)              /* but <= 128 lines/stripe */
+    --s->l0;
+  if (s->l0 < 2) s->l0 = 2;
+}
+
+
+/*
+ * Calculate the number of stripes, as defined in clause 6.2.3 of T.82.
+ */
+static unsigned long jbg_stripes(unsigned long l0, unsigned long yd,
+			  unsigned long d)
+{
+  unsigned long y0 = jbg_ceil_half(yd, d);
+
+  return y0 / l0 + (y0 % l0 != 0);
+}
+
+
+/*
+ * Resolution reduction table given by ITU-T T.82 Table 17
+ */
+
+static char jbg_resred[4096] = {
+  0,0,0,1,0,0,0,1,0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
+  0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,1,0,1,0,0,1,1,1,0,1,1,
+  0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,1,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
+  1,0,0,1,0,0,1,1,0,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,0,0,1,0,0,0,1,0,0,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,0,1,1,1,0,
+  0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,1,0,0,0,1,0,1,1,0,1,0,1,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
+  1,1,1,0,1,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
+  1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,
+  0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,1,1,0,1,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,0,1,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,1,1,1,1,0,1,1,
+  1,0,0,1,0,0,1,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,
+  0,0,1,0,1,1,1,1,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,
+  0,0,0,0,1,0,0,1,0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,
+  0,0,1,0,0,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1,
+  0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,1,0,0,1,0,0,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,1,0,1,0,1,1,0,0,0,1,0,0,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,0,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,
+  0,0,1,0,0,1,1,1,0,0,0,0,1,0,0,1,0,0,0,1,1,1,1,0,1,0,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,1,0,
+  0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,1,1,1,0,1,1,1,
+  0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,1,1,0,1,0,0,0,1,1,0,1,0,0,0,0,1,1,1,1,0,0,1,1,1,0,1,1,0,0,1,1,
+  0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,1,0,0,0,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,1,0,0,1,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
+  0,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,1,0,1,1,
+  0,0,0,1,0,0,0,1,0,0,1,0,0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,1,0,1,1,0,1,1,1,0,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,1,0,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,1,0,1,1,
+  1,0,1,0,1,0,0,1,1,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,0,1,1,0,1,1,1,
+  0,0,1,0,0,0,0,1,0,0,0,0,0,0,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,0,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,1,0,1,0,1,0,1,1,0,1,0,1,0,0,0,1,1,1,1,1,1,1,1,1,
+  1,1,1,0,1,0,0,0,1,1,0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
+  1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,1,0,1,1,
+  0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,1,0,0,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,1,0,1,0,0,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,
+  1,0,0,0,1,0,0,0,0,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,1,1,1,0,1,1,0,
+  0,0,1,1,1,1,1,1,0,0,0,0,1,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
+  0,0,1,0,1,0,1,1,0,0,1,0,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,
+  0,0,1,0,1,0,1,1,0,1,1,1,1,1,1,1,0,0,1,1,1,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1,
+  0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,
+  0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,1,0,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,
+  0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,1,
+  0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,1,
+  1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,
+  0,0,1,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,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,1,0,0,1,1,
+  0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,
+  0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,
+  0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,1,
+  0,0,0,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,1,1,0,0,0,1,0,1,1,1,0,1,1,1
+};
+
+/*
+ * Deterministic prediction tables given by ITU-T T.82 tables
+ * 19 to 22. The table below is organized differently, the
+ * index bits are permutated for higher efficiency.
+ */
+
+static char jbg_dptable[256 + 512 + 2048 + 4096] = {
+  /* phase 0: offset=0 */
+  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,0,2,2,2,2,2,2,2,
+  0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,0,2,0,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  /* phase 1: offset=256 */
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,0,2,0,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,2,1,2,1,2,2,2,2,1,1,1,1,2,0,2,0,2,2,2,2,0,2,0,2,2,2,2,2,
+  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,0,2,2,2,2,2,2,2,
+  0,2,0,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,0,0,2,2,2,2,2,0,0,2,2,2,2,2,
+  0,2,2,2,2,1,2,1,2,2,2,2,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,
+  1,2,1,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,1,1,2,2,2,2,2,0,2,2,2,2,2,2,
+  2,2,2,2,2,0,2,0,2,2,2,2,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2,2,2,2,2,2,
+  0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,2,0,2,0,2,2,2,2,2,
+  2,2,2,2,2,1,1,1,2,2,2,2,1,1,1,1,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,0,1,2,0,2,0,2,2,2,2,2,0,2,0,2,2,2,2,1,
+  0,2,0,2,2,1,2,1,2,2,2,2,1,1,1,1,0,0,0,0,2,2,2,2,0,2,0,2,2,2,2,1,
+  2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,0,0,0,2,2,2,2,2,
+  2,2,2,2,2,1,2,1,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,1,2,1,2,2,2,2,1,
+  2,2,2,2,2,2,2,2,0,2,0,2,2,1,2,2,2,2,2,2,2,2,2,2,0,0,0,2,2,2,2,2,
+  /* phase 2: offset=768 */
+  2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,1,1,1,1,
+  0,2,2,2,2,1,2,1,2,2,2,2,1,2,1,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
+  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,1,2,1,2,2,2,2,2,1,1,1,
+  2,0,2,2,2,1,2,1,0,2,2,2,1,2,1,2,2,2,2,0,2,2,2,2,0,2,0,2,2,2,2,2,
+  0,2,0,0,1,1,1,1,2,2,2,2,1,1,1,1,0,2,0,2,1,1,1,1,2,2,2,2,1,1,1,1,
+  2,2,0,2,2,2,1,2,2,2,2,2,1,2,1,2,2,2,0,2,2,1,2,1,0,2,0,2,1,1,1,1,
+  2,0,0,2,2,2,2,2,0,2,0,2,2,0,2,0,2,0,2,0,2,2,2,1,2,2,0,2,1,1,2,1,
+  2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,1,1,1,1,
+  0,0,0,0,2,2,2,2,0,0,0,0,2,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,0,2,2,2,2,1,0,2,2,2,1,1,1,1,2,0,2,2,2,2,2,2,0,2,0,2,2,1,2,1,
+  2,0,2,0,2,2,2,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,
+  0,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  2,2,0,2,2,2,2,2,2,2,2,2,2,2,0,2,2,0,0,2,2,1,2,1,0,2,2,2,1,1,1,1,
+  2,2,2,0,2,2,2,2,2,2,0,2,2,0,2,0,2,1,2,2,2,2,2,2,1,2,1,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,1,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,1,1,1,2,2,2,2,1,1,1,1,
+  2,2,2,1,2,2,2,2,2,2,1,2,0,0,0,0,2,2,0,2,2,1,2,2,2,2,2,2,1,1,1,1,
+  2,0,0,0,2,2,2,2,0,2,2,2,2,2,2,0,2,2,2,0,2,2,2,2,2,0,0,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,0,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,1,
+  0,2,0,2,2,1,1,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,
+  2,0,2,0,2,1,2,1,0,2,0,2,2,2,1,2,2,0,2,0,2,2,2,2,0,2,0,2,2,2,1,2,
+  2,2,2,0,2,2,2,2,2,2,0,2,2,2,2,2,2,2,1,2,2,2,2,2,2,0,1,2,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  0,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,2,1,2,1,0,2,2,2,1,1,1,1,
+  2,0,2,0,2,1,2,2,0,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,1,2,2,
+  2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,0,2,2,2,2,0,0,0,0,2,1,2,1,
+  2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,0,0,2,2,2,1,2,2,2,
+  0,0,2,0,2,2,2,2,0,2,0,2,2,0,2,0,1,1,1,2,2,2,2,2,2,2,2,2,2,1,1,1,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,1,
+  2,2,0,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,1,1,1,1,
+  0,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  2,0,0,2,2,2,2,2,0,2,0,2,2,2,2,2,1,0,1,2,2,2,2,1,0,2,2,2,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,2,0,2,2,0,2,0,2,1,2,2,2,2,2,2,2,2,0,2,2,1,2,2,
+  0,2,0,0,1,1,1,1,0,2,2,2,1,1,1,1,2,2,2,2,2,2,2,2,2,0,2,2,1,2,1,1,
+  2,2,0,2,2,1,2,2,2,2,2,2,1,2,2,2,2,0,2,2,2,2,2,2,0,2,0,2,1,2,1,1,
+  2,0,2,0,2,2,2,2,0,2,0,2,2,1,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,2,0,2,0,2,2,2,2,0,0,0,0,2,2,2,2,2,1,1,2,2,2,2,2,1,2,2,2,
+  2,0,2,2,2,1,2,1,0,2,2,2,2,2,1,2,2,0,2,0,2,2,2,2,0,2,0,2,2,1,2,2,
+  0,2,0,0,2,2,2,2,1,2,2,2,2,2,2,0,2,1,2,2,2,2,2,2,1,2,2,2,2,2,2,2,
+  0,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,1,0,2,2,
+  0,0,0,2,2,1,1,1,2,2,2,2,1,2,2,2,2,0,2,0,2,2,2,1,2,2,2,2,1,2,1,2,
+  0,0,0,0,2,2,2,2,2,2,0,2,2,1,2,2,2,1,2,1,2,2,2,2,1,2,1,2,0,2,2,2,
+  2,0,2,0,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  0,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,1,2,2,2,2,2,0,2,2,1,2,2,0,0,0,2,2,2,2,2,1,2,2,0,2,2,2,1,2,1,2,
+  2,0,2,0,2,2,2,2,0,2,0,2,2,1,2,2,0,2,0,0,2,2,2,2,2,2,2,2,2,1,2,2,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,0,2,0,2,2,2,1,
+  1,2,0,2,2,1,2,1,2,2,2,2,1,2,2,2,2,0,2,0,2,2,2,2,2,0,2,2,1,1,1,1,
+  0,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,2,1,2,1,
+  2,2,0,0,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,
+  2,2,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,2,
+  2,0,2,0,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,1,0,2,0,2,2,2,1,2,
+  2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  2,0,2,0,2,2,2,2,2,0,2,0,2,2,2,2,2,0,2,0,2,2,2,2,0,0,0,0,2,1,2,1,
+  2,2,2,2,2,1,2,1,0,2,0,2,2,2,2,2,2,0,2,0,2,2,2,2,0,2,0,2,2,2,2,1,
+  2,0,2,0,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,0,
+  2,0,2,0,2,2,2,1,2,2,2,0,2,2,2,1,2,0,2,0,2,2,2,2,0,0,0,2,2,2,2,1,
+  2,0,2,0,2,2,2,2,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,2,
+  /* phase 3: offset=2816 */
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,0,2,2,2,1,2,0,2,2,2,1,2,2,2,2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,
+  2,2,2,1,2,2,2,0,1,1,1,1,0,0,0,0,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,0,0,0,0,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,
+  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,2,0,2,0,2,1,2,1,
+  2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,
+  2,0,2,2,2,1,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,0,0,2,0,1,1,2,1,
+  2,2,2,0,2,2,2,1,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
+  0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,
+  2,0,0,2,2,1,1,2,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,1,1,1,2,0,0,0,
+  2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,2,2,2,0,2,2,2,1,2,0,2,0,2,1,2,1,
+  2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
+  2,2,2,2,2,2,2,2,2,0,0,0,2,1,1,1,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,2,0,2,0,2,1,2,1,0,0,2,0,1,1,2,1,
+  2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,0,0,0,2,1,1,1,
+  2,2,2,1,2,2,2,0,2,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,
+  2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,
+  2,0,2,2,2,1,2,2,0,0,2,0,1,1,2,1,2,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,
+  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,0,0,0,0,1,1,1,1,
+  2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,2,1,0,2,2,0,1,2,
+  2,2,2,1,2,2,2,0,2,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,
+  2,1,2,1,2,0,2,0,1,2,1,1,0,2,0,0,0,0,2,1,1,1,2,0,0,0,0,0,1,1,1,1,
+  2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,0,2,1,2,1,2,0,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,0,2,2,2,1,2,2,2,0,0,2,2,1,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,
+  2,0,2,0,2,1,2,1,0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,0,0,0,0,1,1,1,1,
+  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,0,0,0,2,1,1,1,
+  2,2,2,0,2,2,2,1,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  2,0,2,2,2,1,2,2,2,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,1,2,1,2,0,2,0,1,2,1,1,0,2,0,0,2,0,2,2,2,1,2,2,0,2,1,2,1,2,0,2,
+  2,2,2,1,2,2,2,0,2,2,1,2,2,2,0,2,2,1,2,2,2,0,2,2,2,2,0,2,2,2,1,2,
+  0,0,2,0,1,1,2,1,0,0,1,0,1,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,0,2,2,2,1,1,2,2,2,0,2,2,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,0,0,2,2,1,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,
+  2,0,0,0,2,1,1,1,0,0,0,0,1,1,1,1,2,2,2,1,2,2,2,0,2,1,2,1,2,0,2,0,
+  2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,0,2,0,0,1,2,1,1,2,0,0,0,2,1,1,1,
+  2,2,2,2,2,2,2,2,2,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,1,2,1,2,0,2,0,2,0,2,2,2,1,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,1,1,1,2,0,0,0,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,0,0,1,2,1,1,
+  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,
+  2,1,2,1,2,0,2,0,2,1,2,2,2,0,2,2,2,2,2,0,2,2,2,1,2,0,2,0,2,1,2,1,
+  2,0,2,0,2,1,2,1,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,0,1,0,0,1,0,1,1,
+  2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,2,2,1,2,2,2,0,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,1,2,2,1,0,2,0,2,2,2,1,2,2,2,
+  2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,
+  2,0,2,0,2,1,2,1,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  0,2,0,0,1,2,1,1,2,0,0,0,2,1,1,1,2,2,2,2,2,2,2,2,1,0,1,2,0,1,0,2,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,1,2,2,2,0,2,2,1,1,2,2,0,0,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,1,2,1,2,0,2,0,2,1,2,2,2,0,2,2,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,1,2,2,2,0,2,2,2,
+  2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,2,
+  0,0,0,0,1,1,1,1,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,2,0,2,2,2,1,2,
+  2,0,2,0,2,1,2,1,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,
+  0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,1,2,2,2,0,1,1,2,1,0,0,2,0,2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,2,2,0,2,2,2,1,2,
+  2,0,2,0,2,1,2,1,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,1,2,2,2,0,2,2,
+  0,2,0,0,1,2,1,1,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,0,0,0,2,1,1,1,2,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,0,0,2,1,1,1,2,0,0,2,2,2,1,2,2,2,
+  2,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,2,0,0,1,2,1,1,
+  0,0,2,2,1,1,2,2,0,2,1,2,1,2,0,2,2,1,2,1,2,0,2,0,1,2,1,2,0,2,0,2,
+  2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
+  2,2,0,0,2,2,1,1,2,2,0,0,2,2,1,1,2,2,2,2,2,2,2,2,2,2,0,0,2,2,1,1,
+  2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,0,2,0,0,1,2,1,1,
+  2,2,2,0,2,2,2,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,1,1,1,2,0,0,0,2,
+  2,2,2,2,2,2,2,2,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,
+  2,0,2,0,2,1,2,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,0,0,0,2,1,1,1,
+  2,0,2,2,2,1,2,2,0,2,2,2,1,2,2,2,2,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,
+  2,0,2,0,2,1,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,0,2,0,2,1,2,1,2,1,2,0,2,0,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,0,2,0,2,1,2,1,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,1,2,1,2,0,2,0,2,2,1,2,1,2,0,2,0,2,2,2,2,2,2,2,2,
+  2,0,2,1,2,1,2,0,0,2,1,2,1,2,0,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,0,2,0,2,1,2,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,0,2,0,2,1,2,1,
+  2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,1,2,1,2,0,2,0,1,1,1,2,0,0,0,2,2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,
+  2,0,2,0,2,1,2,1,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,0,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+};
+
+
+/*
  * Initialize the status struct for the encoder.
  */
 void jbg_enc_init(struct jbg_enc_state *s, unsigned long x, unsigned long y,
@@ -693,12 +774,13 @@ void jbg_enc_init(struct jbg_enc_state *s, unsigned long x, unsigned long y,
 {
   unsigned long l, lx;
   int i;
-  size_t bufsize;
-
-  extern char jbg_resred[], jbg_dptable[];
 
+  assert(x > 0 && y > 0 && planes > 0 && planes < 256);
   s->xd = x;
   s->yd = y;
+  s->yd1 = y; /* This is the hight initially announced in BIH. To provoke
+                 generation of NEWLEN for T.85 compatibility tests,
+                 overwrite with new value s->yd1 > s->yd  */
   s->planes = planes;
   s->data_out = data_out;
   s->file = file;
@@ -706,33 +788,32 @@ void jbg_enc_init(struct jbg_enc_state *s, unsigned long x, unsigned long y,
   s->d = 0;
   s->dl = 0;
   s->dh = s->d;
-  s->l0 = jbg_ceil_half(s->yd, s->d) / 35;   /* 35 stripes/image */
-  while ((s->l0 << s->d) > 128)              /* but <= 128 lines/stripe */
-    --s->l0;
-  if (s->l0 < 2) s->l0 = 2;
+  jbg_set_default_l0(s);
   s->mx = 8;
   s->my = 0;
   s->order = JBG_ILEAVE | JBG_SMID;
   s->options = JBG_TPBON | JBG_TPDON | JBG_DPON;
+  s->comment = NULL;
   s->dppriv = jbg_dptable;
   s->res_tab = jbg_resred;
   
-  s->highres = checked_malloc(planes * sizeof(int));
+  s->highres = (int *) checked_malloc(planes, sizeof(int));
   s->lhp[0] = p;
-  s->lhp[1] = checked_malloc(planes * sizeof(unsigned char *));
-  bufsize = ((jbg_ceil_half(x, 1) + 7) / 8) * jbg_ceil_half(y, 1);
+  s->lhp[1] = (unsigned char **)
+    checked_malloc(planes, sizeof(unsigned char *));
   for (i = 0; i < planes; i++) {
     s->highres[i] = 0;
-    s->lhp[1][i] = checked_malloc(sizeof(unsigned char) * bufsize);
+    s->lhp[1][i] = (unsigned char *)
+      checked_malloc(jbg_ceil_half(y, 1), jbg_ceil_half(x, 1+3));
   }
   
   s->free_list = NULL;
   s->s = (struct jbg_arenc_state *) 
-    checked_malloc(s->planes * sizeof(struct jbg_arenc_state));
-  s->tx = (int *) checked_malloc(s->planes * sizeof(int));
+    checked_malloc(s->planes, sizeof(struct jbg_arenc_state));
+  s->tx = (int *) checked_malloc(s->planes, sizeof(int));
   lx = jbg_ceil_half(x, 1);
-  s->tp = (char *) checked_malloc(lx * sizeof(char));
-  for (l = 0; l < lx; s->tp[l++] = 2);
+  s->tp = (char *) checked_malloc(lx, sizeof(char));
+  for (l = 0; l < lx; s->tp[l++] = 2) ;
   s->sde = NULL;
 
   return;
@@ -759,12 +840,7 @@ int jbg_enc_lrlmax(struct jbg_enc_state *s, unsigned long x,
       break;
   s->dl = 0;
   s->dh = s->d;
-
-  s->l0 = jbg_ceil_half(s->yd, s->d) / 35;  /* 35 stripes/image */
-  while ((s->l0 << s->d) > 128)             /* but <= 128 lines/stripe */
-    --s->l0;
-  if (s->l0 < 2) s->l0 = 2;
-
+  jbg_set_default_l0(s);
   return s->d;
 }
 
@@ -776,17 +852,12 @@ int jbg_enc_lrlmax(struct jbg_enc_state *s, unsigned long x,
  */
 void jbg_enc_layers(struct jbg_enc_state *s, int d)
 {
-  if (d < 0 || d > 255)
+  if (d < 0 || d > 31)
     return;
   s->d  = d;
   s->dl = 0;
   s->dh = s->d;
-
-  s->l0 = jbg_ceil_half(s->yd, s->d) / 35;  /* 35 stripes/image */
-  while ((s->l0 << s->d) > 128)             /* but <= 128 lines/stripe */
-    --s->l0;
-  if (s->l0 < 2) s->l0 = 2;
-
+  jbg_set_default_l0(s);
   return;
 }
 
@@ -813,11 +884,11 @@ int jbg_enc_lrange(struct jbg_enc_state *s, int dl, int dh)
  * the number of layer 0 lines per stripes.
  */
 void jbg_enc_options(struct jbg_enc_state *s, int order, int options,
-		     long l0, int mx, int my)
+		     unsigned long l0, int mx, int my)
 {
   if (order >= 0 && order <= 0x0f) s->order = order;
   if (options >= 0) s->options = options;
-  if (l0 >= 0) s->l0 = l0;
+  if (l0 > 0) s->l0 = l0;
   if (mx >= 0 && mx < 128) s->mx = mx;
   if (my >= 0 && my < 256) s->my = my;
 
@@ -838,13 +909,17 @@ static void encode_sde(struct jbg_enc_state *s,
   unsigned long line_h0 = 0, line_h1 = 0;
   unsigned long line_h2, line_h3, line_l1, line_l2, line_l3;
   struct jbg_arenc_state *se;
-  unsigned long i, j, y;
-  unsigned t;
+  unsigned long y;  /* current line number in highres image */
+  unsigned long i;  /* current line number within highres stripe */
+  unsigned long j;  /* current column number in highres image */
+  long o;
+  unsigned a, p, t;
   int ltp, ltp_old, cx;
   unsigned long c_all, c[MX_MAX + 1], cmin, cmax, clmin, clmax;
   int tmax, at_determined;
   int new_tx;
   long new_tx_line = -1;
+  int reset;
   struct jbg_buf *new_jbg_buf;
 
 #ifdef DEBUG
@@ -876,16 +951,19 @@ static void encode_sde(struct jbg_enc_state *s,
   lx = jbg_ceil_half(hx, 1);
   ly = jbg_ceil_half(hy, 1);
   /* bytes per line in highres and lowres image */
-  hbpl = (hx + 7) / 8;
-  lbpl = (lx + 7) / 8;
+  hbpl = jbg_ceil_half(hx, 3);
+  lbpl = jbg_ceil_half(lx, 3);
   /* pointer to first image byte of highres stripe */
   hp = s->lhp[s->highres[plane]][plane] + stripe * hl * hbpl;
   lp2 = s->lhp[1 - s->highres[plane]][plane] + stripe * ll * lbpl;
   lp1 = lp2 + lbpl;
   
+  /* check whether we can refer to any state of a previous stripe */
+  reset = (stripe == 0) || (s->options & JBG_SDRST);
+
   /* initialize arithmetic encoder */
   se = s->s + plane;
-  arith_encode_init(se, stripe != 0);
+  arith_encode_init(se, !reset);
   s->sde[stripe][layer][plane] = jbg_buf_init(&s->free_list);
   se->byte_out = jbg_buf_write;
   se->file = s->sde[stripe][layer][plane];
@@ -894,7 +972,7 @@ static void encode_sde(struct jbg_enc_state *s,
   c_all = 0;
   for (t = 0; t <= s->mx; t++)
     c[t] = 0;
-  if (stripe == 0)
+  if (stripe == 0)    /* the SDRST case is handled at the end */
     s->tx[plane] = 0;
   new_tx = -1;
   at_determined = 0;  /* we haven't yet decided the template move */
@@ -903,16 +981,16 @@ static void encode_sde(struct jbg_enc_state *s,
 
   /* initialize typical prediction */
   ltp = 0;
-  if (stripe == 0)
+  if (reset)
     ltp_old = 0;
   else {
     ltp_old = 1;
     p1 = hp - hbpl;
     if (y > 1) {
       q1 = p1 - hbpl;
-      while (p1 < hp && (ltp_old = (*p1++ == *q1++)) != 0);
+      while (p1 < hp && (ltp_old = (*p1++ == *q1++)) != 0) ;
     } else
-      while (p1 < hp && (ltp_old = (*p1++ == 0)) != 0);
+      while (p1 < hp && (ltp_old = (*p1++ == 0)) != 0) ;
   }
 
   if (layer == 0) {
@@ -938,7 +1016,7 @@ static void encode_sde(struct jbg_enc_state *s,
 	if (c_all - cmax < (c_all >> 3) &&
 	    cmax - c[s->tx[plane]] > c_all - cmax &&
 	    cmax - c[s->tx[plane]] > (c_all >> 4) &&
-	    /*                     ^ T.82 says here < !!! Typo ? */
+	    /*                     ^ T.82 said < here, fixed in Cor.1/25 */
 	    cmax - (c_all - c[s->tx[plane]]) > c_all - cmax &&
 	    cmax - (c_all - c[s->tx[plane]]) > (c_all >> 4) &&
 	    cmax - cmin > (c_all >> 2) &&
@@ -949,19 +1027,24 @@ static void encode_sde(struct jbg_enc_state *s,
 	    new_tx_line = i;
 	    s->tx[plane] = new_tx;
 	  }
+#ifdef DEBUG
+	  fprintf(stderr, "ATMOVE: line=%ld, tx=%d, c_all=%ld\n",
+		  i, new_tx, c_all);
+#endif
 	}
 	at_determined = 1;
       }
+      assert(s->tx[plane] >= 0); /* i.e., tx can safely be cast to unsigned */
       
       /* typical prediction */
       if (s->options & JBG_TPBON) {
 	ltp = 1;
 	p1 = hp;
-	if (y > 0) {
+	if (i > 0 || !reset) {
 	  q1 = hp - hbpl;
-	  while (q1 < hp && (ltp = (*p1++ == *q1++)) != 0);
+	  while (q1 < hp && (ltp = (*p1++ == *q1++)) != 0) ;
 	} else
-	  while (p1 < hp + hbpl && (ltp = (*p1++ == 0)) != 0);
+	  while (p1 < hp + hbpl && (ltp = (*p1++ == 0)) != 0) ;
 	arith_encode(se, (s->options & JBG_LRLTWO) ? TPB2CX : TPB3CX,
 		     ltp == ltp_old);
 #ifdef DEBUG
@@ -985,26 +1068,35 @@ static void encode_sde(struct jbg_enc_state *s,
        */
       
       line_h1 = line_h2 = line_h3 = 0;
-      if (y > 0) line_h2 = (long)*(hp - hbpl) << 8;
-      if (y > 1) line_h3 = (long)*(hp - hbpl - hbpl) << 8;
+      if (i > 0 || !reset) line_h2 = (long)*(hp - hbpl) << 8;
+      if (i > 1 || !reset) line_h3 = (long)*(hp - hbpl - hbpl) << 8;
       
       /* encode line */
       for (j = 0; j < hx; hp++) {
 	line_h1 |= *hp;
-	if (j < hbpl * 8 - 8 && y > 0) {
+	if (j < hbpl * 8 - 8 && (i > 0 || !reset)) {
 	  line_h2 |= *(hp - hbpl + 1);
-	  if (y > 1)
+	  if (i > 1 || !reset)
 	    line_h3 |= *(hp - hbpl - hbpl + 1);
 	}
 	if (s->options & JBG_LRLTWO) {
 	  /* two line template */
 	  do {
 	    line_h1 <<= 1;  line_h2 <<= 1;  line_h3 <<= 1;
-	    if (s->tx[plane])
-	      arith_encode(se, (((line_h2 >> 10) & 0x3e0) |
-				((line_h1 >> (4 + s->tx[plane])) & 0x010) |
+	    if (s->tx[plane]) {
+	      if ((unsigned) s->tx[plane] > j)
+		a = 0;
+	      else {
+		o = (j - s->tx[plane]) - (j & ~7L);
+		a = (hp[o >> 3] >> (7 - (o & 7))) & 1;
+		a <<= 4;
+	      }
+	      assert(s->tx[plane] > 23 ||
+		     a == ((line_h1 >> (4 + s->tx[plane])) & 0x010));
+	      arith_encode(se, (((line_h2 >> 10) & 0x3e0) | a |
 				((line_h1 >>  9) & 0x00f)),
 			   (line_h1 >> 8) & 1);
+	    }
 	    else
 	      arith_encode(se, (((line_h2 >> 10) & 0x3f0) |
 				((line_h1 >>  9) & 0x00f)),
@@ -1014,9 +1106,20 @@ static void encode_sde(struct jbg_enc_state *s,
 #endif
 	    /* statistics for adaptive template changes */
 	    if (!at_determined && j >= s->mx && j < hx-2) {
-	      c[0] += !(((line_h2 >> 6) ^ line_h1) & 0x100);
-	      for (t = 5; t <= s->mx; t++)
-		c[t] += !(((line_h1 >> t) ^ line_h1) & 0x100);
+	      p = (line_h1 & 0x100) != 0; /* current pixel value */
+	      c[0] += ((line_h2 & 0x4000) != 0) == p; /* default position */
+	      assert(!(((line_h2 >> 6) ^ line_h1) & 0x100) ==
+		     (((line_h2 & 0x4000) != 0) == p));
+	      for (t = 5; t <= s->mx && t <= j; t++) {
+		o = (j - t) - (j & ~7L);
+		a = (hp[o >> 3] >> (7 - (o & 7))) & 1;
+		assert(t > 23 ||
+		       (a == p) == !(((line_h1 >> t) ^ line_h1) & 0x100));
+		c[t] += a == p;
+	      }
+	      for (; t <= s->mx; t++) {
+		c[t] += 0 == p;
+	      }
 	      ++c_all;
 	    }
 	  } while (++j & 7 && j < hx);
@@ -1024,13 +1127,21 @@ static void encode_sde(struct jbg_enc_state *s,
 	  /* three line template */
 	  do {
 	    line_h1 <<= 1;  line_h2 <<= 1;  line_h3 <<= 1;
-	    if (s->tx[plane]) 
+	    if (s->tx[plane]) {
+	      if ((unsigned) s->tx[plane] > j)
+		a = 0;
+	      else {
+		o = (j - s->tx[plane]) - (j & ~7L);
+		a = (hp[o >> 3] >> (7 - (o & 7))) & 1;
+		a <<= 2;
+	      }
+	      assert(s->tx[plane] > 23 ||
+		     a == ((line_h1 >> (6 + s->tx[plane])) & 0x004));
 	      arith_encode(se, (((line_h3 >>  8) & 0x380) |
-				((line_h2 >> 12) & 0x078) |
-				((line_h1 >> (6 + s->tx[plane])) & 0x004) |
+				((line_h2 >> 12) & 0x078) | a |
 				((line_h1 >>  9) & 0x003)),
 			   (line_h1 >> 8) & 1);
-	    else
+	    } else
 	      arith_encode(se, (((line_h3 >>  8) & 0x380) |
 				((line_h2 >> 12) & 0x07c) |
 				((line_h1 >>  9) & 0x003)),
@@ -1040,9 +1151,20 @@ static void encode_sde(struct jbg_enc_state *s,
 #endif
 	    /* statistics for adaptive template changes */
 	    if (!at_determined && j >= s->mx && j < hx-2) {
-	      c[0] += !(((line_h2 >> 6) ^ line_h1) & 0x100);
-	      for (t = 3; t <= s->mx; t++)
-		c[t] += !(((line_h1 >> t) ^ line_h1) & 0x100);
+	      p = (line_h1 & 0x100) != 0; /* current pixel value */
+	      c[0] += ((line_h2 & 0x4000) != 0) == p; /* default position */
+	      assert(!(((line_h2 >> 6) ^ line_h1) & 0x100) ==
+		     (((line_h2 & 0x4000) != 0) == p));
+	      for (t = 3; t <= s->mx && t <= j; t++) {
+		o = (j - t) - (j & ~7L);
+		a = (hp[o >> 3] >> (7 - (o & 7))) & 1;
+		assert(t > 23 ||
+		       (a == p) == !(((line_h1 >> t) ^ line_h1) & 0x100));
+		c[t] += a == p;
+	      }
+	      for (; t <= s->mx; t++) {
+		c[t] += 0 == p;
+	      }
 	      ++c_all;
 	    }
 	  } while (++j & 7 && j < hx);
@@ -1073,7 +1195,7 @@ static void encode_sde(struct jbg_enc_state *s,
 	if (c_all - cmax < (c_all >> 3) &&
 	    cmax - c[s->tx[plane]] > c_all - cmax &&
 	    cmax - c[s->tx[plane]] > (c_all >> 4) &&
-	    /*                     ^ T.82 says here < !!! Typo ? */
+	    /*                     ^ T.82 said < here, fixed in Cor.1/25 */
 	    cmax - (c_all - c[s->tx[plane]]) > c_all - cmax &&
 	    cmax - (c_all - c[s->tx[plane]]) > (c_all >> 4) &&
 	    cmax - cmin > (c_all >> 2) &&
@@ -1101,7 +1223,7 @@ static void encode_sde(struct jbg_enc_state *s,
 	p0 = p1 = hp;
 	if (i < hl - 1 && y < hy - 1)
 	  p0 = hp + hbpl;
-	if (y > 1)
+	if (i > 1 || !reset)
 	  line_l3 = (long)*(q2 - lbpl) << 8;
 	else
 	  line_l3 = 0;
@@ -1110,7 +1232,7 @@ static void encode_sde(struct jbg_enc_state *s,
 	ltp = 1;
 	for (j = 0; j < lx && ltp; q1++, q2++) {
 	  if (j < lbpl * 8 - 8) {
-	    if (y > 1)
+	    if (i > 1 || !reset)
 	      line_l3 |= *(q2 - lbpl + 1);
 	    line_l2 |= *(q2 + 1);
 	    line_l1 |= *(q1 + 1);
@@ -1180,8 +1302,8 @@ static void encode_sde(struct jbg_enc_state *s,
       
 
       line_h1 = line_h2 = line_h3 = line_l1 = line_l2 = line_l3 = 0;
-      if (y > 0) line_h2 = (long)*(hp - hbpl) << 8;
-      if (y > 1) {
+      if (i > 0 || !reset) line_h2 = (long)*(hp - hbpl) << 8;
+      if (i > 1 || !reset) {
 	line_h3 = (long)*(hp - hbpl - hbpl) << 8;
 	line_l3 = (long)*(lp2 - lbpl) << 8;
       }
@@ -1191,12 +1313,12 @@ static void encode_sde(struct jbg_enc_state *s,
       /* encode line */
       for (j = 0; j < hx; lp1++, lp2++) {
 	if ((j >> 1) < lbpl * 8 - 8) {
-	  if (y > 1)
+	  if (i > 1 || !reset)
 	    line_l3 |= *(lp2 - lbpl + 1);
 	  line_l2 |= *(lp2 + 1);
 	  line_l1 |= *(lp1 + 1);
 	}
-	do {
+	do { /* ... while (j & 15 && j < hx) */
 
 	  assert(hp - (s->lhp[s->highres[plane]][plane] +
 		       (stripe * hl + i) * hbpl)
@@ -1206,15 +1328,15 @@ static void encode_sde(struct jbg_enc_state *s,
 			(stripe * ll + (i>>1)) * lbpl)
 		 == (ptrdiff_t) j >> 4);
 
-	  line_h1 |= *(hp++);
+	  line_h1 |= *hp;
 	  if (j < hbpl * 8 - 8) {
-	    if (y > 0) {
-	      line_h2 |= *(hp - hbpl);
-	      if (y > 1)
-		line_h3 |= *(hp - hbpl - hbpl);
+	    if (i > 0 || !reset) {
+	      line_h2 |= *(hp - hbpl + 1);
+	      if (i > 1 || !reset)
+		line_h3 |= *(hp - hbpl - hbpl + 1);
 	    }
 	  }
-	  do {
+	  do { /* ... while (j & 7 && j < hx) */
 	    line_l1 <<= 1;  line_l2 <<= 1;  line_l3 <<= 1;
 	    if (ltp && s->tp[j >> 1] < 2) {
 	      /* pixel are typical and have not to be encoded */
@@ -1227,7 +1349,7 @@ static void encode_sde(struct jbg_enc_state *s,
 	      j += 2;
 #endif
 	    } else
-	      do {
+	      do { /* ... while (++j & 1 && j < hx) */
 		line_h1 <<= 1;  line_h2 <<= 1;  line_h3 <<= 1;
 
 		/* deterministic prediction */
@@ -1286,12 +1408,20 @@ static void encode_sde(struct jbg_enc_state *s,
 		}
 
 		/* determine context */
-		if (s->tx[plane])
-		  cx = (((line_h1 >> 9)  & 0x003) |
-			((line_h1 >> (4 + s->tx[plane])) & 0x010) |
+		if (s->tx[plane]) {
+		  if ((unsigned) s->tx[plane] > j)
+		    a = 0;
+		  else {
+		    o = (j - s->tx[plane]) - (j & ~7L);
+		    a = (hp[o >> 3] >> (7 - (o & 7))) & 1;
+		    a <<= 4;
+		  }
+		  assert(s->tx[plane] > 23 ||
+			 a == ((line_h1 >> (4 + s->tx[plane])) & 0x010));
+		  cx = (((line_h1 >> 9)  & 0x003) | a |
 			((line_h2 >> 13) & 0x00c) |
 			((line_h3 >> 11) & 0x020));
-		else
+		} else
 		  cx = (((line_h1 >> 9)  & 0x003) |
 			((line_h2 >> 13) & 0x01c) |
 			((line_h3 >> 11) & 0x020));
@@ -1318,6 +1448,7 @@ static void encode_sde(struct jbg_enc_state *s,
 		
 	      } while (++j & 1 && j < hx);
 	  } while (j & 7 && j < hx);
+	  hp++;
 	} while (j & 15 && j < hx);
       } /* for (j = ...) */
 
@@ -1333,7 +1464,10 @@ static void encode_sde(struct jbg_enc_state *s,
   arith_encode_flush(se);
   jbg_buf_remove_zeros(s->sde[stripe][layer][plane]);
   jbg_buf_write(MARKER_ESC, s->sde[stripe][layer][plane]);
-  jbg_buf_write(MARKER_SDNORM, s->sde[stripe][layer][plane]);
+  jbg_buf_write((s->options & JBG_SDRST) ? MARKER_SDRST : MARKER_SDNORM,
+		s->sde[stripe][layer][plane]);
+  if (s->options & JBG_SDRST)
+    s->tx[plane] = 0;
 
   /* add ATMOVE */
   if (new_tx != -1) {
@@ -1381,12 +1515,18 @@ static void encode_sde(struct jbg_enc_state *s,
 static void resolution_reduction(struct jbg_enc_state *s, int plane,
 				 int higher_layer)
 {
-  unsigned long hx, hy, lx, ly, hbpl, lbpl;
+  unsigned long hl, ll, hx, hy, lx, ly, hbpl, lbpl;
   unsigned char *hp1, *hp2, *hp3, *lp;
   unsigned long line_h1, line_h2, line_h3, line_l2;
-  unsigned long i, j;
+  unsigned long y;  /* current line number in lowres image */
+  unsigned long i;  /* current line number within lowres stripe */
+  unsigned long j;  /* current column number in lowres image */
   int pix, k, l;
 
+  /* number of lines per stripe in highres image */
+  hl = s->l0 << higher_layer;
+  /* number of lines per stripe in lowres image */
+  ll = hl >> 1;
   /* number of pixels in highres image */
   hx = jbg_ceil_half(s->xd, s->d - higher_layer);
   hy = jbg_ceil_half(s->yd, s->d - higher_layer);
@@ -1394,8 +1534,8 @@ static void resolution_reduction(struct jbg_enc_state *s, int plane,
   lx = jbg_ceil_half(hx, 1);
   ly = jbg_ceil_half(hy, 1);
   /* bytes per line in highres and lowres image */
-  hbpl = (hx + 7) / 8;
-  lbpl = (lx + 7) / 8;
+  hbpl = jbg_ceil_half(hx, 3);
+  lbpl = jbg_ceil_half(lx, 3);
   /* pointers to first image bytes */
   hp2 = s->lhp[s->highres[plane]][plane];
   hp1 = hp2 + hbpl;
@@ -1424,40 +1564,44 @@ static void resolution_reduction(struct jbg_enc_state *s, int plane,
    *   76543210 76543210 76543210 76543210     line_l2
    *                            X
    */
-      
-  for (i = 0; i < ly; i++) {
-    if (2*i + 1 >= hy)
-      hp1 = hp2;
-    pix = 0;
-    line_h1 = line_h2 = line_h3 = line_l2 = 0;
-    for (j = 0; j < lbpl * 8; j += 8) {
-      *lp = 0;
-      line_l2 |= i ? lp[-lbpl] : 0;
-      for (k = 0; k < 8 && j + k < lx; k += 4) {
-	if (((j + k) >> 2) < hbpl) {
-	  line_h3 |= i ? *hp3 : 0;
-	  ++hp3;
-	  line_h2 |= *(hp2++);
-	  line_h1 |= *(hp1++);
-	}
-	for (l = 0; l < 4 && j + k + l < lx; l++) {
-	  line_h3 <<= 2;
-	  line_h2 <<= 2;
-	  line_h1 <<= 2;
-	  line_l2 <<= 1;
-	  pix = s->res_tab[((line_h1 >> 8) & 0x007) |
-			   ((line_h2 >> 5) & 0x038) |
-			   ((line_h3 >> 2) & 0x1c0) |
-			   (pix << 9) | ((line_l2 << 2) & 0xc00)];
-	  *lp = (*lp << 1) | pix;
+
+  for (y = 0; y < ly;) {
+    for (i = 0; i < ll && y < ly; i++, y++) {
+      if (2*y + 1 >= hy)
+	hp1 = hp2;
+      pix = 0;
+      line_h1 = line_h2 = line_h3 = line_l2 = 0;
+      for (j = 0; j < lbpl * 8; j += 8) {
+	*lp = 0;
+	if (i > 0 || (y > 0 && !(s->options & JBG_SDRST)))
+	  line_l2 |= *(lp-lbpl);
+	for (k = 0; k < 8 && j + k < lx; k += 4) {
+	  if (((j + k) >> 2) < hbpl) {
+	    if (i > 0 || (y > 0 && !(s->options & JBG_SDRST)))
+	      line_h3 |= *hp3;
+	    ++hp3;
+	    line_h2 |= *(hp2++);
+	    line_h1 |= *(hp1++);
+	  }
+	  for (l = 0; l < 4 && j + k + l < lx; l++) {
+	    line_h3 <<= 2;
+	    line_h2 <<= 2;
+	    line_h1 <<= 2;
+	    line_l2 <<= 1;
+	    pix = s->res_tab[((line_h1 >> 8) & 0x007) |
+			     ((line_h2 >> 5) & 0x038) |
+			     ((line_h3 >> 2) & 0x1c0) |
+			     (pix << 9) | ((line_l2 << 2) & 0xc00)];
+	    *lp = (*lp << 1) | pix;
+	  }
 	}
+	++lp;
       }
-      ++lp;
+      *(lp - 1) <<= lbpl * 8 - lx;
+      hp1 += hbpl;
+      hp2 += hbpl;
+      hp3 += hbpl;
     }
-    *(lp - 1) <<= lbpl * 8 - lx;
-    hp1 += hbpl;
-    hp2 += hbpl;
-    hp3 += hbpl;
   }
 
 #ifdef DEBUG
@@ -1482,16 +1626,15 @@ static void resolution_reduction(struct jbg_enc_state *s, int plane,
  * order to write the next SDE. It has first to generate the required
  * SDE and all SDEs which have to be encoded before this SDE can be
  * created. The problem here is that if we want to output a lower
- * resolution layer, we have to allpy the resolution reduction
- * algorithm in order to get it. As we try to safe as much memory as
+ * resolution layer, we have to apply the resolution reduction
+ * algorithm first to get it. As we try to safe as much memory as
  * possible, the resolution reduction will overwrite previous higher
  * resolution bitmaps. Consequently, we have to encode and buffer SDEs
  * which depend on higher resolution layers before we can start the
- * resolution reduction. All this logic about which SDE has to be
- * encoded before resolution reduction is allowed is handled here.
- * This approach might be a little bit more complex than alternative
- * ways to do it, but it allows us to do the encoding with the minimal
- * possible amount of temporary memory.
+ * resolution reduction. All the logic about which SDE has to be
+ * encoded before resolution reduction is allowed is handled
+ * here. This approach may be a bit more complex than alternative ways
+ * of doing it, but it minimizes the amount of temporary memory used.
  */
 static void output_sde(struct jbg_enc_state *s,
 		       unsigned long stripe, int layer, int plane)
@@ -1587,12 +1730,12 @@ void jbg_int2dppriv(unsigned char *dptable, const char *internal)
   int trans2[11] = { 1, 0, 3, 2, 10, 9, 8, 7, 6, 5, 4 };
   int trans3[12] = { 1, 0, 3, 2, 11, 10, 9, 8, 7, 6, 5, 4 };
   
-  for (i = 0; i < 1728; dptable[i++] = 0);
+  for (i = 0; i < 1728; dptable[i++] = 0) ;
 
 #define FILL_TABLE1(offset, len, trans) \
   for (i = 0; i < len; i++) { \
     k = 0; \
-    for (j = 0; j < 8; j++) \
+    for (j = 0; i >> j; j++) \
       k |= ((i >> j) & 1) << trans[j]; \
     dptable[(i + offset) >> 2] |= \
       (internal[k + offset] & 3) << ((3 - (i&3)) << 1); \
@@ -1623,7 +1766,7 @@ void jbg_dppriv2int(char *internal, const unsigned char *dptable)
 #define FILL_TABLE2(offset, len, trans) \
   for (i = 0; i < len; i++) { \
     k = 0; \
-    for (j = 0; j < 8; j++) \
+    for (j = 0; i >> j; j++) \
       k |= ((i >> j) & 1) << trans[j]; \
     internal[k + offset] = \
       (dptable[(i + offset) >> 2] >> ((3 - (i & 3)) << 1)) & 3; \
@@ -1644,20 +1787,19 @@ void jbg_dppriv2int(char *internal, const unsigned char *dptable)
  */
 void jbg_enc_out(struct jbg_enc_state *s)
 {
-  long bpl;
-  unsigned char bih[20];
+  unsigned long bpl;
+  unsigned char buf[20];
   unsigned long xd, yd, y;
   long ii[3], is[3], ie[3];    /* generic variables for the 3 nested loops */ 
   unsigned long stripe;
   int layer, plane;
   int order;
   unsigned char dpbuf[1728];
-  extern char jbg_dptable[];
 
   /* some sanity checks */
   s->order &= JBG_HITOLO | JBG_SEQ | JBG_ILEAVE | JBG_SMID;
   order = s->order & (JBG_SEQ | JBG_ILEAVE | JBG_SMID);
-  if (index[order][0] < 0)
+  if (iindex[order][0] < 0)
     s->order = order = JBG_SMID | JBG_ILEAVE;
   if (s->options & JBG_DPON && s->dppriv != jbg_dptable)
     s->options |= JBG_DPPRIV;
@@ -1669,29 +1811,65 @@ void jbg_enc_out(struct jbg_enc_state *s)
   if (s->d > 255 || s->d < 0 || s->dh > s->d || s->dh < 0 ||
       s->dl < 0 || s->dl > s->dh || s->planes < 0 || s->planes > 255)
     return;
+  /* prevent uint32 overflow: s->l0 * 2 ^ s->d < 2 ^ 32 */
+  if (s->d > 31 || (s->d != 0 && s->l0 >= (1UL << (32 - s->d))))
+    return;
+  if (s->yd1 < s->yd)
+    s->yd1 = s->yd;
+  if (s->yd1 > s->yd)
+    s->options |= JBG_VLENGTH;
 
   /* ensure correct zero padding of bitmap at the final byte of each line */
   if (s->xd & 7) {
-    bpl = (s->xd + 7) / 8;     /* bytes per line */
+    bpl = jbg_ceil_half(s->xd, 3);     /* bytes per line */
     for (plane = 0; plane < s->planes; plane++)
       for (y = 0; y < s->yd; y++)
 	s->lhp[0][plane][y * bpl + bpl - 1] &= ~((1 << (8 - (s->xd & 7))) - 1);
   }
 
+  /* prepare BIH */
+  buf[0] = s->dl;
+  buf[1] = s->dh;
+  buf[2] = s->planes;
+  buf[3] = 0;
+  xd = jbg_ceil_half(s->xd, s->d - s->dh);
+  yd = jbg_ceil_half(s->yd1, s->d - s->dh);
+  buf[4] = xd >> 24;
+  buf[5] = (xd >> 16) & 0xff;
+  buf[6] = (xd >> 8) & 0xff;
+  buf[7] = xd & 0xff;
+  buf[8] = yd >> 24;
+  buf[9] = (yd >> 16) & 0xff;
+  buf[10] = (yd >> 8) & 0xff;
+  buf[11] = yd & 0xff;
+  buf[12] = s->l0 >> 24;
+  buf[13] = (s->l0 >> 16) & 0xff;
+  buf[14] = (s->l0 >> 8) & 0xff;
+  buf[15] = s->l0 & 0xff;
+  buf[16] = s->mx;
+  buf[17] = s->my;
+  buf[18] = s->order;
+  buf[19] = s->options & 0x7f;
+
+#if 0
+  /* sanitize L0 (if it was set to 0xffffffff for T.85-style NEWLEN tests) */
+  if (s->l0 > (s->yd >> s->d))
+    s->l0 = s->yd >> s->d;
+#endif
+
   /* calculate number of stripes that will be required */
-  s->stripes = ((s->yd >> s->d) + 
-		((((1UL << s->d) - 1) & s->xd) != 0) + s->l0 - 1) / s->l0;
+  s->stripes = jbg_stripes(s->l0, s->yd, s->d);
 
   /* allocate buffers for SDE pointers */
   if (s->sde == NULL) {
     s->sde = (struct jbg_buf ****)
-      checked_malloc(s->stripes * sizeof(struct jbg_buf ***));
+      checked_malloc(s->stripes, sizeof(struct jbg_buf ***));
     for (stripe = 0; stripe < s->stripes; stripe++) {
       s->sde[stripe] = (struct jbg_buf ***)
-	checked_malloc((s->d + 1) * sizeof(struct jbg_buf **));
+	checked_malloc(s->d + 1, sizeof(struct jbg_buf **));
       for (layer = 0; layer < s->d + 1; layer++) {
 	s->sde[stripe][layer] = (struct jbg_buf **)
-	  checked_malloc(s->planes * sizeof(struct jbg_buf *));
+	  checked_malloc(s->planes, sizeof(struct jbg_buf *));
 	for (plane = 0; plane < s->planes; plane++)
 	  s->sde[stripe][layer][plane] = SDE_TODO;
       }
@@ -1699,29 +1877,7 @@ void jbg_enc_out(struct jbg_enc_state *s)
   }
 
   /* output BIH */
-  bih[0] = s->dl;
-  bih[1] = s->dh;
-  bih[2] = s->planes;
-  bih[3] = 0;
-  xd = jbg_ceil_half(s->xd, s->d - s->dh);
-  yd = jbg_ceil_half(s->yd, s->d - s->dh);
-  bih[4] = xd >> 24;
-  bih[5] = (xd >> 16) & 0xff;
-  bih[6] = (xd >> 8) & 0xff;
-  bih[7] = xd & 0xff;
-  bih[8] = yd >> 24;
-  bih[9] = (yd >> 16) & 0xff;
-  bih[10] = (yd >> 8) & 0xff;
-  bih[11] = yd & 0xff;
-  bih[12] = s->l0 >> 24;
-  bih[13] = (s->l0 >> 16) & 0xff;
-  bih[14] = (s->l0 >> 8) & 0xff;
-  bih[15] = s->l0 & 0xff;
-  bih[16] = s->mx;
-  bih[17] = s->my;
-  bih[18] = s->order;
-  bih[19] = s->options & 0x7f;
-  s->data_out(bih, 20, s->file);
+  s->data_out(buf, 20, s->file);
   if ((s->options & (JBG_DPON | JBG_DPPRIV | JBG_DPLAST)) ==
       (JBG_DPON | JBG_DPPRIV)) {
     /* write private table */
@@ -1751,27 +1907,68 @@ void jbg_enc_out(struct jbg_enc_state *s)
    * stripe depends on the option flags.
    */
 
-  /* start and end value vor each loop */
-  is[index[order][STRIPE]] = 0;
-  ie[index[order][STRIPE]] = s->stripes - 1;
-  is[index[order][LAYER]] = s->dl;
-  ie[index[order][LAYER]] = s->dh;
-  is[index[order][PLANE]] = 0;
-  ie[index[order][PLANE]] = s->planes - 1;
+  /* start and end value for each loop */
+  is[iindex[order][STRIPE]] = 0;
+  ie[iindex[order][STRIPE]] = s->stripes - 1;
+  is[iindex[order][LAYER]] = s->dl;
+  ie[iindex[order][LAYER]] = s->dh;
+  is[iindex[order][PLANE]] = 0;
+  ie[iindex[order][PLANE]] = s->planes - 1;
 
   for (ii[0] = is[0]; ii[0] <= ie[0]; ii[0]++)
     for (ii[1] = is[1]; ii[1] <= ie[1]; ii[1]++)
       for (ii[2] = is[2]; ii[2] <= ie[2]; ii[2]++) {
 	
-	stripe = ii[index[order][STRIPE]];
+	stripe = ii[iindex[order][STRIPE]];
 	if (s->order & JBG_HITOLO)
-	  layer = s->dh - (ii[index[order][LAYER]] - s->dl);
+	  layer = s->dh - (ii[iindex[order][LAYER]] - s->dl);
 	else
-	  layer = ii[index[order][LAYER]];
-	plane = ii[index[order][PLANE]];
+	  layer = ii[iindex[order][LAYER]];
+	plane = ii[iindex[order][PLANE]];
+
+	/* output comment marker segment if there is any pending */
+	if (s->comment) {
+	  buf[0] = MARKER_ESC;
+	  buf[1] = MARKER_COMMENT;
+	  buf[2] = s->comment_len >> 24;
+	  buf[3] = (s->comment_len >> 16) & 0xff;
+	  buf[4] = (s->comment_len >> 8) & 0xff;
+	  buf[5] = s->comment_len & 0xff;
+	  s->data_out(buf, 6, s->file);
+	  s->data_out(s->comment, s->comment_len, s->file);
+	  s->comment = NULL;
+	}
 
 	output_sde(s, stripe, layer, plane);
 
+	/*
+	 * When we generate a NEWLEN test case (s->yd1 > s->yd), output
+	 * NEWLEN after last stripe if we have only a single
+	 * resolution layer or plane (see ITU-T T.85 profile), otherwise
+	 * output NEWLEN before last stripe.
+	 */
+	if (s->yd1 > s->yd &&
+	    (stripe == s->stripes - 1 ||
+	     (stripe == s->stripes - 2 && 
+	      (s->dl != s->dh || s->planes > 1)))) {
+	  s->yd1 = s->yd;
+	  yd = jbg_ceil_half(s->yd, s->d - s->dh);
+	  buf[0] = MARKER_ESC;
+	  buf[1] = MARKER_NEWLEN;
+	  buf[2] = yd >> 24;
+	  buf[3] = (yd >> 16) & 0xff;
+	  buf[4] = (yd >> 8) & 0xff;
+	  buf[5] = yd & 0xff;
+	  s->data_out(buf, 6, s->file);
+#ifdef DEBUG
+	  fprintf(stderr, "NEWLEN: yd=%lu\n", yd);
+#endif
+	  if (stripe == s->stripes - 1) {
+	    buf[1] = MARKER_SDNORM;
+	    s->data_out(buf, 2, s->file);
+	  }
+	}
+
       }
 
   return;
@@ -1784,7 +1981,7 @@ void jbg_enc_free(struct jbg_enc_state *s)
   int layer, plane;
 
 #ifdef DEBUG
-  fprintf(stderr, "jbg_enc_free(%p)\n", s);
+  fprintf(stderr, "jbg_enc_free(%p)\n", (void *) s);
 #endif
 
   /* clear buffers for SDEs */
@@ -1820,23 +2017,24 @@ void jbg_enc_free(struct jbg_enc_state *s)
       checked_free(s->lhp[1][plane]);
     checked_free(s->lhp[1]);
   }
-
+  
+  /* clear buffer for index of highres image in lhp */
+  checked_free(s->highres);
+  
   return;
 }
 
 
 /*
- * Convert the error codes used by jbg_dec_in() into a string
- * written in the selected language and character set.
+ * Convert the error codes used by jbg_dec_in() into an English ASCII string
  */
-const char *jbg_strerror(int errnum, int language)
+const char *jbg_strerror(int errnum)
 {
-  if (errnum < 0 || errnum >= NEMSG)
+  errnum >>= 4;
+  if (errnum < 0 || (unsigned) errnum >= sizeof(errmsg)/sizeof(errmsg[0]))
     return "Unknown error code passed to jbg_strerror()";
-  if (language < 0 || language >= NEMSG_LANG)
-    return "Unknown language code passed to jbg_strerror()";
 
-  return errmsg[language][errnum];
+  return errmsg[errnum];
 }
 
 
@@ -1880,8 +2078,8 @@ void jbg_dec_maxsize(struct jbg_dec_state *s, unsigned long xmax,
  * Decode the new len PSDC bytes to which data points and add them to
  * the current stripe. Return the number of bytes which have actually
  * been read (this will be less than len if a marker segment was 
- * part of the data or if the final byte was 0xff were this code
- * can not determine, whether we have a marker segment.
+ * part of the data or if the final byte was 0xff, in which case
+ * this code cannot determine whether we have a marker segment).
  */
 static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 			  size_t len)
@@ -1894,13 +2092,15 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
   register unsigned long line_l1, line_l2, line_l3;
   struct jbg_ardec_state *se;
   unsigned long x;
+  long o;
+  unsigned a;
   int n;
-  int pix, cx = 0, slntp, shift, tx;
+  int pix, cx = 0, slntp, tx;
 
   /* SDE loop variables */
-  stripe = s->ii[index[s->order & 7][STRIPE]];
-  layer = s->ii[index[s->order & 7][LAYER]];
-  plane = s->ii[index[s->order & 7][PLANE]];
+  stripe = s->ii[iindex[s->order & 7][STRIPE]];
+  layer = s->ii[iindex[s->order & 7][LAYER]];
+  plane = s->ii[iindex[s->order & 7][PLANE]];
 
   /* forward data to arithmetic decoder */
   se = s->s[plane] + layer - s->dl;
@@ -1920,8 +2120,8 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
   lx = jbg_ceil_half(hx, 1);
   ly = jbg_ceil_half(hy, 1);
   /* bytes per line in highres and lowres image */
-  hbpl = (hx + 7) / 8;
-  lbpl = (lx + 7) / 8;
+  hbpl = jbg_ceil_half(hx, 3);
+  lbpl = jbg_ceil_half(lx, 3);
   /* pointer to highres and lowres image bytes */
   hp  = s->lhp[ layer    & 1][plane] + (stripe * hl + s->i) * hbpl +
     (s->x >> 3);
@@ -1938,19 +2138,18 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
   line_l3 = s->line_l3;
   x = s->x;
 
-  if (s->x == 0 && s->i == 0 &&
-      (stripe == 0 || s->reset[plane][layer - s->dl])) {
-    s->tx[plane][layer - s->dl] = s->ty[plane][layer - s->dl] = 0;
-    if (s->pseudo)
-      s->lntp[plane][layer - s->dl] = 1;
-  }
-
 #ifdef DEBUG
   if (s->x == 0 && s->i == 0 && s->pseudo)
     fprintf(stderr, "decode_pscd(%p, %p, %ld): s/d/p = %2lu/%2u/%2u\n",
-	    s, data, (long) len, stripe, layer, plane);
+	    (void *) s, (void *) data, (long) len, stripe, layer, plane);
 #endif
 
+  if (s->x == 0 && s->i == 0 &&
+      (stripe == 0 || s->reset[plane][layer - s->dl]) && s->pseudo) {
+    s->tx[plane][layer - s->dl] = s->ty[plane][layer - s->dl] = 0;
+    s->lntp[plane][layer - s->dl] = 1;
+  }
+
   if (layer == 0) {
 
     /*
@@ -1960,7 +2159,7 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
     for (; s->i < hl && y < hy; s->i++, y++) {
 
       /* adaptive template changes */
-      if (x == 0)
+      if (x == 0 && s->pseudo)
 	for (n = 0; n < s->at_moves; n++)
 	  if (s->at_line[n] == s->i) {
 	    s->tx[plane][layer - s->dl] = s->at_tx[n];
@@ -1971,19 +2170,16 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 #endif
 	  }
       tx = s->tx[plane][layer - s->dl];
-      shift =  tx - ((s->options & JBG_LRLTWO) ? 5 : 3);
+      assert(tx >= 0); /* i.e., tx can safely be cast to unsigned */
 
       /* typical prediction */
       if (s->options & JBG_TPBON && s->pseudo) {
 	slntp = arith_decode(se, (s->options & JBG_LRLTWO) ? TPB2CX : TPB3CX);
-	if (se->result == JBG_MORE || se->result == JBG_MARKER)
+	if (slntp < 0)
 	  goto leave;
 	s->lntp[plane][layer - s->dl] =
 	  !(slntp ^ s->lntp[plane][layer - s->dl]);
-	if (s->lntp[plane][layer - s->dl]) {
-	  /* this line is 'not typical' and has to be coded completely */
-	  s->pseudo = 0;
-	} else {
+	if (!s->lntp[plane][layer - s->dl]) {
 	  /* this line is 'typical' (i.e. identical to the previous one) */
 	  p1 = hp;
 	  if (s->i == 0 && (stripe == 0 || s->reset[plane][layer - s->dl]))
@@ -1995,7 +2191,9 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 	  hp += hbpl;
 	  continue;
 	}
+	/* this line is 'not typical' and has to be coded completely */
       }
+      s->pseudo = 0;
       
       /*
        * Layout of the variables line_h1, line_h2, line_h3, which contain
@@ -2071,14 +2269,24 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 	if (s->options & JBG_LRLTWO) {
 	  /* two line template */
 	  do {
-	    if (tx)
-	      pix = arith_decode(se, (((line_h2 >> 9) & 0x3e0) |
-				      ((line_h1 >> shift) & 0x010) |
+	    if (tx) {
+	      if ((unsigned) tx > x)
+		a = 0;
+	      else if (tx < 8)
+		a = ((line_h1 >> (tx - 5)) & 0x010);
+	      else {
+		o = (x - tx) - (x & ~7L);
+		a = (hp[o >> 3] >> (7 - (o & 7))) & 1;
+		a <<= 4;
+	      }
+	      assert(tx > 31 ||
+		     a == ((line_h1 >> (tx - 5)) & 0x010));
+	      pix = arith_decode(se, (((line_h2 >> 9) & 0x3e0) | a |
 				      (line_h1 & 0x00f)));
-	    else
+	    } else
 	      pix = arith_decode(se, (((line_h2 >> 9) & 0x3f0) |
 				      (line_h1 & 0x00f)));
-	    if (se->result == JBG_MORE || se->result == JBG_MARKER)
+	    if (pix < 0)
 	      goto leave;
 	    line_h1 = (line_h1 << 1) | pix;
 	    line_h2 <<= 1;
@@ -2086,16 +2294,26 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 	} else {
 	  /* three line template */
 	  do {
-	    if (tx) 
+	    if (tx) {
+	      if ((unsigned) tx > x)
+		a = 0;
+	      else if (tx < 8)
+		a = ((line_h1 >> (tx - 3)) & 0x004);
+	      else {
+		o = (x - tx) - (x & ~7L);
+		a = (hp[o >> 3] >> (7 - (o & 7))) & 1;
+		a <<= 2;
+	      }
+	      assert(tx > 31 ||
+		     a == ((line_h1 >> (tx - 3)) & 0x004));
 	      pix = arith_decode(se, (((line_h3 >>  7) & 0x380) |
-				      ((line_h2 >> 11) & 0x078) |
-				      ((line_h1 >> shift) & 0x004) |
+				      ((line_h2 >> 11) & 0x078) | a |
 				      (line_h1 & 0x003)));
-	    else
+	    } else
 	      pix = arith_decode(se, (((line_h3 >>  7) & 0x380) |
 				      ((line_h2 >> 11) & 0x07c) |
 				      (line_h1 & 0x003)));
-	    if (se->result == JBG_MORE || se->result == JBG_MARKER)
+	    if (pix < 0)
 	      goto leave;
 	    
 	    line_h1 = (line_h1 << 1) | pix;
@@ -2130,20 +2348,17 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 #endif
 	  }
       tx = s->tx[plane][layer - s->dl];
-      shift = tx - 3;
 
       /* handle lower border of low-resolution image */
       if ((s->i >> 1) >= ll - 1 || (y >> 1) >= ly - 1)
 	lp1 = lp2;
 
       /* typical prediction */
-      if (s->options & JBG_TPDON && s->pseudo) {
-	s->lntp[plane][layer - s->dl] = arith_decode(se, TPDCX);
-	if (se->result == JBG_MORE || se->result == JBG_MARKER)
+      if ((s->options & JBG_TPDON) && s->pseudo) {
+	if ((s->lntp[plane][layer - s->dl] = arith_decode(se, TPDCX)) < 0)
 	  goto leave;
-	s->pseudo = 0;
       }
-
+      s->pseudo = 0;
 
       /*
        * Layout of the variables line_h1, line_h2, line_h3, which contain
@@ -2254,7 +2469,7 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 		if (pix & 2) {
 		  if (tx)
 		    cx = ((line_h1         & 0x003) |
-			  (((line_h1 << 2) >> shift) & 0x010) |
+			  (((line_h1 << 2) >> (tx - 3)) & 0x010) |
 			  ((line_h2 >> 12) & 0x00c) |
 			  ((line_h3 >> 10) & 0x020));
 		  else
@@ -2270,7 +2485,7 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 		  cx |= (y & 1) << 11;
 
 		  pix = arith_decode(se, cx);
-		  if (se->result == JBG_MORE || se->result == JBG_MARKER)
+		  if (pix < 0)
 		    goto leave;
 		}
 
@@ -2316,23 +2531,38 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 
 
 /*
- * Provide a new BIE fragment to the decoder.
+ * Provide to the decoder a new BIE fragment of len bytes starting at data.
+ *
+ * Unless cnt is NULL, *cnt will contain the number of actually read bytes
+ * on return.
+ *
+ * Normal return values:
+ *
+ *   JBG_EAGAIN      All data bytes provided so far have been processed
+ *                   (*cnt == len) but the end of the data stream has
+ *                   not yet been recognized. Call the function again
+ *                   with additional BIE bytes.
+ *   JBG_EOK         The function has reached the end of a and
+ *                   a full image has been decoded. The function can
+ *                   be called again with data from the next BIE, if
+ *                   there exists one, in order to get to a higher
+ *                   resolution layer. The remaining len - *cnt bytes
+ *                   of the previous data block will then have to passed
+ *                   to this function again if len > *cnt.
+ *   JBG_EOK_INTR    Parsing the BIE has been interrupted as had been
+ *                   requested by a jbg_dec_maxsize() specification.
+ *                   This function can be called again with the
+ *                   rest of the BIE to continue the decoding process.
+ *                   The remaining len - *cnt bytes of the previous
+ *                   data block will then have to be passed to this
+ *                   function again if len > *cnt.
  *
- * If cnt is not NULL, then *cnt will contain after the call the
- * number of actually read bytes. If the data was not complete, then
- * the return value will be JBG_EAGAIN and *cnt == len. In case this
- * function has returned with JBG_EOK, then it has reached the end of
- * a BIE but it can be called again with data from the next BIE if
- * there exists one in order to get to a higher resolution layer. In
- * case the return value was JBG_EOK_INTR then this function can be
- * called again with the rest of the BIE, because parsing the BIE has
- * been interrupted by a jbg_dec_maxsize() specification. In both
- * cases the remaining len - *cnt bytes of the previous block will
- * have to passed to this function again (if len > *cnt). In case of
- * any other return value than JBG_EOK, JBG_EOK_INTR or JBG_EAGAIN, a
- * serious problem has occured and the only function you should call
- * is jbg_dec_free() in order to remove the mess (and probably
- * jbg_strerror() in order to find out what to tell the user).
+ * Any other return value indicates that the decoding process was
+ * aborted by a serious problem and the only function you can then
+ * still call is jbg_dec_free() in order to remove the mess, and
+ * jbg85_strerror() to find out what to tell the user. (Looking at the
+ * least significant bits of the return value will provide additional
+ * information by identifying which test exactly has failed.)
  */
 int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
 	       size_t *cnt)
@@ -2340,9 +2570,8 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
   int i, j, required_length;
   unsigned long x, y;
   unsigned long is[3], ie[3];
-  long hsize, lsize;
-  extern char jbg_dptable[];
   size_t dummy_cnt;
+  unsigned char *dppriv;
 
   if (!cnt) cnt = &dummy_cnt;
   *cnt = 0;
@@ -2354,92 +2583,112 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
       s->buffer[s->bie_len++] = data[(*cnt)++];
     if (s->bie_len < 20) 
       return JBG_EAGAIN;
-    if (s->buffer[1] < s->buffer[0])
-      return JBG_EINVAL;
     /* test whether this looks like a valid JBIG header at all */
-    if (s->buffer[3] != 0 || (s->buffer[18] & 0xf0) != 0 ||
-	(s->buffer[19] & 0x80) != 0)
-      return JBG_EINVAL;
+    if (s->buffer[1] < s->buffer[0])
+      return JBG_EINVAL | 1;
+    if (s->buffer[3] != 0)           return JBG_EINVAL | 2; /* padding != 0 */
+    if ((s->buffer[18] & 0xf0) != 0) return JBG_EINVAL | 3; /* padding != 0 */
+    if ((s->buffer[19] & 0x80) != 0) return JBG_EINVAL | 4; /* padding != 0 */
     if (s->buffer[0] != s->d + 1)
-      return JBG_ENOCONT;
+      return JBG_ENOCONT | 1;
     s->dl = s->buffer[0];
     s->d = s->buffer[1];
     if (s->dl == 0)
       s->planes = s->buffer[2];
     else
       if (s->planes != s->buffer[2])
-	return JBG_ENOCONT;
+	return JBG_ENOCONT | 2;
     x = (((long) s->buffer[ 4] << 24) | ((long) s->buffer[ 5] << 16) |
 	 ((long) s->buffer[ 6] <<  8) | (long) s->buffer[ 7]);
     y = (((long) s->buffer[ 8] << 24) | ((long) s->buffer[ 9] << 16) |
 	 ((long) s->buffer[10] <<  8) | (long) s->buffer[11]);
     if (s->dl != 0 && ((s->xd << (s->d - s->dl + 1)) != x &&
 		       (s->yd << (s->d - s->dl + 1)) != y))
-      return JBG_ENOCONT;
+      return JBG_ENOCONT | 3;
     s->xd = x;
     s->yd = y;
     s->l0 = (((long) s->buffer[12] << 24) | ((long) s->buffer[13] << 16) |
 	     ((long) s->buffer[14] <<  8) | (long) s->buffer[15]);
-    if (!s->planes || !s->xd || !s->yd || !s->l0)
-      return JBG_EINVAL;
+    /* ITU-T T.85 trick not directly implemented by decoder; for full
+     * T.85 compatibility with respect to all NEWLEN marker scenarios,
+     * preprocess BIE with jbg_newlen() before passing it to the decoder,
+     * or consider using the decoder found in jbig85.c instead. */
+    if (s->yd == 0xffffffff)
+      return JBG_EIMPL | 1;
+    if (!s->planes) return JBG_EINVAL | 5;
+    if (!s->xd)     return JBG_EINVAL | 6;
+    if (!s->yd)     return JBG_EINVAL | 7;
+    if (!s->l0)     return JBG_EINVAL | 8;
+    /* prevent uint32 overflow: s->l0 * 2 ^ s->d < 2 ^ 32 */
+    if (s->d > 31)
+      return JBG_EIMPL | 2;
+    if ((s->d != 0 && s->l0 >= (1UL << (32 - s->d))))
+      return JBG_EIMPL | 3;
     s->mx = s->buffer[16];
     if (s->mx > 127)
-      return JBG_EINVAL;
+      return JBG_EINVAL | 9;
     s->my = s->buffer[17];
-    if (s->mx > 32 || s->my > 0) 
-      return JBG_EIMPL;
+#if 0
+    if (s->my > 0)
+      return JBG_EIMPL | 4;
+#endif
     s->order = s->buffer[18];
-    if (index[s->order & 7][0] < 0)
-      return JBG_EINVAL;
+    if (iindex[s->order & 7][0] < 0)
+      return JBG_EINVAL | 10;
     /* HITOLO and SEQ currently not yet implemented */
     if (s->dl != s->d && (s->order & JBG_HITOLO || s->order & JBG_SEQ))
-      return JBG_EIMPL;
+      return JBG_EIMPL | 5;
     s->options = s->buffer[19];
 
     /* calculate number of stripes that will be required */
-    s->stripes = ((s->yd >> s->d) +
-		  ((((1UL << s->d) - 1) & s->xd) != 0) + s->l0 - 1) / s->l0;
-
+    s->stripes = jbg_stripes(s->l0, s->yd, s->d);
+    
     /* some initialization */
-    s->ii[index[s->order & 7][STRIPE]] = 0;
-    s->ii[index[s->order & 7][LAYER]] = s->dl;
-    s->ii[index[s->order & 7][PLANE]] = 0;
-    /* bytes required for resolution layer D and D-1 */
-    hsize = ((s->xd + 7) / 8) * s->yd;
-    lsize = ((jbg_ceil_half(s->xd, 1) + 7) / 8) *
-      jbg_ceil_half(s->yd, 1);
+    s->ii[iindex[s->order & 7][STRIPE]] = 0;
+    s->ii[iindex[s->order & 7][LAYER]] = s->dl;
+    s->ii[iindex[s->order & 7][PLANE]] = 0;
     if (s->dl == 0) {
-      s->s = checked_malloc(s->planes * sizeof(struct jbg_ardec_state *));
-      s->tx = checked_malloc(s->planes * sizeof(int *));
-      s->ty = checked_malloc(s->planes * sizeof(int *));
-      s->reset = checked_malloc(s->planes * sizeof(int *));
-      s->lntp = checked_malloc(s->planes * sizeof(int *));
-      s->lhp[0] = checked_malloc(s->planes * sizeof(unsigned char *));
-      s->lhp[1] = checked_malloc(s->planes * sizeof(unsigned char *));
+      s->s      = (struct jbg_ardec_state **)
+	checked_malloc(s->planes, sizeof(struct jbg_ardec_state *));
+      s->tx     = (int **) checked_malloc(s->planes, sizeof(int *));
+      s->ty     = (int **) checked_malloc(s->planes, sizeof(int *));
+      s->reset  = (int **) checked_malloc(s->planes, sizeof(int *));
+      s->lntp   = (int **) checked_malloc(s->planes, sizeof(int *));
+      s->lhp[0] = (unsigned char **)
+	checked_malloc(s->planes, sizeof(unsigned char *));
+      s->lhp[1] = (unsigned char **)
+	checked_malloc(s->planes, sizeof(unsigned char *));
       for (i = 0; i < s->planes; i++) {
-	s->s[i] = checked_malloc((s->d - s->dl + 1) *
-				 sizeof(struct jbg_ardec_state));
-	s->tx[i] = checked_malloc((s->d - s->dl + 1) * sizeof(int));
-	s->ty[i] = checked_malloc((s->d - s->dl + 1) * sizeof(int));
-	s->reset[i] = checked_malloc((s->d - s->dl + 1) * sizeof(int));
-	s->lntp[i] = checked_malloc((s->d - s->dl + 1) * sizeof(int));
-	s->lhp[s->d    &1][i] = checked_malloc(sizeof(unsigned char) * hsize);
-	s->lhp[(s->d-1)&1][i] = checked_malloc(sizeof(unsigned char) * lsize);
+	s->s[i]     = (struct jbg_ardec_state *)
+	  checked_malloc(s->d - s->dl + 1, sizeof(struct jbg_ardec_state));
+	s->tx[i]    = (int *) checked_malloc(s->d - s->dl + 1, sizeof(int));
+	s->ty[i]    = (int *) checked_malloc(s->d - s->dl + 1, sizeof(int));
+	s->reset[i] = (int *) checked_malloc(s->d - s->dl + 1, sizeof(int));
+	s->lntp[i]  = (int *) checked_malloc(s->d - s->dl + 1, sizeof(int));
+	s->lhp[ s->d    & 1][i] = (unsigned char *)
+	  checked_malloc(s->yd, jbg_ceil_half(s->xd, 3));
+	s->lhp[(s->d-1) & 1][i] = (unsigned char *)
+	  checked_malloc(jbg_ceil_half(s->yd, 1), jbg_ceil_half(s->xd, 1+3));
       }
     } else {
       for (i = 0; i < s->planes; i++) {
-	s->s[i] = checked_realloc(s->s[i], (s->d - s->dl + 1) *
-				  sizeof(struct jbg_ardec_state));
-	s->tx[i] = checked_realloc(s->tx[i], (s->d - s->dl + 1) * sizeof(int));
-	s->ty[i] = checked_realloc(s->ty[i], (s->d - s->dl + 1) * sizeof(int));
-	s->reset[i] = checked_realloc(s->reset[i],
-				      (s->d - s->dl +1) * sizeof(int));
-	s->lntp[i] = checked_realloc(s->lntp[i],
-				     (s->d - s->dl +1) * sizeof(int));
-	s->lhp[s->d    &1][i] = checked_realloc(s->lhp[s->d    & 1][i],
-						sizeof(unsigned char) * hsize);
-	s->lhp[(s->d-1)&1][i] = checked_realloc(s->lhp[(s->d-1)&1][i],
-						sizeof(unsigned char) * lsize);
+	s->s[i]     = (struct jbg_ardec_state *)
+	  checked_realloc(s->s[i], s->d - s->dl + 1,
+			  sizeof(struct jbg_ardec_state));
+	s->tx[i]    = (int *) checked_realloc(s->tx[i],
+					      s->d - s->dl + 1, sizeof(int));
+	s->ty[i]    = (int *) checked_realloc(s->ty[i],
+					      s->d - s->dl + 1, sizeof(int));
+	s->reset[i] = (int *) checked_realloc(s->reset[i],
+					      s->d - s->dl + 1, sizeof(int));
+	s->lntp[i]  = (int *) checked_realloc(s->lntp[i],
+					      s->d - s->dl + 1, sizeof(int));
+	s->lhp[ s->d    & 1][i] = (unsigned char *)
+	  checked_realloc(s->lhp[ s->d    & 1][i],
+			  s->yd, jbg_ceil_half(s->xd, 3));
+	s->lhp[(s->d-1) & 1][i] = (unsigned char *)
+	  checked_realloc(s->lhp[(s->d-1) & 1][i],
+			  jbg_ceil_half(s->yd, 1), jbg_ceil_half(s->xd, 1+3));
       }
     }
     for (i = 0; i < s->planes; i++)
@@ -2460,13 +2709,16 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
       (s->options & (JBG_DPON | JBG_DPPRIV | JBG_DPLAST)) ==
       (JBG_DPON | JBG_DPPRIV)) {
     assert(s->bie_len >= 20);
+    if (!s->dppriv || s->dppriv == jbg_dptable)
+      s->dppriv = (char *) checked_malloc(1728, sizeof(char));
     while (s->bie_len < 20 + 1728 && *cnt < len)
-      s->buffer[s->bie_len++ - 20] = data[(*cnt)++];
+      s->dppriv[s->bie_len++ - 20] = data[(*cnt)++];
     if (s->bie_len < 20 + 1728) 
       return JBG_EAGAIN;
-    if (!s->dppriv || s->dppriv == jbg_dptable)
-      s->dppriv = checked_malloc(sizeof(char) * 1728);
-    jbg_dppriv2int(s->dppriv, s->buffer);
+    dppriv = (unsigned char *) s->dppriv;
+    s->dppriv = (char *) checked_malloc(6912, sizeof(char));
+    jbg_dppriv2int(s->dppriv, dppriv);
+    checked_free(dppriv);
   }
 
   /*
@@ -2516,7 +2768,7 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
       /* now the buffer is filled with exactly one marker segment */
       switch (s->buffer[1]) {
       case MARKER_COMMENT:
-	s->comment_skip = 
+	s->comment_skip =
 	  (((long) s->buffer[2] << 24) | ((long) s->buffer[3] << 16) |
 	   ((long) s->buffer[4] <<  8) | (long) s->buffer[5]);
 	break;
@@ -2531,21 +2783,21 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
 	      s->at_tx[s->at_moves] >   (int) s->mx ||
 	      s->at_ty[s->at_moves] >   (int) s->my ||
 	      (s->at_ty[s->at_moves] == 0 && s->at_tx[s->at_moves] < 0))
-	    return JBG_EINVAL;
+	    return JBG_EINVAL | 11;
+	  if (s->at_ty[s->at_moves] != 0)
+	    return JBG_EIMPL | 6;
 	  s->at_moves++;
 	} else
-	  return JBG_EINVAL;
+	  return JBG_EIMPL | 7; /* more than JBG_ATMOVES_MAX ATMOVES */
 	break;
       case MARKER_NEWLEN:
 	y = (((long) s->buffer[2] << 24) | ((long) s->buffer[3] << 16) |
 	     ((long) s->buffer[4] <<  8) | (long) s->buffer[5]);
-	if (y > s->yd || !(s->options & JBG_VLENGTH))
-	  return JBG_EINVAL;
+	if (y > s->yd)                   return JBG_EINVAL | 12;
+	if (!(s->options & JBG_VLENGTH)) return JBG_EINVAL | 13;
 	s->yd = y;
 	/* calculate again number of stripes that will be required */
-	s->stripes = 
-	  ((s->yd >> s->d) +
-	   ((((1UL << s->d) - 1) & s->xd) != 0) + s->l0 - 1) / s->l0;
+	s->stripes = jbg_stripes(s->l0, s->yd, s->d);
 	break;
       case MARKER_ABORT:
 	return JBG_EABORT;
@@ -2555,13 +2807,13 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
 	/* decode final pixels based on trailing zero bytes */
 	decode_pscd(s, s->buffer, 2);
 
-	arith_decode_init(s->s[s->ii[index[s->order & 7][PLANE]]] + 
-			  s->ii[index[s->order & 7][LAYER]] - s->dl,
-			  s->ii[index[s->order & 7][STRIPE]] != s->stripes - 1
+	arith_decode_init(s->s[s->ii[iindex[s->order & 7][PLANE]]] + 
+			  s->ii[iindex[s->order & 7][LAYER]] - s->dl,
+			  s->ii[iindex[s->order & 7][STRIPE]] != s->stripes - 1
 			  && s->buffer[1] != MARKER_SDRST);
 	
-	s->reset[s->ii[index[s->order & 7][PLANE]]]
-	  [s->ii[index[s->order & 7][LAYER]] - s->dl] =
+	s->reset[s->ii[iindex[s->order & 7][PLANE]]]
+	  [s->ii[iindex[s->order & 7][LAYER]] - s->dl] =
 	    (s->buffer[1] == MARKER_SDRST);
 	
 	/* prepare for next SDE */
@@ -2572,12 +2824,12 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
 	
 	/* increment layer/stripe/plane loop variables */
 	/* start and end value for each loop: */
-	is[index[s->order & 7][STRIPE]] = 0;
-	ie[index[s->order & 7][STRIPE]] = s->stripes - 1;
-	is[index[s->order & 7][LAYER]] = s->dl;
-	ie[index[s->order & 7][LAYER]] = s->d;
-	is[index[s->order & 7][PLANE]] = 0;
-	ie[index[s->order & 7][PLANE]] = s->planes - 1;
+	is[iindex[s->order & 7][STRIPE]] = 0;
+	ie[iindex[s->order & 7][STRIPE]] = s->stripes - 1;
+	is[iindex[s->order & 7][LAYER]] = s->dl;
+	ie[iindex[s->order & 7][LAYER]] = s->d;
+	is[iindex[s->order & 7][PLANE]] = 0;
+	ie[iindex[s->order & 7][PLANE]] = s->planes - 1;
 	i = 2;  /* index to innermost loop */
 	do {
 	  j = 0;  /* carry flag */
@@ -2593,12 +2845,16 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
 	
 	/* check whether this have been all SDEs */
 	if (j) {
+#ifdef DEBUG
+	  fprintf(stderr, "This was the final SDE in this BIE, "
+		  "%ld bytes left.\n", (long) (len - *cnt));
+#endif
 	  s->bie_len = 0;
 	  return JBG_EOK;
 	}
 
 	/* check whether we have to abort because of xmax/ymax */
-	if (index[s->order & 7][LAYER] == 0 && i < 0) {
+	if (iindex[s->order & 7][LAYER] == 0 && i < 0) {
 	  /* LAYER is the outermost loop and we have just gone to next layer */
 	  if (jbg_ceil_half(s->xd, s->d - s->ii[0]) > s->xmax ||
 	      jbg_ceil_half(s->yd, s->d - s->ii[0]) > s->ymax) {
@@ -2629,7 +2885,7 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
 		"%02x %02x %02x %02x ...\n", data[*cnt], data[*cnt+1],
 		data[*cnt+2], data[*cnt+3]);
 #endif
-	return JBG_EINVAL;
+	return JBG_EINVAL | 14;
       }
       
     }
@@ -2641,15 +2897,16 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
 
 /*
  * After jbg_dec_in() returned JBG_EOK or JBG_EOK_INTR, you can call this
- * function in order to find out the width of the image.
+ * function in order to find out the width of the image. Returns 0 if
+ * there is no image available yet.
  */
-long jbg_dec_getwidth(const struct jbg_dec_state *s)
+unsigned long jbg_dec_getwidth(const struct jbg_dec_state *s)
 {
   if (s->d < 0)
-    return -1;
-  if (index[s->order & 7][LAYER] == 0) {
+    return 0;
+  if (iindex[s->order & 7][LAYER] == 0) {
     if (s->ii[0] < 1)
-      return -1;
+      return 0;
     else
       return jbg_ceil_half(s->xd, s->d - (s->ii[0] - 1));
   }
@@ -2660,15 +2917,16 @@ long jbg_dec_getwidth(const struct jbg_dec_state *s)
 
 /*
  * After jbg_dec_in() returned JBG_EOK or JBG_EOK_INTR, you can call this
- * function in order to find out the height of the image.
+ * function in order to find out the height of the image. Returns 0 if
+ * there is no image available yet.
  */
-long jbg_dec_getheight(const struct jbg_dec_state *s)
+unsigned long jbg_dec_getheight(const struct jbg_dec_state *s)
 {
   if (s->d < 0)
-    return -1;
-  if (index[s->order & 7][LAYER] == 0) {
+    return 0;
+  if (iindex[s->order & 7][LAYER] == 0) {
     if (s->ii[0] < 1)
-      return -1;
+      return 0;
     else
       return jbg_ceil_half(s->yd, s->d - (s->ii[0] - 1));
   }
@@ -2679,13 +2937,14 @@ long jbg_dec_getheight(const struct jbg_dec_state *s)
 
 /*
  * After jbg_dec_in() returned JBG_EOK or JBG_EOK_INTR, you can call this
- * function in order to get a pointer to the image.
+ * function in order to get a pointer to the image. Returns NULL if
+ * there is no image available yet.
  */
 unsigned char *jbg_dec_getimage(const struct jbg_dec_state *s, int plane)
 {
   if (s->d < 0)
     return NULL;
-  if (index[s->order & 7][LAYER] == 0) {
+  if (iindex[s->order & 7][LAYER] == 0) {
     if (s->ii[0] < 1)
       return NULL;
     else
@@ -2701,20 +2960,20 @@ unsigned char *jbg_dec_getimage(const struct jbg_dec_state *s, int plane)
  * this function in order to find out the size in bytes of one
  * bitplane of the image.
  */
-long jbg_dec_getsize(const struct jbg_dec_state *s)
+unsigned long jbg_dec_getsize(const struct jbg_dec_state *s)
 {
   if (s->d < 0)
-    return -1;
-  if (index[s->order & 7][LAYER] == 0) {
+    return 0;
+  if (iindex[s->order & 7][LAYER] == 0) {
     if (s->ii[0] < 1)
-      return -1;
+      return 0;
     else
       return 
-	((jbg_ceil_half(s->xd, s->d - (s->ii[0] - 1)) + 7) / 8) *
+	jbg_ceil_half(s->xd, s->d - (s->ii[0] - 1) + 3) * /* overflow risk? */
 	jbg_ceil_half(s->yd, s->d - (s->ii[0] - 1));
   }
   
-  return ((s->xd + 7) / 8) * s->yd;
+  return jbg_ceil_half(s->xd, 3) * s->yd;
 }
 
 
@@ -2723,16 +2982,16 @@ long jbg_dec_getsize(const struct jbg_dec_state *s)
  * this function in order to find out the size of the image that you
  * can retrieve with jbg_merge_planes().
  */
-long jbg_dec_getsize_merged(const struct jbg_dec_state *s)
+unsigned long jbg_dec_getsize_merged(const struct jbg_dec_state *s)
 {
   if (s->d < 0)
-    return -1;
-  if (index[s->order & 7][LAYER] == 0) {
+    return 0;
+  if (iindex[s->order & 7][LAYER] == 0) {
     if (s->ii[0] < 1)
-      return -1;
+      return 0;
     else
       return 
-	jbg_ceil_half(s->xd, s->d - (s->ii[0] - 1)) *
+	jbg_ceil_half(s->xd, s->d - (s->ii[0] - 1)) * /* overflow risk? */
 	jbg_ceil_half(s->yd, s->d - (s->ii[0] - 1)) *
 	((s->planes + 7) / 8);
   }
@@ -2770,6 +3029,8 @@ void jbg_dec_free(struct jbg_dec_state *s)
   checked_free(s->lntp);
   checked_free(s->lhp[0]);
   checked_free(s->lhp[1]);
+  if (s->dppriv && s->dppriv != jbg_dptable)
+    checked_free(s->dppriv);
 
   s->s = NULL;
 
@@ -2790,11 +3051,10 @@ void jbg_split_planes(unsigned long x, unsigned long y, int has_planes,
 		      const unsigned char *src, unsigned char **dest,
 		      int use_graycode)
 {
-  unsigned bpl = (x + 7) / 8;           /* bytes per line in dest plane */
-  unsigned i, k = 8;
+  unsigned long bpl = jbg_ceil_half(x, 3);  /* bytes per line in dest plane */
+  unsigned long line, i;
+  unsigned k = 8;
   int p;
-  unsigned long line;
-  extern void *memset(void *s, int c, size_t n);
   unsigned prev;     /* previous *src byte shifted by 8 bit to the left */
   register int bits, msb = has_planes - 1;
   int bitno;
@@ -2818,7 +3078,7 @@ void jbg_split_planes(unsigned long x, unsigned long y, int has_planes,
 	  bits = (prev | *src) >> bitno;
 	  /* go to next *src byte, but keep old */
 	  if (bitno == 0)
-	    prev = *src++;
+	    prev = *src++ << 8;
 	  /* make space for inserting new bit */
 	  dest[p][bpl * line + i] <<= 1;
 	  /* insert bit, if requested apply Gray encoding */
@@ -2831,7 +3091,7 @@ void jbg_split_planes(unsigned long x, unsigned long y, int has_planes,
 	}
 	/* skip unused *src bytes */
 	for (;p < has_planes; p++)
-	  if (((has_planes - 1 - p) & 7) == 0)
+	  if (((msb - p) & 7) == 0)
 	    src++;
       }
     }
@@ -2845,16 +3105,16 @@ void jbg_split_planes(unsigned long x, unsigned long y, int has_planes,
 /* 
  * Merge the separate bit planes decoded by the JBIG decoder into an
  * integer pixel field. This is essentially the counterpart to
- * jbg_split_planes(). */
+ * jbg_split_planes().
+ */
 void jbg_dec_merge_planes(const struct jbg_dec_state *s, int use_graycode,
 			  void (*data_out)(unsigned char *start, size_t len,
 					   void *file), void *file)
 {
 #define BUFLEN 4096
-  int bpp, bpl;
-  unsigned long line;
-  unsigned i, k = 8;
-  int p, q;
+  unsigned long bpl, line, i;
+  unsigned k = 8;
+  int p;
   unsigned char buf[BUFLEN];
   unsigned char *bp = buf;
   unsigned char **src;
@@ -2866,12 +3126,11 @@ void jbg_dec_merge_planes(const struct jbg_dec_state *s, int use_graycode,
   
   x = jbg_dec_getwidth(s);
   y = jbg_dec_getheight(s);
-  if (x <= 0 || y <= 0)
+  if (x == 0 || y == 0)
     return;
-  bpp = (s->planes + 7) / 8;   /* bytes per pixel in dest image */
-  bpl = (x + 7) / 8;           /* bytes per line in src plane */
+  bpl = jbg_ceil_half(x, 3);   /* bytes per line in src plane */
 
-  if (index[s->order & 7][LAYER] == 0)
+  if (iindex[s->order & 7][LAYER] == 0)
     if (s->ii[0] < 1)
       return;
     else
@@ -2882,12 +3141,13 @@ void jbg_dec_merge_planes(const struct jbg_dec_state *s, int use_graycode,
   for (line = 0; line < y; line++) {                    /* lines loop */
     for (i = 0; i * 8 < x; i++) {                       /* src bytes loop */
       for (k = 0; k < 8 && i * 8 + k < x; k++) {        /* pixel loop */
-	for (p = (s->planes-1) & ~7; p >= 0; p -= 8) {  /* dest bytes loop */
-	  v = 0;
-	  for (q = 0; q < 8 && p+q < s->planes; q++)    /* pixel bit loop */
+	v = 0;
+	for (p = 0; p < s->planes;) {                   /* dest bytes loop */
+	  do {
 	    v = (v << 1) |
-	      (((src[p+q][bpl * line + i] >> (7 - k)) & 1) ^
+	      (((src[p][bpl * line + i] >> (7 - k)) & 1) ^
 	       (use_graycode & v));
+	  } while ((s->planes - ++p) & 7);
 	  *bp++ = v;
 	  if (bp - buf == BUFLEN) {
 	    data_out(buf, BUFLEN, file);
@@ -2903,3 +3163,123 @@ void jbg_dec_merge_planes(const struct jbg_dec_state *s, int use_graycode,
   
   return;
 }
+
+
+/*
+ * Given a pointer p to the first byte of either a marker segment or a
+ * PSCD, as well as the length len of the remaining data, return
+ * either the pointer to the first byte of the next marker segment or
+ * PSCD, or p+len if this was the last one, or NULL if some error was
+ * encountered. Possible errors are:
+ *
+ *  - not enough bytes left for complete marker segment
+ *  - no marker segment terminates the PSCD
+ *  - unknown marker code encountered
+ *  
+ */
+static unsigned char *jbg_next_pscdms(unsigned char *p, size_t len)
+{
+  unsigned char *pp;
+  unsigned long l;
+
+  if (len < 2)
+    return NULL; /* not enough bytes left for complete marker segment */
+
+  if (p[0] != MARKER_ESC || p[1] == MARKER_STUFF) {
+    do {
+      while (p[0] == MARKER_ESC && p[1] == MARKER_STUFF) {
+	p += 2;
+	len -= 2;
+	if (len < 2)
+	  return NULL; /* not enough bytes left for complete marker segment */
+      }
+      assert(len >= 2);
+      pp = (unsigned char *) memchr(p, MARKER_ESC, len - 1);
+      if (!pp)
+	return NULL; /* no marker segment terminates the PSCD */
+      l = pp - p;
+      assert(l < len);
+      p += l;
+      len -= l;
+    } while (p[1] == MARKER_STUFF);
+  } else {
+    switch (p[1]) {
+    case MARKER_SDNORM:
+    case MARKER_SDRST:
+    case MARKER_ABORT:
+      return p + 2;
+    case MARKER_NEWLEN:
+      if (len < 6)
+	return NULL; /* not enough bytes left for complete marker segment */
+      return p + 6;
+    case MARKER_ATMOVE:
+      if (len < 8)
+	return NULL; /* not enough bytes left for complete marker segment */
+      return p + 8;
+    case MARKER_COMMENT:
+      if (len < 6)
+	return NULL; /* not enough bytes left for complete marker segment */
+      l = (((long) p[2] << 24) | ((long) p[3] << 16) |
+	   ((long) p[4] <<  8) |  (long) p[5]);
+      if (len - 6 < l)
+	return NULL; /* not enough bytes left for complete marker segment */
+      return p + 6 + l;
+    default:
+      /* unknown marker sequence encountered */
+      return NULL;
+    }
+  }
+
+  return p;
+}
+
+
+/*
+ * Scan a complete BIE for a NEWLEN marker segment, then read the new
+ * YD value found in it and use it to overwrite the one in the BIE
+ * header. Use this procedure if a BIE initially declares an
+ * unreasonably high provisional YD value (e.g., 0xffffffff) or
+ * depends on the fact that section 6.2.6.2 of ITU-T T.82 says that a
+ * NEWLEN marker segment "could refer to a line in the immediately
+ * preceding stripe due to an unexpected termination of the image or
+ * the use of only such stripe". ITU-T.85 explicitely suggests the
+ * use of this for fax machines that start transmission before having
+ * encountered the end of the page. None of this is necessary for
+ * BIEs produced by JBIG-KIT, which normally does not use NEWLEN.
+ */
+int jbg_newlen(unsigned char *bie, size_t len)
+{
+  unsigned char *p = bie + 20;
+  int i;
+  unsigned long y, yn;
+
+  if (len < 20)
+    return JBG_EAGAIN;
+  if ((bie[19] & (JBG_DPON | JBG_DPPRIV | JBG_DPLAST))
+      == (JBG_DPON | JBG_DPPRIV))
+    p += 1728; /* skip DPTABLE */
+  if (p >= bie + len)
+    return JBG_EAGAIN;
+
+  while ((p = jbg_next_pscdms(p, len - (p - bie)))) {
+    if (p == bie + len)
+      return JBG_EOK;
+    else if (p[0] == MARKER_ESC)
+      switch (p[1]) {
+      case MARKER_NEWLEN:
+	y = (((long) bie[ 8] << 24) | ((long) bie[ 9] << 16) |
+	     ((long) bie[10] <<  8) |  (long) bie[11]);
+	yn = (((long) p[2] << 24) | ((long) p[3] << 16) |
+	      ((long) p[4] <<  8) |  (long) p[5]);
+	if (yn > y) return JBG_EINVAL | 12;
+	/* overwrite YD in BIH with YD from NEWLEN */
+	for (i = 0; i < 4; i++) {
+	  bie[8+i] = p[2+i];
+	}
+	return JBG_EOK;
+      case MARKER_ABORT:
+	return JBG_EABORT;
+      }
+  }
+  return JBG_EINVAL | 0;
+}
diff --git a/converter/other/jbig/jbig.doc b/converter/other/jbig/libjbig/jbig.txt
index 10eeda80..bdc14b17 100644
--- a/converter/other/jbig/jbig.doc
+++ b/converter/other/jbig/libjbig/jbig.txt
@@ -2,47 +2,56 @@
 Using the JBIG-KIT library
 --------------------------
 
-Markus Kuhn -- 1998-04-10
+Markus Kuhn -- 2013-09-10
 
 
-This text explains how to include the functions provided by the
-JBIG-KIT portable image compression library into your application
-software.
+This text explains how to use the functions provided by the JBIG-KIT
+portable image compression library jbig.c in your application
+software. The jbig.c library is a full-featured implementation of the
+JBIG1 standard aimed at applications that can hold the entire
+uncompressed and compressed image in RAM.
+
+[For applications that require only the single-bit-per-pixel "fax
+subset" of the JBIG1 standard defined in ITU-T Recommendation T.85
+<http://www.itu.int/rec/T-REC-T.85/en>, the alternative implementation
+found in jbig85.c may be preferable. It keeps not more than three
+lines of the uncompressed image in RAM, which makes it particularly
+suitable for embedded applications. For information on how to use
+jbig85.c, please refer to the separate documentation file jbig85.txt.]
 
 
 1  Introduction to JBIG
 
-Below follows a short introduction into some technical aspects of the
-JBIG standard. More detailed information is provided in the
-"Introduction and overview" section of the JBIG standard. Information
-about how to obtain a copy of the standard is available on the
-Internet from <http://www.itu.ch/> or <http://www.iso.ch/>.
+We start with a short introduction to JBIG1. More detailed information
+is provided in the "Introduction and overview" section of the JBIG1
+standard. Information on how to obtain a copy of the standard is
+available from <http://www.itu.int/rec/T-REC-T.82/en> or
+<http://www.iso.ch/>.
 
 Image data encoded with the JBIG algorithm is separated into planes,
 layers, and stripes. Each plane contains one bit per pixel. The number
 of planes stored in a JBIG data stream is the number of bits per
 pixel. Resolution layers are numbered from 0 to D with 0 being the
-layer with the lowest resolution and D the layer with the highest one.
-Each next higher resolution layer has exactly twice the number of rows
-and columns than the previous one. Layer 0 is encoded independently of
-any other data, all other resolution layers are encoded as only the
-difference between the next lower and the current layer. For
-applications that require very quick access to parts of an image it is
-possible to divide an image into several horizontal stripes. All
-stripes of one resolution layer have equal size except perhaps the
-final one. The number of stripes of an image is equal in all
-resolution layers and in all bit planes.
+layer with the lowest resolution and D the one with the highest. Each
+next higher resolution layer has twice the number of rows and columns.
+Layer 0 is encoded independently of any other data, all other
+resolution layers are encoded as only the difference between the next
+lower and the current layer. For applications that require very quick
+access to parts of an image, it is possible to divide an image into
+several horizontal stripes. All stripes of one resolution layer have
+equal size, except perhaps the final one. The number of stripes of an
+image is equal in all resolution layers and in all bit planes.
 
 The compressed data stream specified by the JBIG standard is called a
 bi-level image entity (BIE). A BIE consists of a 20-byte header,
 followed by an optional 1728-byte table (usually not present, except
 in special applications) followed by a sequence of stripe data
-entities (SDE). SDEs are the data blocks of which each encodes the
-content of one single stripe in one plane and resolution layer.
-Between the SDEs, other information blocks (called floating marker
-segments) can also be present, which change certain parameters of the
-algorithm in the middle of an image or contain additional application
-specific information. A BIE looks like this:
+entities (SDE). Each SDE encodes the content of one single stripe in
+one plane of one resolution layer. Between the SDEs, other information
+blocks (called floating marker segments) can also be present. They are
+used to change certain parameters of the algorithm in the middle of an
+image or contain additional application specific information. A BIE
+looks like this:
 
 
           +------------------------------------------------+
@@ -57,6 +66,10 @@ specific information. A BIE looks like this:
           |                                                |
           +------------------------------------------------+
           |                                                |
+          |       optional floating marker segments        |
+          |                                                |
+          +------------------------------------------------+
+          |                                                |
           |              stripe data entity                |
           |                                                |
           +------------------------------------------------+
@@ -85,58 +98,58 @@ storing the image in one single resolution layer.
 
 Different applications might have different requirements for the order
 in which the SDEs for stripes of various planes and layers are stored
-in the BIE, so all possible sensible orderings are allowed and
-indicated by four bits in the header.
+in the BIE, so all possible sensible orderings are allowed by the
+standard and indicated by four bits in the header.
 
 It is possible to use the raw BIE data stream as specified by the JBIG
 standard directly as the format of a file used for storing images.
-This is what the JBIG<->PBM conversion tools that are provided in this
-package as demonstration applications do. However as the BIE format
-has been designed for a large number of very different applications
-and also in order to allow efficient direct processing by special JBIG
-hardware chip implementations, the BIE header contains only the
-minimum amount of information absolutely required by the decompression
-algorithm. A large number of features expected from a good file format
-are missing in the BIE data stream:
+This is what the pbmtojbg, jbgtopbm, pbmtojbg85, and jbgtopbm85
+conversion tools do that are provided in this package as demonstration
+applications. However, as the BIE format has been designed for a large
+number of very different applications, and to allow efficient direct
+processing by special JBIG hardware chip implementations, the BIE
+header contains only the minimum amount of information absolutely
+required by the decompression algorithm. Many features expected from a
+good file format are missing in the BIE data stream:
 
   - no "magic code" in the first few bytes to allow identification
-    of the file on a typeless file system as JBIG encoded and to allow
+    of the file format on a typeless file system and to allow
     automatic distinction from other compression algorithms
 
-  - no standardized way to encode additional information like a textual
-    description, information about the meaning of various bit planes,
-    the physical size and resolution of the document, etc.
+  - no standardized way to encode additional information such as a
+    textual description, information about the meaning of various bit
+    planes, the physical size and resolution of the document, etc.
 
-  - a checksum that ensures image integrity
+  - a checksum to ensure image integrity
 
   - encryption and signature mechanisms
 
   - many things more
 
-Raw BIE data streams alone are consequently no suitable format for
+Raw BIE data streams alone may therefore not be a suitable format for
 document archiving and exchange. A standard format for this purpose
-would typically combine a BIE representing the image data together
-with an additional header providing auxiliary information into one
-file. Existing established multi-purpose file formats with a rich set
-of auxiliary information attributes like TIFF could be extended easily
-so that they can also contain JBIG compressed data.
+would typically combine a BIE representing the image data with an
+additional header providing auxiliary information into one file.
+Existing established multi-purpose file formats with a rich set of
+auxiliary information attributes like TIFF could be extended easily to
+also hold JBIG compressed data.
 
-On the other hand, in database applications for instance, a BIE might
-be directly stored in a variable length field. Auxiliary information
-on which efficient search operations are required would then be stored
-in other fields of the same record.
+On the other hand, in e.g. database applications, a BIE might be
+stored directly in a binary variable-length field. Auxiliary
+information would then be stored in other fields of the same record,
+to simplify search operations.
 
 
 2  Compressing an image
 
 2.1  Format of the source image
 
-For processing by the library, the image has to be present in memory
-as separate bitmap planes. Each byte of a bitmap contains eight
-pixels, the most significant bit represents the leftmost of these
-pixels. Each line of a bitmap has to be stored in an integral number
-of bytes. If the image width is not an integral multiple of eight,
-then the final byte has to be padded with zero bits.
+To be processed by the jbig.c encoder, the image has to be present in
+memory as separate bitmap planes. Each byte of a bitmap contains eight
+pixels, where the most significant bit represents the leftmost of
+these. Each line of a bitmap has to be stored in an integral number of
+bytes. If the image width is not an integral multiple of eight, then
+the final byte has to be padded with zero bits.
 
 For example the 23x5 pixels large single plane image:
 
@@ -158,28 +171,29 @@ or in hexadecimal notation
 
    7c e2 38 04 92 40 04 e2 5c 44 92 44 38 e2 38
 
-This is the format used in binary PBM files and it can also be be
-handled directly by the Xlib library of the X Window System.
+This is the format used in binary PBM files and it can also be handled
+directly by the Xlib library of the X Window System.
 
-As JBIG can also handle images with several bit planes, the JBIG-KIT
+As JBIG can also handle images with multiple bit planes, the jbig.c
 library functions accept and return always arrays of pointers to
 bitmaps with one pointer per plane.
 
-For single plane images, the standard recommends that a 0 pixel
+For single-plane images, the standard recommends that a 0 pixel
 represents the background and a 1 pixel represents the foreground
-color of an image, i.e. 0 is white and 1 is black for scanned paper
-documents. For images with several bits per pixel, the JBIG standard
-makes no recommendations about how various colors should be encoded.
+colour of an image, in other words, 0 is white and 1 is black for
+scanned paper documents. For images with several bits per pixel, the
+JBIG standard makes no recommendations about how various colours should
+be encoded.
 
-For greyscale images, by using a Gray code instead of a simple binary
+For grey-scale images, by using a Gray code instead of a simple binary
 weighted representation of the pixel intensity, some increase in
 coding efficiency can be reached.
 
-A Gray code is also a binary representation of integer numbers, but
+A Gray code is also a binary representation of integer numbers, but it
 has the property that the representations of the integer numbers i and
-(i+1) differ always in exactly one single bit. For example, the
-numbers 0 to 7 can be represented in normal binary code and Gray code
-as in the following table:
+(i+1) always differ in exactly one bit. For example, the numbers 0 to
+7 can be represented in normal binary code and Gray code as in the
+following table:
 
                            normal
               number    binary code     Gray code
@@ -198,7 +212,7 @@ half of the code (numbers 4 - 7) is simply the mirrored first half
 (numbers 3 - 0) with the first bit set to one. This way, arbitrarily
 large Gray codes can be generated quickly by mirroring the above
 example and prefixing the first half with zeros and the second half
-with ones as often as required. In greyscale images, it is common
+with ones as often as required. In grey-scale images, it is common
 practise to use the all-0 code for black and the all-1 code for white.
 
 No matter whether a Gray code or a binary code is used for encoding a
@@ -212,7 +226,7 @@ during the transmission.
 
 2.2  A simple compression application
 
-In order to use JBIG-KIT in your application, just link libjbig.a to
+In order to use jbig.c in your application, just link libjbig.a to
 your executable (on Unix systems just add -ljbig and -L. to the
 command line options of your compiler, on other systems you will have
 to write a new Makefile anyway), copy the file jbig.h into your source
@@ -222,10 +236,10 @@ directory and put the line
 
 into your source code.
 
-The library interface follows the concepts of object-oriented
-programming. You have to declare a variable (object)
+The library interface follows object-oriented programming principles.
+You have to declare a variable (object)
 
-  struct jbg_enc_state se;
+  struct jbg_enc_state s;
 
 which contains the current status of an encoder. Then you initialize
 the encoder by calling the constructor function
@@ -238,12 +252,12 @@ the encoder by calling the constructor function
 
 The parameters have the following meaning:
 
-  s             A pointer to the jbg_enc_state structure which you want
+  s             A pointer to the jbg_enc_state structure that you want
                 to initialize.
 
-  x             The width of your image.
+  x             The width of your image in pixels.
 
-  y             The height of your image.
+  y             The height of your image in pixels (lines).
 
   pl            the number of bitmap planes you want to encode.
 
@@ -251,36 +265,37 @@ The parameters have the following meaning:
                 pointing to the first byte of a bitmap as described in
                 section 2.1.
 
-  data_out      This is a call-back function which will be called during
-                the compression process by libjbig in order to deliver
-                the BIE data to the application. The parameters of the
-                function data_out are a pointer start to the new block of
-                data to be delivered as well as the number len of delivered
-                bytes. The pointer file is transparently delivered to
-                data_out as specified in jbg_enc_init(). Usually, data_out
-                will write the BIE portion to a file, send it to a
-                network connection or append it to some memory buffer.
-
-  file          A pointer parameter which is transparently passed to
-                data_out() and allows data_out() to distinguish by which
-                compression task it has been called in multi-threaded
-                applications.
+  data_out      This is a call-back function that the encoder will
+                call during the compression process by in order to
+                deliver the BIE data to your application. The
+                parameters of the function data_out are a pointer
+                start to the new block of data being delivered, as
+                well as the number len of delivered bytes. The pointer
+                file is transparently delivered to data_out, as
+                specified in jbg_enc_init(). Typically, data_out will
+                write the BIE portion to a file, send it to a network
+                connection, or append it to some memory buffer.
+
+  file          A pointer parameter that is passed on to data_out()
+                and can be used, for instance, to allow data_out() to
+                distinguish by which compression task it has been
+                called in multi-threaded applications.
 
 In the simplest case, the compression is then started by calling the
 function
 
   void jbg_enc_out(struct jbg_enc_state *s);
 
-which will deliver the complete BIE to data_out(). After this, a call
-to the destructor function
+which will deliver the complete BIE to data_out() in several calls.
+After jbg_enc_out has returned, a call to the destructor function
 
   void jbg_enc_free(struct jbg_enc_state *s);
 
-will release any memory allocated by the previous functions.
+will release any heap memory allocated by the previous functions.
 
 
-A minimal example application which sends the BIE of the above example
-bitmap to stdout looks like this:
+A minimal example application, which sends the BIE of the above bitmap
+to stdout, looks like this:
 
 ---------------------------------------------------------------------------
 /* A sample JBIG encoding application */
@@ -332,7 +347,7 @@ following default values are used for various compression parameters:
   - The number of lines per stripe is selected so that approximately
     35 stripes per image are used (as recommended in annex C of the
     standard together with the suggested adaptive template change
-    algorithm). However not less than 2 and not more than 128 lines
+    algorithm). However, not less than 2 and not more than 128 lines
     are used in order to stay within the suggested minimum parameter
     support range specified in annex A of the standard).
 
@@ -340,7 +355,7 @@ following default values are used for various compression parameters:
     TPDON and DPON).
 
   - The default resolution reduction table and the default deterministic
-    prediction tables are used
+    prediction table are used
 
   - The maximal vertical offset of the adaptive template pixel is 0
     and the maximal horizontal offset is 8 (mx = 8, my = 0).
@@ -354,14 +369,16 @@ with
   void jbg_enc_layers(struct jbg_enc_state *s, int d);
 
 the number d of differential resolution layers which shall be encoded
-in addition to the lowest resolution layer 0. For example, if a 300
-dpi document has to be stored and the lowest resolution layer shall
-have 75 dpi so that a screen previewer can directly decompress only
-the required resolution, then a call
+in addition to the lowest resolution layer 0. For example, if a
+document with 60-micrometer pixels has to be stored, and the lowest
+resolution layer shall have 240-micrometer pixels, so that a screen
+previewer can directly decompress only the required resolution, then a
+call
 
   jbg_enc_layers(&se, 2);
 
-will cause three resolution layers with 75, 150 and 300 dots per inch.
+will cause three layers with 240, 120 and 60 micrometers resolution to
+be generated.
 
 If the application does not know what typical resolutions are used and
 simply wants to ensure that the lowest resolution layer will fit into
@@ -381,26 +398,26 @@ resolution directly, then call
 The return value is the number of differential layers selected.
 
 After the number of resolution layers has been specified by calls to
-jbg_enc_layers() or jbg_enc_lrlmax(), by default all these layers will
-be written into the BIE. This can be changed with a call to
+jbg_enc_layers() or jbg_enc_lrlmax(), by default, all these layers
+will be written into the BIE. This can be changed with a call to
 
   int  jbg_enc_lrange(struct jbg_enc_state *s, int dl, int dh);
 
 Parameter dl specifies the lowest resolution layer and dh the highest
-resolution layer that will appear in the BIE. If e.g. only layer 0
+resolution layer that will appear in the BIE. For instance, if layer 0
 shall be written to the first BIE and layer 1 and 2 shall be written
-to a second one, then before writing the first BIE, one calls
+to a second one, then before writing the first BIE, call
 
   jbg_enc_lrange(&se, 0, 0);
 
-and before writing the second BIE with jbg_enc_out(), one calls
+and before writing the second BIE with jbg_enc_out(), call
 
   jbg_enc_lrange(&se, 1, 2);
 
 If any of the parameters is negative, it will be ignored. The return
-value is the total number of differential layers which will represent
+value is the total number of differential layers that will represent
 the input image. This way, jbg_enc_lrange(&se, -1, -1) can be used to
-query the layer of the full image.
+query the layer of the full image resolution.
 
 A number of other more exotic options of the JBIG algorithm can be
 modified by calling
@@ -418,14 +435,14 @@ the SDEs are stored in the BIE. The bits have the following meaning:
                the higher resolution layers, so that a decoder can
                already start to display a low resolution version of
                the full image once a prefix of the BIE has been
-               received. When this bit is set however, the BIE will
+               received. When this bit is set, however, the BIE will
                contain the higher layers before the lower layers. This
                avoids additional buffer memory in the encoder and is
                intended for applications where the encoder is connected
                to a database which can easily reorder the SDEs before
                sending them to a decoder. Warning: JBIG decoders are
                not expected to support the HITOLO option (e.g. the
-               JBIG-KIT decoder does currently not) so you should
+               jbig.c decoder currently does not) so you should
                normally not use it.
 
   JBG_SEQ      Usually, at first all stripes of one resolution layer
@@ -434,10 +451,10 @@ the SDEs are stored in the BIE. The bits have the following meaning:
                all layers of the first stripe will be written,
                followed by all layers of the second stripe, etc. This
                option also should normally never be required and is
-               not supported by the current JBIG-KIT decoder.
+               not supported by the current jbig.c decoder.
 
   JBG_SMID     In case there exist several bit planes, then the order of
-               the stripes is determined by 3 loops over all stripes,
+               the stripes is determined by three loops over all stripes,
                all planes and all layers. When SMID is set, the loop
                over all stripes is the middle loop.
 
@@ -445,8 +462,8 @@ the SDEs are stored in the BIE. The bits have the following meaning:
                plane are written before the encoder starts with the next
                plane.
 
-The above description might be somewhat confusing, but the following
-table (see also Table 11 in ITU-T T.82) makes clear how the three bits
+The above description may be somewhat confusing, but the following
+table (see also Table 11 in ITU-T T.82) clarifies how the three bits
 JBG_SEQ, JBIG_ILEAVE and JBG_SMID influence the ordering of the loops
 over all stripes, planes and layers:
 
@@ -482,15 +499,15 @@ some of the optional algorithms defined by JBIG:
                  the slightly faster but 5% less well compressing two
                  line alternative is selected. God bless the committees.
                  Although probably nobody will ever need this option,
-                 it has been implemented in JBIG-KIT and is off by
+                 it has been implemented in jbig.c and is off by
                  default.
 
   JBG_TPDON      This activates the "typical prediction" algorithm
                  for differential layers which avoids that large
-                 areas of equal color have to be encoded at all.
+                 areas of equal colour have to be encoded at all.
                  This is on by default and there is no good reason to
                  switch it off except for debugging or preparing data
-                 for cheap JBIG hardware which does not support this
+                 for cheap JBIG hardware that might not support this
                  option.
 
   JBG_TPBON      Like JBG_TPDON this activates the "typical prediction"
@@ -501,12 +518,12 @@ some of the optional algorithms defined by JBIG:
                  layers the "deterministic prediction" algorithm,
                  which avoids that higher resolution layer pixels are
                  encoded when their value can already be determined
-                 with the knowledge of the neighbor pixels, the
+                 with the knowledge of the neighbour pixels, the
                  corresponding lower resolution pixels and the
                  resolution reduction algorithm. This is also
-                 activated by default and one only might perhaps want
-                 to deactivate it if the default resolution reduction
-                 algorithm is replaced by a new one.
+                 activated by default and one reason for deactivating
+                 it would be if the default resolution reduction
+                 algorithm were replaced by another one.
 
   JBG_DELAY_AT   Use a slightly less efficient algorithm to determine
                  when an adaptive template change is necessary. With
@@ -514,31 +531,32 @@ some of the optional algorithms defined by JBIG:
                  conformance test examples in cause 7.2 of ITU-T T.82.
                  Then all adaptive template changes are delayed until
                  the first line of the next stripe. This option is by
-                 default deactivated and only required for passing a
+                 default deactivated and is only required for passing a
                  special compatibility test suite.
 
 In addition, parameter l0 in jbg_enc_options() allows you to specify
 the number of lines per stripe in resolution layer 0. The parameters
 mx and my change the maximal offset allowed for the adaptive template
-pixel. The JBIG-KIT implementation allows currently a maximal mx value
-of 23 in the encoder and 32 in the decoder. Parameter my is at the
-moment ignored and always set to 0. As the standard requires of all
-decoder implementations only a maximum supported mx = 16 and my = 0,
+pixel. JBIG-KIT now supports the full range of possible mx values up
+to 127 in the encoder and decoder, but my is at the moment ignored and
+always set to 0. As the standard requires of all decoder
+implementations only to support maximum values mx = 16 and my = 0,
 higher values should normally be avoided in order to guarantee
-interoperability. Default is mx = 8 and my = 0. If any of the
-parameters order, options, l0, mx or my is negative, then this value
-is ignored and the current value stays unmodified.
+interoperability. The ITU-T T.85 profile for JBIG in fax machines
+requires support for mx = 127 and my = 0. Default is mx = 8 and my =
+0. If any of the parameters order, options, mx or my is negative, or
+l0 is zero, then the corresponding current value remains unmodified.
 
 The resolution reduction and deterministic prediction tables can also
 be replaced. However as these options are anyway only for experts,
 please have a look at the source code of jbg_enc_out() and the struct
 members dppriv and res_tab of struct jbg_enc_state for the details of
-how to do this in case you really need it. The functions
+how to do this, in case you really need it. The functions
 jbg_int2dppriv and jbg_dppriv2int are provided in order to convert the
 DPTABLE data from the format used in the standard into the more
 efficient format used internally by JBIG-KIT.
 
-If you want to encode a greyscale image, you can use the library
+If you want to encode a grey-scale image, you can use the library
 function
 
   void jbg_split_planes(unsigned long x, unsigned long y, int has_planes,
@@ -547,8 +565,8 @@ function
                         int use_graycode);
 
 It separates an image in which each pixel is represented by one or
-more bytes into separate bitplanes. The dest array of pointers to
-these bitplanes can then be handed over to jbg_enc_init(). The
+more bytes into separate bit planes. The dest array of pointers to
+these bit planes can then be handed over to jbg_enc_init(). The
 variables x and y specify the width and height of the image in pixels,
 and has_planes specifies how many bits per pixel are used. As each
 pixel is represented by an integral number of consecutive bytes, of
@@ -557,7 +575,7 @@ image array src[] will therefore be x * y * ((has_planes + 7) / 8)
 bytes. The pixels are stored as usually in English reading order, and
 for each pixel the integer value is stored with the most significant
 byte coming first (Bigendian). This is exactly the format used in raw
-PGM files. In encode_planes, the number of bitplanes that shall be
+PGM files. In encode_planes, the number of bit planes that shall be
 extracted can be specified. This allows for instance to extract only
 the most significant 8 bits of a 12-bit image, where each pixel is
 represented by two bytes, by specifying has_planes = 12 and
@@ -568,7 +586,7 @@ the pixel integer values will be used instead of the Gray code. Plane
 
 3  Decompressing an image
 
-Like with the compression functions, if you want to use the JBIG-KIT
+Like with the compression functions, if you want to use the jbig.c
 library, you have to put the line
 
   #include "jbig.h"
@@ -603,19 +621,19 @@ will return the error JBG_ENOCONT after the header of the new BIE has
 been received completely.
 
 If pointer cnt is not NULL, then the number of bytes actually read
-from the data block is stored there. In case the data block did not
-contain the end of the BIE, then the value JBG_EAGAIN will be returned
-and *cnt equals len.
+from the data block will be stored there. In case the data block did
+not contain the end of the BIE, then the value JBG_EAGAIN will be
+returned and *cnt equals len.
 
 Once the end of a BIE has been reached, the return value of
 jbg_dec_in() will be JBG_EOK. After this has happened, the functions
 and macros
 
-  long jbg_dec_getwidth(struct jbg_dec_state *s);
-  long jbg_dec_getheight(struct jbg_dec_state *s);
+  unsigned long jbg_dec_getwidth(struct jbg_dec_state *s);
+  unsigned long jbg_dec_getheight(struct jbg_dec_state *s);
   int jbg_dec_getplanes(struct jbg_dec_state *s);
   unsigned char *jbg_dec_getimage(struct jbg_dec_state *s, int plane);
-  long jbg_dec_getsize(struct jbg_dec_state *s);
+  unsigned long jbg_dec_getsize(struct jbg_dec_state *s);
 
 can be used to query the dimensions of the now completely decoded
 image and to get a pointer to all bitmap planes. The bitmaps are
@@ -628,14 +646,14 @@ The function
                             void (*data_out)(unsigned char *start, size_t len,
                                              void *file), void *file);
 
-allows you to merge the bitplanes that can be accessed individually
+allows you to merge the bit planes that can be accessed individually
 with jbg_dec_getimage() into an array with one or more bytes per pixel
 (i.e., the format provided to jbg_split_planes()). If use_graycode is
 zero, then a binary encoding will be used. The output array will be
 delivered via the callback function data_out, exactly in the same way
 in which the encoder provides the BIE. The function
 
-  long jbg_dec_getsize_merged(const struct jbg_dec_state *s);
+  unsigned long jbg_dec_getsize_merged(const struct jbg_dec_state *s);
 
 determines how long the data array delivered by jbg_dec_merge_planes()
 is going to be.
@@ -683,34 +701,105 @@ and the memory can be released.
 
 The function
 
-  const char *jbg_strerror(int errnum, int language);
+  const char *jbg_strerror(int errnum);
 
-returns a pointer to a short single line test message which explains
+returns a pointer to a short single line test message that explains
 the return value of jbg_dec_in(). This message can be used in order to
 provide the user a brief informative message about what when wrong
-while decompressing the JBIG image. The error messages are available
-in several languages and in several character sets. Currently
-supported are the following values for the language parameter:
-
-  JBG_EN              English messages in ASCII
-  JBG_DE_8859_1       German messages in ISO 8859-1 Latin 1 character set
-  JBG_DE_UTF_8        German messages in ISO 10646/Unicode UTF-8 encoding
-
-
-The current implementation of the JBIG-KIT decoder has the following
+while decompressing a JBIG image. The po/ subdirectory contains *.po
+files that translate the English ASCII strings returned by
+jbg_strerror() into other languages (e.g., for use with GNU gettext).
+The four least-significant bits of the return value of jbg_dec_in()
+may contain additional detailed technical information about the exact
+test that spotted the error condition (see source code for details),
+i.e. more than the text message returned by jbg_strerror() reveals.
+Therefore it may be useful to display the return value itself as a
+hexadecimal number, in addition to the string returned by
+jbg_strerror().
+
+The current implementation of the jbig.c decoder has the following
 limitations:
 
-  - The maximal horizontal offset mx of the adaptive template pixel
-    must not be larger than 32 and the maximal vertical offset must
-    be zero.
+  - The maximal vertical offset MY of the adaptive template pixel
+    must be zero.
 
   - HITOLO and SEQ bits must not be set in the order value.
 
+  - Not more than JBG_ATMOVES_MAX (currently set to 64) ATMOVE
+    marker segments can be handled per stripe.
+
+  - the number D of differential layers must be less than 32
+
+None of the above limitations can be exceeded by a JBIG data stream
+that conforms to the ITU-T T.85 application profile for the use of
+JBIG1 in fax machines.
+
+The current implementation of the jbig.c decoder does not impose any
+limits on the image size that it will process, as long as malloc() is
+able to allocate enough heap space for the resulting bitmaps. The only
+exception is that jbg_dec_in() will return "Input data stream uses
+unimplemented JBIG features" (JBG_EIMPL | 1) if Y_D equals 0xffffffff,
+which is an extreme value commonly used to encode images according to
+ITU-T T.85 where the height was unknown when the BIH was emitted.
+After jbg_dec_in() received the 20-byte long BIH at the start of the
+BIE, it will malloc() to allocate enough memory to hold the requested
+image planes and layers. If you want to defend your application
+against excessive image-size parameters in a received BIH, then do
+make sure that you check X_D, Y_D, and P against appropriate safety
+limits before handing over the BIH to jbg_dec_in().
+
+There are two more limitations of the current implementation of the
+jbig.c decoder that might cause problems with processing JBIG data
+stream that conform to ITU-T T.85:
+
+  - The jbig.c decoder was designed to operate incrementally.
+    Each received byte is processed immediately as soon as it arrives.
+    As a result, it does not look beyond the SDRST/SDNORM at the end
+    of all stripes for any immediately following NEWLEN marker that
+    might reduce the number of lines encoded by the current stripe.
+    However section 6.2.6.2 of ITU-T T.82 says that a NEWLEN marker
+    segment "could refer to a line in the immediately preceding stripe
+    due to an unexpected termination of the image or the use of only
+    such stripe", and ITU-T.85 explicitly suggests the use of this
+    for fax machines that start transmission before having encountered
+    the end of the page.
+
+  - The image size initially indicated in the BIE header is used to
+    allocate memory for a bitmap of this size. This means that BIEs
+    that set initially Y_D = 0xffffffff (as suggested in ITU-T T.85
+    for fax machines that do not know the height of the page at the
+    start of the transmission) cannot be decoded directly by this
+    version.
+
+For both issues, there is a workaround available:
+
+If you encounter a BIE that has in the header the VLENGTH=1 option bit
+set, then first wait until you have received the entire BIE and stored
+it in memory. Then call the function
+
+  int jbg_newlen(unsigned char *bie, size_t len);
+
+where bie is a pointer to the first byte of the BIE and len its length
+in bytes. This function will scan the entire BIE for the first NEWLEN
+marker segment. It will then take the updated image-height value YD
+from it and use it to overwrite the YD value in the BIE header. The
+jbg_newlen() can return some of the same error codes as jbg_dec_in(),
+namely JBG_EOK if everything went fine, JBG_EAGAIN is the data
+provided is too short to be a valid BIE, JBG_EINVAL if a format error
+was encountered, and JBG_EABORT if an ABORT marker segment was found.
+After having patched the image-height value in the BIE using
+jbg_newlen(), simply hand over the BIE as usual to jbg_dec_in().
+
+In general, for applications where NEWLEN markers can appear, in
+particular fax reception, you should consider using the jbig85.c
+decoder instead, as it can process BIEs with NEWLEN markers in a
+single pass.
+
 A more detailed description of the JBIG-KIT implementation is
 
   Markus Kuhn: Effiziente Kompression von bi-level Bilddaten durch
   kontextsensitive arithmetische Codierung. Studienarbeit, Lehrstuhl
-  für Betriebssysteme, IMMD IV, Universität Erlangen-Nürnberg,
+  für Betriebssysteme, IMMD IV, Universität Erlangen-Nürnberg,
   Erlangen, July 1995. (German, 62 pages)
   <http://www.cl.cam.ac.uk/~mgk25/kuhn-sta.pdf>
 
diff --git a/converter/other/jbig/libjbig/jbig_ar.c b/converter/other/jbig/libjbig/jbig_ar.c
new file mode 100644
index 00000000..d23a317d
--- /dev/null
+++ b/converter/other/jbig/libjbig/jbig_ar.c
@@ -0,0 +1,417 @@
+/*
+ *  Arithmetic encoder and decoder of the portable JBIG
+ *  compression library
+ *
+ *  Markus Kuhn -- http://www.cl.cam.ac.uk/~mgk25/jbigkit/
+ *
+ *  This module implements a portable standard C arithmetic encoder
+ *  and decoder used by the JBIG lossless bi-level image compression
+ *  algorithm as specified in International Standard ISO 11544:1993
+ *  and ITU-T Recommendation T.82.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 
+ *  If you want to use this program under different license conditions,
+ *  then contact the author for an arrangement.
+ */
+
+#include <assert.h>
+#include "jbig_ar.h"
+
+/*
+ *  Probability estimation tables for the arithmetic encoder/decoder
+ *  given by ITU T.82 Table 24.
+ */
+
+static short lsztab[113] = {
+  0x5a1d, 0x2586, 0x1114, 0x080b, 0x03d8, 0x01da, 0x00e5, 0x006f,
+  0x0036, 0x001a, 0x000d, 0x0006, 0x0003, 0x0001, 0x5a7f, 0x3f25,
+  0x2cf2, 0x207c, 0x17b9, 0x1182, 0x0cef, 0x09a1, 0x072f, 0x055c,
+  0x0406, 0x0303, 0x0240, 0x01b1, 0x0144, 0x00f5, 0x00b7, 0x008a,
+  0x0068, 0x004e, 0x003b, 0x002c, 0x5ae1, 0x484c, 0x3a0d, 0x2ef1,
+  0x261f, 0x1f33, 0x19a8, 0x1518, 0x1177, 0x0e74, 0x0bfb, 0x09f8,
+  0x0861, 0x0706, 0x05cd, 0x04de, 0x040f, 0x0363, 0x02d4, 0x025c,
+  0x01f8, 0x01a4, 0x0160, 0x0125, 0x00f6, 0x00cb, 0x00ab, 0x008f,
+  0x5b12, 0x4d04, 0x412c, 0x37d8, 0x2fe8, 0x293c, 0x2379, 0x1edf,
+  0x1aa9, 0x174e, 0x1424, 0x119c, 0x0f6b, 0x0d51, 0x0bb6, 0x0a40,
+  0x5832, 0x4d1c, 0x438e, 0x3bdd, 0x34ee, 0x2eae, 0x299a, 0x2516,
+  0x5570, 0x4ca9, 0x44d9, 0x3e22, 0x3824, 0x32b4, 0x2e17, 0x56a8,
+  0x4f46, 0x47e5, 0x41cf, 0x3c3d, 0x375e, 0x5231, 0x4c0f, 0x4639,
+  0x415e, 0x5627, 0x50e7, 0x4b85, 0x5597, 0x504f, 0x5a10, 0x5522,
+  0x59eb
+};
+
+static unsigned char nmpstab[113] = {
+    1,   2,   3,   4,   5,   6,   7,   8,
+    9,  10,  11,  12,  13,  13,  15,  16,
+   17,  18,  19,  20,  21,  22,  23,  24,
+   25,  26,  27,  28,  29,  30,  31,  32,
+   33,  34,  35,   9,  37,  38,  39,  40,
+   41,  42,  43,  44,  45,  46,  47,  48,
+   49,  50,  51,  52,  53,  54,  55,  56,
+   57,  58,  59,  60,  61,  62,  63,  32,
+   65,  66,  67,  68,  69,  70,  71,  72,
+   73,  74,  75,  76,  77,  78,  79,  48,
+   81,  82,  83,  84,  85,  86,  87,  71,
+   89,  90,  91,  92,  93,  94,  86,  96,
+   97,  98,  99, 100,  93, 102, 103, 104,
+   99, 106, 107, 103, 109, 107, 111, 109,
+  111
+};
+
+/*
+ * least significant 7 bits (mask 0x7f) of nlpstab[] contain NLPS value,
+ * most significant bit (mask 0x80) contains SWTCH bit
+ */
+static unsigned char nlpstab[113] = {
+  129,  14,  16,  18,  20,  23,  25,  28,
+   30,  33,  35,   9,  10,  12, 143,  36,
+   38,  39,  40,  42,  43,  45,  46,  48,
+   49,  51,  52,  54,  56,  57,  59,  60,
+   62,  63,  32,  33, 165,  64,  65,  67,
+   68,  69,  70,  72,  73,  74,  75,  77,
+   78,  79,  48,  50,  50,  51,  52,  53,
+   54,  55,  56,  57,  58,  59,  61,  61,
+  193,  80,  81,  82,  83,  84,  86,  87,
+   87,  72,  72,  74,  74,  75,  77,  77,
+  208,  88,  89,  90,  91,  92,  93,  86,
+  216,  95,  96,  97,  99,  99,  93, 223,
+  101, 102, 103, 104,  99, 105, 106, 107,
+  103, 233, 108, 109, 110, 111, 238, 112,
+  240
+};
+
+/*
+ * The next functions implement the arithmedic encoder and decoder
+ * required for JBIG. The same algorithm is also used in the arithmetic
+ * variant of JPEG.
+ */
+
+/* marker codes */
+#define MARKER_STUFF    0x00
+#define MARKER_ESC      0xff
+
+void arith_encode_init(struct jbg_arenc_state *s, int reuse_st)
+{
+  int i;
+  
+  if (!reuse_st)
+    for (i = 0; i < 4096; s->st[i++] = 0) ;
+  s->c = 0;
+  s->a = 0x10000L;
+  s->sc = 0;
+  s->ct = 11;
+  s->buffer = -1;    /* empty */
+  
+  return;
+}
+
+
+void arith_encode_flush(struct jbg_arenc_state *s)
+{
+  unsigned long temp;
+
+  /* find the s->c in the coding interval with the largest
+   * number of trailing zero bits */
+  if ((temp = (s->a - 1 + s->c) & 0xffff0000L) < s->c)
+    s->c = temp + 0x8000;
+  else
+    s->c = temp;
+  /* send remaining bytes to output */
+  s->c <<= s->ct;
+  if (s->c & 0xf8000000L) {
+    /* one final overflow has to be handled */
+    if (s->buffer >= 0) {
+      s->byte_out(s->buffer + 1, s->file);
+      if (s->buffer + 1 == MARKER_ESC)
+	s->byte_out(MARKER_STUFF, s->file);
+    }
+    /* output 0x00 bytes only when more non-0x00 will follow */
+    if (s->c & 0x7fff800L)
+      for (; s->sc; --s->sc)
+	s->byte_out(0x00, s->file);
+  } else {
+    if (s->buffer >= 0)
+      s->byte_out(s->buffer, s->file); 
+    /* T.82 figure 30 says buffer+1 for the above line! Typo? */
+    for (; s->sc; --s->sc) {
+      s->byte_out(0xff, s->file);
+      s->byte_out(MARKER_STUFF, s->file);
+    }
+  }
+  /* output final bytes only if they are not 0x00 */
+  if (s->c & 0x7fff800L) {
+    s->byte_out((s->c >> 19) & 0xff, s->file);
+    if (((s->c >> 19) & 0xff) == MARKER_ESC)
+      s->byte_out(MARKER_STUFF, s->file);
+    if (s->c & 0x7f800L) {
+      s->byte_out((s->c >> 11) & 0xff, s->file);
+      if (((s->c >> 11) & 0xff) == MARKER_ESC)
+	s->byte_out(MARKER_STUFF, s->file);
+    }
+  }
+
+  return;
+}
+
+
+void arith_encode(struct jbg_arenc_state *s, int cx, int pix) 
+{
+  register unsigned lsz, ss;
+  register unsigned char *st;
+  long temp;
+
+  assert(cx >= 0 && cx < 4096);
+  st = s->st + cx;
+  ss = *st & 0x7f;
+  assert(ss < 113);
+  lsz = lsztab[ss];
+
+#if 0
+  fprintf(stderr, "pix = %d, cx = %d, mps = %d, st = %3d, lsz = 0x%04x, "
+	  "a = 0x%05lx, c = 0x%08lx, ct = %2d, buf = 0x%02x\n",
+	  pix, cx, !!(s->st[cx] & 0x80), ss, lsz, s->a, s->c, s->ct,
+	  s->buffer);
+#endif
+
+  if (((pix << 7) ^ s->st[cx]) & 0x80) {
+    /* encode the less probable symbol */
+    if ((s->a -= lsz) >= lsz) {
+      /* If the interval size (lsz) for the less probable symbol (LPS)
+       * is larger than the interval size for the MPS, then exchange
+       * the two symbols for coding efficiency, otherwise code the LPS
+       * as usual: */
+      s->c += s->a;
+      s->a = lsz;
+    }
+    /* Check whether MPS/LPS exchange is necessary
+     * and chose next probability estimator status */
+    *st &= 0x80;
+    *st ^= nlpstab[ss];
+  } else {
+    /* encode the more probable symbol */
+    if ((s->a -= lsz) & 0xffff8000L)
+      return;   /* A >= 0x8000 -> ready, no renormalization required */
+    if (s->a < lsz) {
+      /* If the interval size (lsz) for the less probable symbol (LPS)
+       * is larger than the interval size for the MPS, then exchange
+       * the two symbols for coding efficiency: */
+      s->c += s->a;
+      s->a = lsz;
+    }
+    /* chose next probability estimator status */
+    *st &= 0x80;
+    *st |= nmpstab[ss];
+  }
+
+  /* renormalization of coding interval */
+  do {
+    s->a <<= 1;
+    s->c <<= 1;
+    --s->ct;
+    if (s->ct == 0) {
+      /* another byte is ready for output */
+      temp = s->c >> 19;
+      if (temp & 0xffffff00L) {
+	/* handle overflow over all buffered 0xff bytes */
+	if (s->buffer >= 0) {
+	  ++s->buffer;
+	  s->byte_out(s->buffer, s->file);
+	  if (s->buffer == MARKER_ESC)
+	    s->byte_out(MARKER_STUFF, s->file);
+	}
+	for (; s->sc; --s->sc)
+	  s->byte_out(0x00, s->file);
+	s->buffer = temp & 0xff;  /* new output byte, might overflow later */
+	assert(s->buffer != 0xff);
+	/* can s->buffer really never become 0xff here? */
+      } else if (temp == 0xff) {
+	/* buffer 0xff byte (which might overflow later) */
+	++s->sc;
+      } else {
+	/* output all buffered 0xff bytes, they will not overflow any more */
+	if (s->buffer >= 0)
+	  s->byte_out(s->buffer, s->file);
+	for (; s->sc; --s->sc) {
+	  s->byte_out(0xff, s->file);
+	  s->byte_out(MARKER_STUFF, s->file);
+	}
+	s->buffer = temp;   /* buffer new output byte (can still overflow) */
+      }
+      s->c &= 0x7ffffL;
+      s->ct = 8;
+    }
+  } while (s->a < 0x8000);
+ 
+  return;
+}
+
+
+void arith_decode_init(struct jbg_ardec_state *s, int reuse_st)
+{
+  int i;
+  
+  if (!reuse_st)
+    for (i = 0; i < 4096; s->st[i++] = 0) ;
+  s->c = 0;
+  s->a = 1;
+  s->ct = 0;
+  s->startup = 1;
+  s->nopadding = 0;
+  return;
+}
+
+/*
+ * Decode and return one symbol from the provided PSCD byte stream
+ * that starts in s->pscd_ptr and ends in the byte before s->pscd_end.
+ * The context cx is a 12-bit integer in the range 0..4095. This
+ * function will advance s->pscd_ptr each time it has consumed all
+ * information from that PSCD byte.
+ *
+ * If a symbol has been decoded successfully, the return value will be
+ * 0 or 1 (depending on the symbol).
+ *
+ * If the decoder was not able to decode a symbol from the provided
+ * PSCD, then the return value will be -1, and two cases can be
+ * distinguished:
+ *
+ * s->pscd_ptr == s->pscd_end:
+ *
+ *   The decoder has used up all information in the provided PSCD
+ *   bytes. Further PSCD bytes have to be provided (via new values of
+ *   s->pscd_ptr and/or s->pscd_end) before another symbol can be
+ *   decoded.
+ *
+ * s->pscd_ptr == s->pscd_end - 1:
+ * 
+ *   The decoder has used up all provided PSCD bytes except for the
+ *   very last byte, because that has the value 0xff. The decoder can
+ *   at this point not yet tell whether this 0xff belongs to a
+ *   MARKER_STUFF sequence or marks the end of the PSCD. Further PSCD
+ *   bytes have to be provided (via new values of s->pscd_ptr and/or
+ *   s->pscd_end), including the not yet processed 0xff byte, before
+ *   another symbol can be decoded successfully.
+ *
+ * If s->nopadding != 0, the decoder will return -2 when it reaches
+ * the first two bytes of the marker segment that follows (and
+ * terminates) the PSCD, but before decoding the first symbol that
+ * depends on a bit in the input data that could have been the result
+ * of zero padding, and might, therefore, never have been encoded.
+ * This gives the caller the opportunity to lookahead early enough
+ * beyond a terminating SDNORM/SDRST for a trailing NEWLEN (as
+ * required by T.85) before decoding remaining symbols. Call the
+ * decoder again afterwards as often as necessary (leaving s->pscd_ptr
+ * pointing to the start of the marker segment) to retrieve any
+ * required remaining symbols that might depend on padding.
+ *
+ * [Note that each PSCD can be decoded into an infinitely long
+ * sequence of symbols, because the encoder might have truncated away
+ * an arbitrarily long sequence of trailing 0x00 bytes, which the
+ * decoder will append automatically as needed when it reaches the end
+ * of the PSCD. Therefore, the decoder cannot report any end of the
+ * symbol sequence and other means (external to the PSCD and
+ * arithmetic decoding process) are needed to determine that.]
+ */
+
+int arith_decode(struct jbg_ardec_state *s, int cx)
+{
+  register unsigned lsz, ss;
+  register unsigned char *st;
+  int pix;
+
+  /* renormalization */
+  while (s->a < 0x8000 || s->startup) {
+    while (s->ct <= 8 && s->ct >= 0) {
+      /* first we can move a new byte into s->c */
+      if (s->pscd_ptr >= s->pscd_end) {
+	return -1;  /* more bytes needed */
+      }
+      if (*s->pscd_ptr == 0xff) 
+	if (s->pscd_ptr + 1 >= s->pscd_end) {
+	  return -1; /* final 0xff byte not processed */
+	} else {
+	  if (*(s->pscd_ptr + 1) == MARKER_STUFF) {
+	    s->c |= 0xffL << (8 - s->ct);
+	    s->ct += 8;
+	    s->pscd_ptr += 2;
+	  } else {
+	    s->ct = -1; /* start padding with zero bytes */
+	    if (s->nopadding) {
+	      s->nopadding = 0;
+	      return -2; /* subsequent symbols might depend on zero padding */
+	    }
+	  }
+	}
+      else {
+	s->c |= (long)*(s->pscd_ptr++) << (8 - s->ct);
+	s->ct += 8;
+      }
+    }
+    s->c <<= 1;
+    s->a <<= 1;
+    if (s->ct >= 0) s->ct--;
+    if (s->a == 0x10000L)
+      s->startup = 0;
+  }
+
+  st = s->st + cx;
+  ss = *st & 0x7f;
+  assert(ss < 113);
+  lsz = lsztab[ss];
+
+#if 0
+  fprintf(stderr, "cx = %d, mps = %d, st = %3d, lsz = 0x%04x, a = 0x%05lx, "
+	  "c = 0x%08lx, ct = %2d\n",
+	  cx, !!(s->st[cx] & 0x80), ss, lsz, s->a, s->c, s->ct);
+#endif
+
+  if ((s->c >> 16) < (s->a -= lsz))
+    if (s->a & 0xffff8000L)
+      return *st >> 7;
+    else {
+      /* MPS_EXCHANGE */
+      if (s->a < lsz) {
+	pix = 1 - (*st >> 7);
+	/* Check whether MPS/LPS exchange is necessary
+	 * and chose next probability estimator status */
+	*st &= 0x80;
+	*st ^= nlpstab[ss];
+      } else {
+	pix = *st >> 7;
+	*st &= 0x80;
+	*st |= nmpstab[ss];
+      }
+    }
+  else {
+    /* LPS_EXCHANGE */
+    if (s->a < lsz) {
+      s->c -= s->a << 16;
+      s->a = lsz;
+      pix = *st >> 7;
+      *st &= 0x80;
+      *st |= nmpstab[ss];
+    } else {
+      s->c -= s->a << 16;
+      s->a = lsz;
+      pix = 1 - (*st >> 7);
+      /* Check whether MPS/LPS exchange is necessary
+       * and chose next probability estimator status */
+      *st &= 0x80;
+      *st ^= nlpstab[ss];
+    }
+  }
+
+  return pix;
+}
diff --git a/converter/other/jbig/pnmtojbig.c b/converter/other/jbig/pnmtojbig.c
index 9dbef3fa..f5188c7b 100644
--- a/converter/other/jbig/pnmtojbig.c
+++ b/converter/other/jbig/pnmtojbig.c
@@ -195,7 +195,7 @@ readPnm(FILE *            const fin,
     free(image);
     
     /* Invert the image if it is just one plane.  See top of this file
-       for an explanation why.  Due to the separate handling of PBM,
+       for an explanation why.  Because of the separate handling of PBM,
        this is for exceptional PGM files.  
     */
 
diff --git a/converter/other/jpeg2000/Makefile b/converter/other/jpeg2000/Makefile
index f2e3b4a1..6e5af8e7 100644
--- a/converter/other/jpeg2000/Makefile
+++ b/converter/other/jpeg2000/Makefile
@@ -5,21 +5,23 @@ endif
 SUBDIR = converter/other/jpeg2000
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-SUBDIRS = libjasper
+SUBDIRS =
 
 include $(BUILDDIR)/config.mk
 
 EXTERN_INCLUDES =
-ifneq ($(JASPERHDR_DIR),NONE)
-  EXTERN_INCLUDES += -I$(JASPERHDR_DIR)
-endif
-
 
 # INTERNAL_JASPERLIB must be relative to the current directory, because it
 # may end up in MERGE_OBJECTS, which must be relative.
 INTERNAL_JASPERLIB = libjasper/libjasper.a
 INTERNAL_JASPERHDR_DIR = $(SRCDIR)/$(SUBDIR)/libjasper/include
 
+ifneq ($(JASPERHDR_DIR),NONE)
+  ifneq ($(JASPERHDR_DIR)x,x)
+    EXTERN_INCLUDES += -I$(JASPERHDR_DIR)
+  endif
+endif
+
 ifeq ($(JASPERLIB),$(INTERNAL_JASPERLIB))
   ifeq ($(HAVE_INT64),Y)
     JASPERLIB_DEP = $(JASPERLIB)
@@ -38,17 +40,19 @@ endif
 
 ifneq ($(JASPERHDR_DIR),NONE)
   ifneq ($(JASPERLIB_USE),NONE)
-    BINARIES = pamtojpeg2k jpeg2ktopam
+    PORTBINARIES = pamtojpeg2k jpeg2ktopam
   endif
 endif
 
+BINARIES = $(PORTBINARIES)
 
 OBJECTS = $(BINARIES:%=%.o)
 MERGE_OBJECTS = $(BINARIES:%=%.o2) 
 ifeq ($(JASPERLIB),$(INTERNAL_JASPERLIB))
   # MERGE_OBJECTS contains relative paths, so $(INTERNAL_JASPERLIB) had better
-  # be relative to the current relative to the current directory.
+  # be relative to the current directory.
   MERGE_OBJECTS += $(JASPERLIB)
+  SUBDIRS += libjasper
 endif
 MERGEBINARIES = $(BINARIES)
 
@@ -57,12 +61,10 @@ all: $(BINARIES)
 
 include $(SRCDIR)/common.mk
 
-LIBOPTS = $(shell $(LIBOPT) $(JASPERLIB_USE) $(NETPBMLIB))
+LIBOPTS = $(shell $(LIBOPT) $(JASPERLIB_USE))
 
-$(BINARIES): %: %.o $(JASPERLIB_DEP) $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $< \
-	  $(LIBOPTS) $(JASPERDEPLIBS) $(MATHLIB) $(RPATH) \
-	  $(LDFLAGS) $(LDLIBS) $(LADD)
+$(BINARIES): %: %.o $(JASPERLIB_DEP) $(LIBOPT)
+$(BINARIES): LDFLAGS_TARGET = $(LIBOPTS) $(JASPERDEPLIBS)
 
 $(INTERNAL_JASPERLIB): $(BUILDDIR)/$(SUBDIR)/libjasper FORCE
 	$(MAKE) -f $(SRCDIR)/$(SUBDIR)/libjasper/Makefile \
diff --git a/converter/other/jpeg2000/jpeg2ktopam.c b/converter/other/jpeg2000/jpeg2ktopam.c
index e6db7658..405de9c9 100644
--- a/converter/other/jpeg2000/jpeg2ktopam.c
+++ b/converter/other/jpeg2000/jpeg2ktopam.c
@@ -9,17 +9,23 @@
 *****************************************************************************/
 
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
-/* Make sure strdup() is in string.h and int_fast32_t is in inttypes.h */
-#define _XOPEN_SOURCE 600
+#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */
+    /* In 2014.09, this was _XOPEN_SOURCE 600, with a comment saying it was
+       necessary to make <inttypes.h> define int_fast32_t, etc. on AIX.
+       <jasper/jasper.h> does use int_fast32_t and does include <inttypes.h>,
+       but plenty of source files of libjasper do to0, and they did not have
+       _XOPEN_SOURCE 600, so it would seem to be superfluous here too.
+    */
 #include <string.h>
 
+#include <jasper/jasper.h>
+
 #include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "nstring.h"
 #include "mallocvar.h"
 
-#include <jasper/jasper.h>
 #include "libjasper_compat.h"
 
 enum compmode {COMPMODE_INTEGER, COMPMODE_REAL};
@@ -41,7 +47,7 @@ parseCommandLine(int argc, char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
-   *cmdline_p structure are actually in the supplied argv array.  And
+   *cmdlineP structure are actually in the supplied argv array.  And
    sometimes, one of these strings is actually just a suffix of an entry
    in argv!
 -----------------------------------------------------------------------------*/
@@ -64,7 +70,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
 
     if (!debuglevelSpec)
         cmdlineP->debuglevel = 0;
@@ -82,36 +88,57 @@ parseCommandLine(int argc, char ** argv,
 
 
 static void
-readJpc(const char *   const inputFilename, 
+validateJ2k(jas_stream_t * const instreamP) {
+/*----------------------------------------------------------------------------
+   Abort program with error message if *instreamP is not a JPEG-2000 code
+   stream (JPC) or image file (JP2).
+-----------------------------------------------------------------------------*/
+    assert(jas_image_lookupfmtbyname("jpc"));
+    assert(jas_image_lookupfmtbyname("jp2"));
+
+    if (jas_image_lookupfmtbyname("jpc")->ops.validate(instreamP) != 0 &&
+        jas_image_lookupfmtbyname("jp2")->ops.validate(instreamP) != 0) {
+
+        pm_error("Input is not JPEG-2000 image file (JP2) "
+                 "or code stream (JPC).  "
+                 "(the first few bytes of the file are not the required "
+                 "signature)");
+    }
+}
+
+        
+
+
+static void
+readJ2k(const char *   const inputFilename, 
         jas_image_t ** const jasperPP) {
 
     jas_image_t * jasperP;
-    jas_stream_t *instream;
+    jas_stream_t * instreamP;
     const char * options;
 
-    if ( strcmp(inputFilename, "-") == 0) {
+    if (streq(inputFilename, "-")) {
         /* The input image is to be read from standard input. */
-        instream = jas_stream_fdopen(fileno(stdin), "rb");
-        if (instream == NULL)
+        instreamP = jas_stream_fdopen(fileno(stdin), "rb");
+        if (instreamP == NULL)
             pm_error("error: cannot reopen standard input");
     } else {
-        instream = jas_stream_fopen(inputFilename, "rb");
-        if (instream == NULL )
+        instreamP = jas_stream_fopen(inputFilename, "rb");
+        if (instreamP == NULL )
             pm_error("cannot open input image file '%s'", inputFilename);
     } 
 
-    if (jas_image_getfmt(instream) != jas_image_strtofmt((char*)"jpc"))
-        pm_error("Input is not JPEG-2000 code stream");
+    validateJ2k(instreamP);
 
     options = "";
 
-    jasperP = jas_image_decode(instream, jas_image_strtofmt((char*)"jpc"), 
+    jasperP = jas_image_decode(instreamP, jas_image_getfmt(instreamP),
                                (char*)options);
     if (jasperP == NULL)
         pm_error("Unable to interpret JPEG-2000 input.  "
                  "The Jasper library jas_image_decode() subroutine failed.");
 
-	jas_stream_close(instream);
+	jas_stream_close(instreamP);
 
     *jasperPP = jasperP;
 }
@@ -189,7 +216,7 @@ static void
 validateComponentsAlike(jas_image_t * const jasperP) {
 /*----------------------------------------------------------------------------
    JPC allows each component to have its own width and height.  But
-   PAM requires all planes to the same shape.  So we validate now that
+   PAM requires all planes to have the same shape.  So we validate now that
    all the channels are the same, and abort the program if not.
 -----------------------------------------------------------------------------*/
     int cmptNo;
@@ -406,8 +433,9 @@ convertToPamPnm(struct pam *  const outpamP,
     unsigned int row;
     tuple * tuplerow;
     jas_seqent_t ** jasperRow;   /* malloc'ed */
-       /* A row of a plane of the raster from the Jasper library 
-          This is an array of pointers into the 'matrix' data structures.
+       /* A row of the raster from the Jasper library This is an array of
+          pointers into the 'matrix' data structures, one for each plane in
+          the row.
        */
     bool singleMaxval;
 
@@ -479,7 +507,7 @@ main(int argc, char **argv)
     
     jas_setdbglevel(cmdline.debuglevel);
     
-    readJpc(cmdline.inputFilename, &jasperP);
+    readJ2k(cmdline.inputFilename, &jasperP);
 
     outpam.file = stdout;
     outpam.size = sizeof(outpam);
diff --git a/converter/other/jpeg2000/libjasper/README b/converter/other/jpeg2000/libjasper/README
index ad3e019b..b0512fe8 100644
--- a/converter/other/jpeg2000/libjasper/README
+++ b/converter/other/jpeg2000/libjasper/README
@@ -6,10 +6,12 @@ The adaptation was done by Bryan Henderson on 2002.10.26.
 
 The adaptation involved:
 
-  - remove stuff for formats other than PNM.
+  - Remove stuff for formats other than JPEG-2000.
 
   - Replace build stuff (Jasper uses GNU Autoconf/Automake/Libtool).
 
+  - Make JP2 decoder not dump the box to stderr unless debug is turned on.
+
 See <http://www.ece.uvic.ca/~mdadams/jasper/>.
 
 
diff --git a/converter/other/jpeg2000/libjasper/base/jas_image.c b/converter/other/jpeg2000/libjasper/base/jas_image.c
index e6439fcd..903b45c6 100644
--- a/converter/other/jpeg2000/libjasper/base/jas_image.c
+++ b/converter/other/jpeg2000/libjasper/base/jas_image.c
@@ -375,9 +375,9 @@ static void jas_image_cmpt_destroy(jas_image_cmpt_t *cmpt)
 	jas_free(cmpt);
 }
 
-/******************************************************************************\
+/*****************************************************************************\
 * Load and save operations.
-\******************************************************************************/
+\*****************************************************************************/
 
 jas_image_t *jas_image_decode(jas_stream_t *in, int fmt, char *optstr)
 {
@@ -623,7 +623,7 @@ int jas_image_getfmt(jas_stream_t *in)
 	int found;
 	int i;
 
-	/* Check for data in each of the supported formats. */
+	/* Check for data in each of the formats we know. */
 	found = 0;
 	for (i = 0, fmtinfo = jas_image_fmtinfos; i < jas_image_numfmts; ++i,
 	  ++fmtinfo) {
@@ -857,7 +857,7 @@ void jas_image_dump(jas_image_t *image, FILE *out)
 	}
 	for (cmptno = 0; cmptno < image->numcmpts_; ++cmptno) {
 		cmpt = image->cmpts_[cmptno];
-		fprintf(out, "prec=%d sgnd=%d\n", cmpt->prec_, cmpt->sgnd_);
+		fprintf(out, "prec=%d sgnd=%d\n", (int)cmpt->prec_, cmpt->sgnd_);
 		if (jas_image_readcmpt(image, cmptno, 0, 0, 1, 1, data)) {
 			abort();
 		}
diff --git a/converter/other/jpeg2000/libjasper/base/jas_seq.c b/converter/other/jpeg2000/libjasper/base/jas_seq.c
index b8e3c94b..12dc1595 100644
--- a/converter/other/jpeg2000/libjasper/base/jas_seq.c
+++ b/converter/other/jpeg2000/libjasper/base/jas_seq.c
@@ -414,7 +414,8 @@ int jas_matrix_output(jas_matrix_t *matrix, FILE *out)
 	int j;
 	jas_seqent_t x;
 
-	fprintf(out, "%d %d\n", jas_matrix_numrows(matrix), jas_matrix_numcols(matrix));
+	fprintf(out, "%d %d\n",
+            (int)jas_matrix_numrows(matrix), (int)jas_matrix_numcols(matrix));
 	for (i = 0; i < jas_matrix_numrows(matrix); ++i) {
 		for (j = 0; j < jas_matrix_numcols(matrix); ++j) {
 			x = jas_matrix_get(matrix, i, j);
diff --git a/converter/other/jpeg2000/libjasper/base/jas_stream.c b/converter/other/jpeg2000/libjasper/base/jas_stream.c
index 4c84e6c2..16c948eb 100644
--- a/converter/other/jpeg2000/libjasper/base/jas_stream.c
+++ b/converter/other/jpeg2000/libjasper/base/jas_stream.c
@@ -117,6 +117,7 @@
     */
 #define _XOPEN_SOURCE 500    /* Make sure P_tmpdir is defined */
 
+#include "pm_config.h"
 #include <assert.h>
 #include <fcntl.h>
 #include <stdlib.h>
@@ -126,11 +127,11 @@
 #if defined(HAVE_UNISTD_H)
 #include <unistd.h>
 #endif
-#if defined(WIN32) || defined(HAVE_IO_H)
+#if HAVE_IO_H
 #include <io.h>
 #endif
 
-#include "pm.h"
+#include "netpbm/pm.h"
 
 #include "jasper/jas_types.h"
 #include "jasper/jas_stream.h"
@@ -440,7 +441,7 @@ jas_stream_t *jas_stream_fdopen(int fd, const char *mode)
 	/* Parse the mode string. */
 	stream->openmode_ = jas_strtoopenmode(mode);
 
-#if defined(WIN32)
+#if defined(HAVE_SETMODE) && defined(O_BINARY)
 	/* Argh!!!  Someone ought to banish text mode (i.e., O_TEXT) to the
 	  greatest depths of purgatory! */
 	/* Ensure that the file descriptor is in binary mode, if the caller
@@ -902,7 +903,7 @@ int jas_stream_copy(jas_stream_t *out, jas_stream_t *in, int n)
 	while (all || m > 0) {
 		if ((c = jas_stream_getc_macro(in)) == EOF) {
 			/* The next character of input could not be read. */
-			/* Return with an error if an I/O error occured
+			/* Return with an error if an I/O error occurred
 			  (not including EOF) or if an explicit copy count
 			  was specified. */
 			return (!all || jas_stream_error(in)) ? (-1) : 0;
diff --git a/converter/other/jpeg2000/libjasper/common.mk b/converter/other/jpeg2000/libjasper/common.mk
index 687a9f3f..a333f5d6 100644
--- a/converter/other/jpeg2000/libjasper/common.mk
+++ b/converter/other/jpeg2000/libjasper/common.mk
@@ -13,19 +13,19 @@ partlist: $(SUBDIRS:%=%/partlist)
 	cat /dev/null $(SUBDIRS:%=%/partlist) >$@
 	echo $(LIB_OBJECTS:%=$(CURDIR)/%) >>$@
 
-.PHONY: $(SUBDIRS:%=%/partlist)
-$(SUBDIRS:%=%/partlist): %/partlist: $(CURDIR)/%
+$(SUBDIRS:%=%/partlist):
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 
 include $(SRCDIR)/common.mk
 
-INCLUDES = -I$(JASPERSRCDIR)/include -Iimportinc
+INCLUDES := -I$(JASPERSRCDIR)/include $(INCLUDES)
 
 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) $(CPPFLAGS) $(CFLAGS) $(CADD) $<
+	$(CC) -c $(INCLUDES) $(DEFS) $(CPPFLAGS) $(CFLAGS) \
+	  $(CFLAGS_PERSONAL) $(CADD) $<
 
 $(LIB_OBJECTS): importinc
 
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h
index d6456f7c..6e914efd 100644
--- a/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_image.h
@@ -123,7 +123,7 @@
 * Includes.
 \******************************************************************************/
 
-#include "pm_c_util.h"
+#include "netpbm/pm_c_util.h"
 #include <jasper/jas_stream.h>
 #include <jasper/jas_seq.h>
 
@@ -171,9 +171,9 @@ extern "C" {
 
 #define	JAS_IMAGE_CT_GRAY_Y	0
 
-/******************************************************************************\
+/*****************************************************************************\
 * Image class and supporting classes.
-\******************************************************************************/
+\*****************************************************************************/
 
 /* Image component class. */
 
@@ -294,7 +294,7 @@ typedef struct {
 \******************************************************************************/
 
 #define	JAS_IMAGE_MAXFMTS	32
-/* The maximum number of image data formats supported. */
+/* The maximum number of image data formats we can handle. */
 
 /* Image format-dependent operations. */
 
@@ -530,57 +530,57 @@ int jas_image_getfmt(jas_stream_t *in);
 * Image format-dependent operations.
 \******************************************************************************/
 
-#if !defined(EXCLUDE_JPG_SUPPORT)
-/* Format-dependent operations for JPG support. */
+#if !defined(EXCLUDE_JPG_CAPABILITY)
+/* Format-dependent operations for JPG capability. */
 jas_image_t *jpg_decode(jas_stream_t *in, char *optstr);
 int jpg_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
 int jpg_validate(jas_stream_t *in);
 #endif
 
-#if !defined(EXCLUDE_MIF_SUPPORT)
-/* Format-dependent operations for MIF support. */
+#if !defined(EXCLUDE_MIF_CAPABILITY)
+/* Format-dependent operations for MIF capability. */
 jas_image_t *mif_decode(jas_stream_t *in, char *optstr);
 int mif_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
 int mif_validate(jas_stream_t *in);
 #endif
 
-#if !defined(EXCLUDE_PNM_SUPPORT)
-/* Format-dependent operations for PNM support. */
+#if !defined(EXCLUDE_PNM_CAPABILITY)
+/* Format-dependent operations for PNM capability. */
 jas_image_t *pnm_decode(jas_stream_t *in, char *optstr);
 int pnm_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
 int pnm_validate(jas_stream_t *in);
 #endif
 
-#if !defined(EXCLUDE_RAS_SUPPORT)
-/* Format-dependent operations for Sun Rasterfile support. */
+#if !defined(EXCLUDE_RAS_CAPABILITY)
+/* Format-dependent operations for Sun Rasterfile capability. */
 jas_image_t *ras_decode(jas_stream_t *in, char *optstr);
 int ras_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
 int ras_validate(jas_stream_t *in);
 #endif
 
-#if !defined(EXCLUDE_BMP_SUPPORT)
-/* Format-dependent operations for BMP support. */
+#if !defined(EXCLUDE_BMP_CAPABILITY)
+/* Format-dependent operations for BMP capability. */
 jas_image_t *bmp_decode(jas_stream_t *in, char *optstr);
 int bmp_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
 int bmp_validate(jas_stream_t *in);
 #endif
 
-#if !defined(EXCLUDE_JP2_SUPPORT)
-/* Format-dependent operations for JP2 support. */
+#if !defined(EXCLUDE_JP2_CAPABILITY)
+/* Format-dependent operations for JP2 capability. */
 jas_image_t *jp2_decode(jas_stream_t *in, char *optstr);
 int jp2_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
 int jp2_validate(jas_stream_t *in);
 #endif
 
-#if !defined(EXCLUDE_JPC_SUPPORT)
-/* Format-dependent operations for JPEG-2000 code stream support. */
+#if !defined(EXCLUDE_JPC_CAPABILITY)
+/* Format-dependent operations for JPEG-2000 code stream capability. */
 jas_image_t *jpc_decode(jas_stream_t *in, char *optstr);
 int jpc_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
 int jpc_validate(jas_stream_t *in);
 #endif
 
-#if !defined(EXCLUDE_PGX_SUPPORT)
-/* Format-dependent operations for PGX support. */
+#if !defined(EXCLUDE_PGX_CAPABILITY)
+/* Format-dependent operations for PGX capability. */
 jas_image_t *pgx_decode(jas_stream_t *in, char *optstr);
 int pgx_encode(jas_image_t *image, jas_stream_t *out, char *optstr);
 int pgx_validate(jas_stream_t *in);
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h
index 4d3a4988..fbcb2ffb 100644
--- a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h
@@ -4,7 +4,7 @@
    In Netpbm, we do that with pm_config.h, and the original Jasper
    method doesn't work.
 */
-#include "pm_config.h"
+#include "netpbm/pm_config.h"
 
 
 /* The below macro is intended to be used for type casts.  By using this
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig
deleted file mode 100644
index 10c1152d..00000000
--- a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * 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/jp2_cod.c b/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c
index 4769c408..c99c9608 100644
--- a/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c
+++ b/converter/other/jpeg2000/libjasper/jp2/jp2_cod.c
@@ -123,7 +123,7 @@
 #include <assert.h>
 #include <stdlib.h>
 
-#include "pm_c_util.h"
+#include "netpbm/pm_c_util.h"
 
 #include "jasper/jas_stream.h"
 #include "jasper/jas_malloc.h"
@@ -135,7 +135,7 @@
 * Function prototypes.
 \******************************************************************************/
 
-#define	ONES(n)	((1 << (n)) - 1)
+#define ONES(n) ((1 << (n)) - 1)
 
 jp2_boxinfo_t *jp2_boxinfolookup(int type);
 
@@ -183,49 +183,49 @@ static void jp2_pclr_dumpdata(jp2_box_t *box, FILE *out);
 \******************************************************************************/
 
 jp2_boxinfo_t jp2_boxinfos[] = {
-	{JP2_BOX_JP, "JP", 0,
-	  {0, 0, jp2_jp_getdata, jp2_jp_putdata, 0}},
-	{JP2_BOX_FTYP, "FTYP", 0,
-	  {0, 0, jp2_ftyp_getdata, jp2_ftyp_putdata, 0}},
-	{JP2_BOX_JP2H, "JP2H", JP2_BOX_SUPER,
-	  {0, 0, 0, 0, 0}},
-	{JP2_BOX_IHDR, "IHDR", 0,
-	  {0, 0, jp2_ihdr_getdata, jp2_ihdr_putdata, 0}},
-	{JP2_BOX_BPCC, "BPCC", 0,
-	  {0, jp2_bpcc_destroy, jp2_bpcc_getdata, jp2_bpcc_putdata, 0}},
-	{JP2_BOX_COLR, "COLR", 0,
-	  {0, jp2_colr_destroy, jp2_colr_getdata, jp2_colr_putdata, jp2_colr_dumpdata}},
-	{JP2_BOX_PCLR, "PCLR", 0,
-	  {0, jp2_pclr_destroy, jp2_pclr_getdata, jp2_pclr_putdata, jp2_pclr_dumpdata}},
-	{JP2_BOX_CMAP, "CMAP", 0,
-	  {0, jp2_cmap_destroy, jp2_cmap_getdata, jp2_cmap_putdata, jp2_cmap_dumpdata}},
-	{JP2_BOX_CDEF, "CDEF", 0,
-	  {0, jp2_cdef_destroy, jp2_cdef_getdata, jp2_cdef_putdata, jp2_cdef_dumpdata}},
-	{JP2_BOX_RES, "RES", JP2_BOX_SUPER,
-	  {0, 0, 0, 0, 0}},
-	{JP2_BOX_RESC, "RESC", 0,
-	  {0, 0, 0, 0, 0}},
-	{JP2_BOX_RESD, "RESD", 0,
-	  {0, 0, 0, 0, 0}},
-	{JP2_BOX_JP2C, "JP2C", JP2_BOX_NODATA,
-	  {0, 0, 0, 0, 0}},
-	{JP2_BOX_JP2I, "JP2I", 0,
-	  {0, 0, 0, 0, 0}},
-	{JP2_BOX_XML, "XML", 0,
-	  {0, 0, 0, 0, 0}},
-	{JP2_BOX_UUID, "UUID", 0,
-	  {0, 0, 0, 0, 0}},
-	{JP2_BOX_UINF, "UINF", JP2_BOX_SUPER,
-	  {0, 0, 0, 0, 0}},
-	{JP2_BOX_ULST, "ULST", 0,
-	  {0, 0, 0, 0, 0}},
-	{JP2_BOX_URL, "URL", 0,
-	  {0, 0, 0, 0, 0}},
-	{0, 0, 0, {0, 0, 0, 0, 0}},
+    {JP2_BOX_JP, "JP", 0,
+      {0, 0, jp2_jp_getdata, jp2_jp_putdata, 0}},
+    {JP2_BOX_FTYP, "FTYP", 0,
+      {0, 0, jp2_ftyp_getdata, jp2_ftyp_putdata, 0}},
+    {JP2_BOX_JP2H, "JP2H", JP2_BOX_SUPER,
+      {0, 0, 0, 0, 0}},
+    {JP2_BOX_IHDR, "IHDR", 0,
+      {0, 0, jp2_ihdr_getdata, jp2_ihdr_putdata, 0}},
+    {JP2_BOX_BPCC, "BPCC", 0,
+      {0, jp2_bpcc_destroy, jp2_bpcc_getdata, jp2_bpcc_putdata, 0}},
+    {JP2_BOX_COLR, "COLR", 0,
+      {0, jp2_colr_destroy, jp2_colr_getdata, jp2_colr_putdata, jp2_colr_dumpdata}},
+    {JP2_BOX_PCLR, "PCLR", 0,
+      {0, jp2_pclr_destroy, jp2_pclr_getdata, jp2_pclr_putdata, jp2_pclr_dumpdata}},
+    {JP2_BOX_CMAP, "CMAP", 0,
+      {0, jp2_cmap_destroy, jp2_cmap_getdata, jp2_cmap_putdata, jp2_cmap_dumpdata}},
+    {JP2_BOX_CDEF, "CDEF", 0,
+      {0, jp2_cdef_destroy, jp2_cdef_getdata, jp2_cdef_putdata, jp2_cdef_dumpdata}},
+    {JP2_BOX_RES, "RES", JP2_BOX_SUPER,
+      {0, 0, 0, 0, 0}},
+    {JP2_BOX_RESC, "RESC", 0,
+      {0, 0, 0, 0, 0}},
+    {JP2_BOX_RESD, "RESD", 0,
+      {0, 0, 0, 0, 0}},
+    {JP2_BOX_JP2C, "JP2C", JP2_BOX_NODATA,
+      {0, 0, 0, 0, 0}},
+    {JP2_BOX_JP2I, "JP2I", 0,
+      {0, 0, 0, 0, 0}},
+    {JP2_BOX_XML, "XML", 0,
+      {0, 0, 0, 0, 0}},
+    {JP2_BOX_UUID, "UUID", 0,
+      {0, 0, 0, 0, 0}},
+    {JP2_BOX_UINF, "UINF", JP2_BOX_SUPER,
+      {0, 0, 0, 0, 0}},
+    {JP2_BOX_ULST, "ULST", 0,
+      {0, 0, 0, 0, 0}},
+    {JP2_BOX_URL, "URL", 0,
+      {0, 0, 0, 0, 0}},
+    {0, 0, 0, {0, 0, 0, 0, 0}},
 };
 
 jp2_boxinfo_t jp2_boxinfo_unk = {
-	0, "Unknown", 0, {0, 0, 0, 0}
+    0, "Unknown", 0, {0, 0, 0, 0}
 };
 
 /******************************************************************************\
@@ -234,21 +234,21 @@ jp2_boxinfo_t jp2_boxinfo_unk = {
 
 jp2_box_t *jp2_box_create(int type)
 {
-	jp2_box_t *box;
-	jp2_boxinfo_t *boxinfo;
+    jp2_box_t *box;
+    jp2_boxinfo_t *boxinfo;
 
-	if (!(box = jas_malloc(sizeof(jp2_box_t)))) {
-		return 0;
-	}
-	memset(box, 0, sizeof(jp2_box_t));
-	box->type = type;
-	box->len = 0;
-	if (!(boxinfo = jp2_boxinfolookup(type))) {
-		return 0;
-	}
-	box->info = boxinfo;
-	box->ops = &boxinfo->ops;
-	return box;
+    if (!(box = jas_malloc(sizeof(jp2_box_t)))) {
+        return 0;
+    }
+    memset(box, 0, sizeof(jp2_box_t));
+    box->type = type;
+    box->len = 0;
+    if (!(boxinfo = jp2_boxinfolookup(type))) {
+        return 0;
+    }
+    box->info = boxinfo;
+    box->ops = &boxinfo->ops;
+    return box;
 }
 
 /******************************************************************************\
@@ -257,28 +257,28 @@ jp2_box_t *jp2_box_create(int type)
 
 void jp2_box_destroy(jp2_box_t *box)
 {
-	if (box->ops->destroy) {
-		(*box->ops->destroy)(box);
-	}
-	jas_free(box);
+    if (box->ops->destroy) {
+        (*box->ops->destroy)(box);
+    }
+    jas_free(box);
 }
 
 static void jp2_bpcc_destroy(jp2_box_t *box)
 {
-	jp2_bpcc_t *bpcc = &box->data.bpcc;
-	if (bpcc->bpcs) {
-		jas_free(bpcc->bpcs);
-		bpcc->bpcs = 0;
-	}
+    jp2_bpcc_t *bpcc = &box->data.bpcc;
+    if (bpcc->bpcs) {
+        jas_free(bpcc->bpcs);
+        bpcc->bpcs = 0;
+    }
 }
 
 static void jp2_cdef_destroy(jp2_box_t *box)
 {
-	jp2_cdef_t *cdef = &box->data.cdef;
-	if (cdef->ents) {
-		jas_free(cdef->ents);
-		cdef->ents = 0;
-	}
+    jp2_cdef_t *cdef = &box->data.cdef;
+    if (cdef->ents) {
+        jas_free(cdef->ents);
+        cdef->ents = 0;
+    }
 }
 
 /******************************************************************************\
@@ -287,222 +287,224 @@ static void jp2_cdef_destroy(jp2_box_t *box)
 
 jp2_box_t *jp2_box_get(jas_stream_t *in)
 {
-	jp2_box_t *box;
-	jp2_boxinfo_t *boxinfo;
-	jas_stream_t *tmpstream;
-	uint_fast32_t len;
+    jp2_box_t *box;
+    jp2_boxinfo_t *boxinfo;
+    jas_stream_t *tmpstream;
+    uint_fast32_t len;
     uint_fast64_t extlen;
-	bool dataflag;
-
-	box = 0;
-	tmpstream = 0;
-
-	if (!(box = jas_malloc(sizeof(jp2_box_t)))) {
-		goto error;
-	}
-	box->ops = &jp2_boxinfo_unk.ops;
-	if (jp2_getuint32(in, &len) || jp2_getuint32(in, &box->type)) {
-		goto error;
-	}
-	boxinfo = jp2_boxinfolookup(box->type);
-	box->info = boxinfo;
-	box->ops = &boxinfo->ops;
-	box->len = len;
-	if (box->len == 1) {
-		if (jp2_getuint64(in, &extlen)) {
-			goto error;
-		}
-		box->len = extlen;
-	}
-	if (box->len != 0 && box->len < 8) {
-		goto error;
-	}
-
-	dataflag = !(box->info->flags & (JP2_BOX_SUPER | JP2_BOX_NODATA));
-
-	if (dataflag) {
-		if (!(tmpstream = jas_stream_memopen(0, 0))) {
-			goto error;
-		}
-		if (jas_stream_copy(tmpstream, in, box->len - JP2_BOX_HDRLEN)) {
-			goto error;
-		}
-		jas_stream_rewind(tmpstream);
-
-		if (box->ops->getdata) {
-			if ((*box->ops->getdata)(box, tmpstream)) {
-				goto error;
-			}
-		}
-		jas_stream_close(tmpstream);
-	}
-
-	jp2_box_dump(box, stderr);
-
-	return box;
-	abort();
+    bool dataflag;
+
+    box = 0;
+    tmpstream = 0;
+
+    if (!(box = jas_malloc(sizeof(jp2_box_t)))) {
+        goto error;
+    }
+    box->ops = &jp2_boxinfo_unk.ops;
+    if (jp2_getuint32(in, &len) || jp2_getuint32(in, &box->type)) {
+        goto error;
+    }
+    boxinfo = jp2_boxinfolookup(box->type);
+    box->info = boxinfo;
+    box->ops = &boxinfo->ops;
+    box->len = len;
+    if (box->len == 1) {
+        if (jp2_getuint64(in, &extlen)) {
+            goto error;
+        }
+        box->len = extlen;
+    }
+    if (box->len != 0 && box->len < 8) {
+        goto error;
+    }
+
+    dataflag = !(box->info->flags & (JP2_BOX_SUPER | JP2_BOX_NODATA));
+
+    if (dataflag) {
+        if (!(tmpstream = jas_stream_memopen(0, 0))) {
+            goto error;
+        }
+        if (jas_stream_copy(tmpstream, in, box->len - JP2_BOX_HDRLEN)) {
+            goto error;
+        }
+        jas_stream_rewind(tmpstream);
+
+        if (box->ops->getdata) {
+            if ((*box->ops->getdata)(box, tmpstream)) {
+                goto error;
+            }
+        }
+        jas_stream_close(tmpstream);
+    }
+
+    if (jas_getdbglevel() > 0) {
+        jp2_box_dump(box, stderr);
+    }
+    return box;
+    abort();
 
 error:
-	if (box) {
-		jp2_box_destroy(box);
-	}
-	if (tmpstream) {
-		jas_stream_close(tmpstream);
-	}
-	return 0;
+    if (box) {
+        jp2_box_destroy(box);
+    }
+    if (tmpstream) {
+        jas_stream_close(tmpstream);
+    }
+    return 0;
 }
 
 void jp2_box_dump(jp2_box_t *box, FILE *out)
 {
-	jp2_boxinfo_t *boxinfo;
-	boxinfo = jp2_boxinfolookup(box->type);
-	assert(boxinfo);
+    jp2_boxinfo_t *boxinfo;
+    boxinfo = jp2_boxinfolookup(box->type);
+    assert(boxinfo);
 
-	fprintf(out, "JP2 box: ");
-	fprintf(out, "type=%c%s%c (0x%08x); length=%d\n", '"', boxinfo->name,
-	  '"', box->type, box->len);
-	if (box->ops->dumpdata) {
-		(*box->ops->dumpdata)(box, out);
-	}
+    fprintf(out, "JP2 box: ");
+    fprintf(out, "type=%c%s%c (0x%08x); length=%d\n", '"', boxinfo->name,
+            '"', (unsigned int) box->type, (int)box->len);
+    if (box->ops->dumpdata) {
+        (*box->ops->dumpdata)(box, out);
+    }
 }
 
 static int jp2_jp_getdata(jp2_box_t *box, jas_stream_t *in)
 {
-	jp2_jp_t *jp = &box->data.jp;
-	if (jp2_getuint32(in, &jp->magic)) {
-		return -1;
-	}
-	return 0;
+    jp2_jp_t *jp = &box->data.jp;
+    if (jp2_getuint32(in, &jp->magic)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jp2_ftyp_getdata(jp2_box_t *box, jas_stream_t *in)
 {
-	jp2_ftyp_t *ftyp = &box->data.ftyp;
-	int i;
-	if (jp2_getuint32(in, &ftyp->majver) || jp2_getuint32(in, &ftyp->minver)) {
-		return -1;
-	}
-	ftyp->numcompatcodes = ((box->len - JP2_BOX_HDRLEN) - 8) / 4;
-	if (ftyp->numcompatcodes > JP2_FTYP_MAXCOMPATCODES) {
-		return -1;
-	}
-	for (i = 0; i < ftyp->numcompatcodes; ++i) {
-		if (jp2_getuint32(in, &ftyp->compatcodes[i])) {
-			return -1;
-		}
-	}
-	return 0;
+    jp2_ftyp_t *ftyp = &box->data.ftyp;
+    int i;
+    if (jp2_getuint32(in, &ftyp->majver) || jp2_getuint32(in, &ftyp->minver)) {
+        return -1;
+    }
+    ftyp->numcompatcodes = ((box->len - JP2_BOX_HDRLEN) - 8) / 4;
+    if (ftyp->numcompatcodes > JP2_FTYP_MAXCOMPATCODES) {
+        return -1;
+    }
+    for (i = 0; i < ftyp->numcompatcodes; ++i) {
+        if (jp2_getuint32(in, &ftyp->compatcodes[i])) {
+            return -1;
+        }
+    }
+    return 0;
 }
 
 static int jp2_ihdr_getdata(jp2_box_t *box, jas_stream_t *in)
 {
-	jp2_ihdr_t *ihdr = &box->data.ihdr;
-	if (jp2_getuint32(in, &ihdr->height) || jp2_getuint32(in, &ihdr->width) ||
-	  jp2_getuint16(in, &ihdr->numcmpts) || jp2_getuint8(in, &ihdr->bpc) ||
-	  jp2_getuint8(in, &ihdr->comptype) || jp2_getuint8(in, &ihdr->csunk) ||
-	  jp2_getuint8(in, &ihdr->ipr)) {
-		return -1;
-	}
-	return 0;
+    jp2_ihdr_t *ihdr = &box->data.ihdr;
+    if (jp2_getuint32(in, &ihdr->height) || jp2_getuint32(in, &ihdr->width) ||
+      jp2_getuint16(in, &ihdr->numcmpts) || jp2_getuint8(in, &ihdr->bpc) ||
+      jp2_getuint8(in, &ihdr->comptype) || jp2_getuint8(in, &ihdr->csunk) ||
+      jp2_getuint8(in, &ihdr->ipr)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jp2_bpcc_getdata(jp2_box_t *box, jas_stream_t *in)
 {
-	jp2_bpcc_t *bpcc = &box->data.bpcc;
-	int i;
-	bpcc->numcmpts = box->len - JP2_BOX_HDRLEN;
-	if (!(bpcc->bpcs = jas_malloc(bpcc->numcmpts * sizeof(uint_fast8_t)))) {
-		return -1;
-	}
-	for (i = 0; i < bpcc->numcmpts; ++i) {
-		if (jp2_getuint8(in, &bpcc->bpcs[i])) {
-			return -1;
-		}
-	}
-	return 0;
+    jp2_bpcc_t *bpcc = &box->data.bpcc;
+    int i;
+    bpcc->numcmpts = box->len - JP2_BOX_HDRLEN;
+    if (!(bpcc->bpcs = jas_malloc(bpcc->numcmpts * sizeof(uint_fast8_t)))) {
+        return -1;
+    }
+    for (i = 0; i < bpcc->numcmpts; ++i) {
+        if (jp2_getuint8(in, &bpcc->bpcs[i])) {
+            return -1;
+        }
+    }
+    return 0;
 }
 
 static void jp2_colr_dumpdata(jp2_box_t *box, FILE *out)
 {
-	jp2_colr_t *colr = &box->data.colr;
-	fprintf(out, "method=%d; pri=%d; approx=%d\n", (int)colr->method, (int)colr->pri, (int)colr->approx);
-	switch (colr->method) {
-	case JP2_COLR_ENUM:
-		fprintf(out, "csid=%d\n", (int)colr->csid);
-		break;
-	case JP2_COLR_ICC:
-		jas_memdump(out, colr->iccp, colr->iccplen);
-		break;
-	}
+    jp2_colr_t *colr = &box->data.colr;
+    fprintf(out, "method=%d; pri=%d; approx=%d\n", (int)colr->method, (int)colr->pri, (int)colr->approx);
+    switch (colr->method) {
+    case JP2_COLR_ENUM:
+        fprintf(out, "csid=%d\n", (int)colr->csid);
+        break;
+    case JP2_COLR_ICC:
+        jas_memdump(out, colr->iccp, colr->iccplen);
+        break;
+    }
 }
 
 static int jp2_colr_getdata(jp2_box_t *box, jas_stream_t *in)
 {
-	jp2_colr_t *colr = &box->data.colr;
-	colr->csid = 0;
-	colr->iccp = 0;
-	colr->iccplen = 0;
-
-	if (jp2_getuint8(in, &colr->method) || jp2_getuint8(in, &colr->pri) ||
-	  jp2_getuint8(in, &colr->approx)) {
-		return -1;
-	}
-	switch (colr->method) {
-	case JP2_COLR_ENUM:
-		if (jp2_getuint32(in, &colr->csid)) {
-			return -1;
-		}
-		break;
-	case JP2_COLR_ICC:
-		colr->iccplen = box->len - JP2_BOX_HDRLEN - 3;
-		if (!(colr->iccp = jas_malloc(colr->iccplen * sizeof(uint_fast8_t)))) {
-			return -1;
-		}
-		if (jas_stream_read(in, colr->iccp, colr->iccplen) != colr->iccplen) {
-			return -1;
-		}
-		break;
-	}
-	return 0;
+    jp2_colr_t *colr = &box->data.colr;
+    colr->csid = 0;
+    colr->iccp = 0;
+    colr->iccplen = 0;
+
+    if (jp2_getuint8(in, &colr->method) || jp2_getuint8(in, &colr->pri) ||
+      jp2_getuint8(in, &colr->approx)) {
+        return -1;
+    }
+    switch (colr->method) {
+    case JP2_COLR_ENUM:
+        if (jp2_getuint32(in, &colr->csid)) {
+            return -1;
+        }
+        break;
+    case JP2_COLR_ICC:
+        colr->iccplen = box->len - JP2_BOX_HDRLEN - 3;
+        if (!(colr->iccp = jas_malloc(colr->iccplen * sizeof(uint_fast8_t)))) {
+            return -1;
+        }
+        if (jas_stream_read(in, colr->iccp, colr->iccplen) != colr->iccplen) {
+            return -1;
+        }
+        break;
+    }
+    return 0;
 }
 
 static void jp2_cdef_dumpdata(jp2_box_t *box, FILE *out)
 {
-	jp2_cdef_t *cdef = &box->data.cdef;
-	int i;
-	for (i = 0; i < cdef->numchans; ++i) {
-		fprintf(out, "channo=%d; type=%d; assoc=%d\n",
-		  cdef->ents[i].channo, cdef->ents[i].type, cdef->ents[i].assoc);
-	}
+    jp2_cdef_t *cdef = &box->data.cdef;
+    int i;
+    for (i = 0; i < cdef->numchans; ++i) {
+        fprintf(out, "channo=%d; type=%d; assoc=%d\n",
+                (int)cdef->ents[i].channo, (int)cdef->ents[i].type,
+                (int)cdef->ents[i].assoc);
+    }
 }
 
 static void jp2_colr_destroy(jp2_box_t *box)
 {
-	jp2_colr_t *colr = &box->data.colr;
-	if (colr->iccp) {
-		free(colr->iccp);
-	}
+    jp2_colr_t *colr = &box->data.colr;
+    if (colr->iccp) {
+        free(colr->iccp);
+    }
 }
 
 static int jp2_cdef_getdata(jp2_box_t *box, jas_stream_t *in)
 {
-	jp2_cdef_t *cdef = &box->data.cdef;
-	jp2_cdefchan_t *chan;
-	int channo;
-	if (jp2_getuint16(in, &cdef->numchans)) {
-		return -1;
-	}
-	if (!(cdef->ents = jas_malloc(cdef->numchans * sizeof(jp2_cdefchan_t)))) {
-		return -1;
-	}
-	for (channo = 0; channo < cdef->numchans; ++channo) {
-		chan = &cdef->ents[channo];
-		if (jp2_getuint16(in, &chan->channo) || jp2_getuint16(in, &chan->type) ||
-		  jp2_getuint16(in, &chan->assoc)) {
-			return -1;
-		}
-	}
-	return 0;
+    jp2_cdef_t *cdef = &box->data.cdef;
+    jp2_cdefchan_t *chan;
+    int channo;
+    if (jp2_getuint16(in, &cdef->numchans)) {
+        return -1;
+    }
+    if (!(cdef->ents = jas_malloc(cdef->numchans * sizeof(jp2_cdefchan_t)))) {
+        return -1;
+    }
+    for (channo = 0; channo < cdef->numchans; ++channo) {
+        chan = &cdef->ents[channo];
+        if (jp2_getuint16(in, &chan->channo) || jp2_getuint16(in, &chan->type) ||
+          jp2_getuint16(in, &chan->assoc)) {
+            return -1;
+        }
+    }
+    return 0;
 }
 
 /******************************************************************************\
@@ -511,23 +513,23 @@ static int jp2_cdef_getdata(jp2_box_t *box, jas_stream_t *in)
 
 int jp2_box_put(jp2_box_t *box, jas_stream_t *out)
 {
-	jas_stream_t *tmpstream;
-	bool dataflag;
+    jas_stream_t *tmpstream;
+    bool dataflag;
 
-	tmpstream = 0;
+    tmpstream = 0;
 
-	dataflag = !(box->info->flags & (JP2_BOX_SUPER | JP2_BOX_NODATA));
+    dataflag = !(box->info->flags & (JP2_BOX_SUPER | JP2_BOX_NODATA));
 
-	if (dataflag) {
-		tmpstream = jas_stream_memopen(0, 0);
-		if (box->ops->putdata) {
-			if ((*box->ops->putdata)(box, tmpstream)) {
-				goto error;
-			}
-		}
-		box->len = jas_stream_tell(tmpstream) + JP2_BOX_HDRLEN;
-		jas_stream_rewind(tmpstream);
-	}
+    if (dataflag) {
+        tmpstream = jas_stream_memopen(0, 0);
+        if (box->ops->putdata) {
+            if ((*box->ops->putdata)(box, tmpstream)) {
+                goto error;
+            }
+        }
+        box->len = jas_stream_tell(tmpstream) + JP2_BOX_HDRLEN;
+        jas_stream_rewind(tmpstream);
+    }
     /* There was code here in official Jasper to handle 64 bit lengths,
        but it was based on determining whether box->len fits in 32 bits.
        But box->len is a 32 bit data type, so it fits in 32 bits by
@@ -535,119 +537,119 @@ int jp2_box_put(jp2_box_t *box, jas_stream_t *out)
        lengths that don't fit in 32 bits, or it passes invalid values of
        box->len to us.  We assume the former because it's easier.
     */
-	if (jp2_putuint32(out, box->len)) {
-		goto error;
-	}
-	if (jp2_putuint32(out, box->type)) {
-		goto error;
-	}
-
-	if (dataflag) {
-		if (jas_stream_copy(out, tmpstream, box->len - JP2_BOX_HDRLEN)) {
-			goto error;
-		}
-		jas_stream_close(tmpstream);
-	}
-
-	return 0;
-	abort();
+    if (jp2_putuint32(out, box->len)) {
+        goto error;
+    }
+    if (jp2_putuint32(out, box->type)) {
+        goto error;
+    }
+
+    if (dataflag) {
+        if (jas_stream_copy(out, tmpstream, box->len - JP2_BOX_HDRLEN)) {
+            goto error;
+        }
+        jas_stream_close(tmpstream);
+    }
+
+    return 0;
+    abort();
 
 error:
 
-	if (tmpstream) {
-		jas_stream_close(tmpstream);
-	}
-	return -1;
+    if (tmpstream) {
+        jas_stream_close(tmpstream);
+    }
+    return -1;
 }
 
 static int jp2_jp_putdata(jp2_box_t *box, jas_stream_t *out)
 {
-	jp2_jp_t *jp = &box->data.jp;
-	if (jp2_putuint32(out, jp->magic)) {
-		return -1;
-	}
-	return 0;
+    jp2_jp_t *jp = &box->data.jp;
+    if (jp2_putuint32(out, jp->magic)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jp2_ftyp_putdata(jp2_box_t *box, jas_stream_t *out)
 {
-	jp2_ftyp_t *ftyp = &box->data.ftyp;
-	int i;
-	if (jp2_putuint32(out, ftyp->majver) || jp2_putuint32(out, ftyp->minver)) {
-		return -1;
-	}
-	for (i = 0; i < ftyp->numcompatcodes; ++i) {
-		if (jp2_putuint32(out, ftyp->compatcodes[i])) {
-			return -1;
-		}
-	}
-	return 0;
+    jp2_ftyp_t *ftyp = &box->data.ftyp;
+    int i;
+    if (jp2_putuint32(out, ftyp->majver) || jp2_putuint32(out, ftyp->minver)) {
+        return -1;
+    }
+    for (i = 0; i < ftyp->numcompatcodes; ++i) {
+        if (jp2_putuint32(out, ftyp->compatcodes[i])) {
+            return -1;
+        }
+    }
+    return 0;
 }
 
 static int jp2_ihdr_putdata(jp2_box_t *box, jas_stream_t *out)
 {
-	jp2_ihdr_t *ihdr = &box->data.ihdr;
-	if (jp2_putuint32(out, ihdr->height) || jp2_putuint32(out, ihdr->width) ||
-	  jp2_putuint16(out, ihdr->numcmpts) || jp2_putuint8(out, ihdr->bpc) ||
-	  jp2_putuint8(out, ihdr->comptype) || jp2_putuint8(out, ihdr->csunk) ||
-	  jp2_putuint8(out, ihdr->ipr)) {
-		return -1;
-	}
-	return 0;
+    jp2_ihdr_t *ihdr = &box->data.ihdr;
+    if (jp2_putuint32(out, ihdr->height) || jp2_putuint32(out, ihdr->width) ||
+      jp2_putuint16(out, ihdr->numcmpts) || jp2_putuint8(out, ihdr->bpc) ||
+      jp2_putuint8(out, ihdr->comptype) || jp2_putuint8(out, ihdr->csunk) ||
+      jp2_putuint8(out, ihdr->ipr)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jp2_bpcc_putdata(jp2_box_t *box, jas_stream_t *out)
 {
-	jp2_bpcc_t *bpcc = &box->data.bpcc;
-	int i;
-	for (i = 0; i < bpcc->numcmpts; ++i) {
-		if (jp2_putuint8(out, bpcc->bpcs[i])) {
-			return -1;
-		}
-	}
-	return 0;
+    jp2_bpcc_t *bpcc = &box->data.bpcc;
+    int i;
+    for (i = 0; i < bpcc->numcmpts; ++i) {
+        if (jp2_putuint8(out, bpcc->bpcs[i])) {
+            return -1;
+        }
+    }
+    return 0;
 }
 
 static int jp2_colr_putdata(jp2_box_t *box, jas_stream_t *out)
 {
-	jp2_colr_t *colr = &box->data.colr;
-	if (jp2_putuint8(out, colr->method) || jp2_putuint8(out, colr->pri) ||
-	  jp2_putuint8(out, colr->approx)) {
-		return -1;
-	}
-	switch (colr->method) {
-	case JP2_COLR_ENUM:
-		if (jp2_putuint32(out, colr->csid)) {
-			return -1;
-		}
-		break;
-	case JP2_COLR_ICC:
-		/* XXX - not implemented */
-		abort();
-		break;
-	}
-	return 0;
+    jp2_colr_t *colr = &box->data.colr;
+    if (jp2_putuint8(out, colr->method) || jp2_putuint8(out, colr->pri) ||
+      jp2_putuint8(out, colr->approx)) {
+        return -1;
+    }
+    switch (colr->method) {
+    case JP2_COLR_ENUM:
+        if (jp2_putuint32(out, colr->csid)) {
+            return -1;
+        }
+        break;
+    case JP2_COLR_ICC:
+        /* XXX - not implemented */
+        abort();
+        break;
+    }
+    return 0;
 }
 
 static int jp2_cdef_putdata(jp2_box_t *box, jas_stream_t *out)
 {
-	jp2_cdef_t *cdef = &box->data.cdef;
-	int i;
-	jp2_cdefchan_t *ent;
+    jp2_cdef_t *cdef = &box->data.cdef;
+    int i;
+    jp2_cdefchan_t *ent;
 
-	if (jp2_putuint16(out, cdef->numchans)) {
-		return -1;
-	}
+    if (jp2_putuint16(out, cdef->numchans)) {
+        return -1;
+    }
 
-	for (i = 0; i < cdef->numchans; ++i) {
-		ent = &cdef->ents[i];
-		if (jp2_putuint16(out, ent->channo) ||
-		  jp2_putuint16(out, ent->type) ||
-		  jp2_putuint16(out, ent->assoc)) {
-			return -1;
-		}
-	}
-	return 0;
+    for (i = 0; i < cdef->numchans; ++i) {
+        ent = &cdef->ents[i];
+        if (jp2_putuint16(out, ent->channo) ||
+          jp2_putuint16(out, ent->type) ||
+          jp2_putuint16(out, ent->assoc)) {
+            return -1;
+        }
+    }
+    return 0;
 }
 
 /******************************************************************************\
@@ -656,63 +658,63 @@ static int jp2_cdef_putdata(jp2_box_t *box, jas_stream_t *out)
 
 static int jp2_getuint8(jas_stream_t *in, uint_fast8_t *val)
 {
-	int c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	if (val) {
-		*val = c;
-	}
-	return 0;
+    int c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    if (val) {
+        *val = c;
+    }
+    return 0;
 }
 
 static int jp2_getuint16(jas_stream_t *in, uint_fast16_t *val)
 {
-	uint_fast16_t v;
-	int c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	v = c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	v = (v << 8) | c;
-	if (val) {
-		*val = v;
-	}
-	return 0;
+    uint_fast16_t v;
+    int c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    v = c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    v = (v << 8) | c;
+    if (val) {
+        *val = v;
+    }
+    return 0;
 }
 
 static int jp2_getuint32(jas_stream_t *in, uint_fast32_t *val)
 {
-	uint_fast32_t v;
-	int c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	v = c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	v = (v << 8) | c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	v = (v << 8) | c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	v = (v << 8) | c;
-	if (val) {
-		*val = v;
-	}
-	return 0;
+    uint_fast32_t v;
+    int c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    v = c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    v = (v << 8) | c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    v = (v << 8) | c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    v = (v << 8) | c;
+    if (val) {
+        *val = v;
+    }
+    return 0;
 }
 
 static int jp2_getuint64(jas_stream_t *in, uint_fast64_t *val)
 {
-	abort();
+    abort();
 }
 
 /******************************************************************************\
@@ -721,30 +723,30 @@ static int jp2_getuint64(jas_stream_t *in, uint_fast64_t *val)
 
 static int jp2_putuint8(jas_stream_t *out, uint_fast8_t val)
 {
-	if (jas_stream_putc(out, val & 0xff) == EOF) {
-		return -1;
-	}
-	return 0;
+    if (jas_stream_putc(out, val & 0xff) == EOF) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jp2_putuint16(jas_stream_t *out, uint_fast16_t val)
 {
-	if (jas_stream_putc(out, (val >> 8) & 0xff) == EOF ||
-	  jas_stream_putc(out, val & 0xff) == EOF) {
-		return -1;
-	}
-	return 0;
+    if (jas_stream_putc(out, (val >> 8) & 0xff) == EOF ||
+      jas_stream_putc(out, val & 0xff) == EOF) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jp2_putuint32(jas_stream_t *out, uint_fast32_t val)
 {
-	if (jas_stream_putc(out, (val >> 24) & 0xff) == EOF ||
-	  jas_stream_putc(out, (val >> 16) & 0xff) == EOF ||
-	  jas_stream_putc(out, (val >> 8) & 0xff) == EOF ||
-	  jas_stream_putc(out, val & 0xff) == EOF) {
-		return -1;
-	}
-	return 0;
+    if (jas_stream_putc(out, (val >> 24) & 0xff) == EOF ||
+      jas_stream_putc(out, (val >> 16) & 0xff) == EOF ||
+      jas_stream_putc(out, (val >> 8) & 0xff) == EOF ||
+      jas_stream_putc(out, val & 0xff) == EOF) {
+        return -1;
+    }
+    return 0;
 }
 
 /******************************************************************************\
@@ -753,13 +755,13 @@ static int jp2_putuint32(jas_stream_t *out, uint_fast32_t val)
 
 jp2_boxinfo_t *jp2_boxinfolookup(int type)
 {
-	jp2_boxinfo_t *boxinfo;
-	for (boxinfo = jp2_boxinfos; boxinfo->name; ++boxinfo) {
-		if (boxinfo->type == type) {
-			return boxinfo;
-		}
-	}
-	return &jp2_boxinfo_unk;
+    jp2_boxinfo_t *boxinfo;
+    for (boxinfo = jp2_boxinfos; boxinfo->name; ++boxinfo) {
+        if (boxinfo->type == type) {
+            return boxinfo;
+        }
+    }
+    return &jp2_boxinfo_unk;
 }
 
 
@@ -768,161 +770,162 @@ jp2_boxinfo_t *jp2_boxinfolookup(int type)
 
 static void jp2_cmap_destroy(jp2_box_t *box)
 {
-	jp2_cmap_t *cmap = &box->data.cmap;
-	if (cmap->ents) {
-		jas_free(cmap->ents);
-	}
+    jp2_cmap_t *cmap = &box->data.cmap;
+    if (cmap->ents) {
+        jas_free(cmap->ents);
+    }
 }
 
 static int jp2_cmap_getdata(jp2_box_t *box, jas_stream_t *in)
 {
-	jp2_cmap_t *cmap = &box->data.cmap;
-	jp2_cmapent_t *ent;
-	int i;
-
-	cmap->numchans = (box->len - JP2_BOX_HDRLEN) / 4;
-	if (!(cmap->ents = jas_malloc(cmap->numchans * sizeof(jp2_cmapent_t)))) {
-		return -1;
-	}
-	for (i = 0; i < cmap->numchans; ++i) {
-		ent = &cmap->ents[i];
-		if (jp2_getuint16(in, &ent->cmptno) ||
-		  jp2_getuint8(in, &ent->map) ||
-		  jp2_getuint8(in, &ent->pcol)) {
-			return -1;
-		}
-	}
-	
-	return 0;
+    jp2_cmap_t *cmap = &box->data.cmap;
+    jp2_cmapent_t *ent;
+    int i;
+
+    cmap->numchans = (box->len - JP2_BOX_HDRLEN) / 4;
+    if (!(cmap->ents = jas_malloc(cmap->numchans * sizeof(jp2_cmapent_t)))) {
+        return -1;
+    }
+    for (i = 0; i < cmap->numchans; ++i) {
+        ent = &cmap->ents[i];
+        if (jp2_getuint16(in, &ent->cmptno) ||
+          jp2_getuint8(in, &ent->map) ||
+          jp2_getuint8(in, &ent->pcol)) {
+            return -1;
+        }
+    }
+    
+    return 0;
 }
 
 static int jp2_cmap_putdata(jp2_box_t *box, jas_stream_t *out)
 {
-	return -1;
+    return -1;
 }
 
 static void jp2_cmap_dumpdata(jp2_box_t *box, FILE *out)
 {
-	jp2_cmap_t *cmap = &box->data.cmap;
-	int i;
-	jp2_cmapent_t *ent;
-	fprintf(stderr, "numchans = %d\n", (int) cmap->numchans);
-	for (i = 0; i < cmap->numchans; ++i) {
-		ent = &cmap->ents[i];
-		fprintf(stderr, "cmptno=%d; map=%d; pcol=%d\n",
-		  (int) ent->cmptno, (int) ent->map, (int) ent->pcol);
-	}
+    jp2_cmap_t *cmap = &box->data.cmap;
+    int i;
+    jp2_cmapent_t *ent;
+    fprintf(stderr, "numchans = %d\n", (int) cmap->numchans);
+    for (i = 0; i < cmap->numchans; ++i) {
+        ent = &cmap->ents[i];
+        fprintf(stderr, "cmptno=%d; map=%d; pcol=%d\n",
+          (int) ent->cmptno, (int) ent->map, (int) ent->pcol);
+    }
 }
 
 static void jp2_pclr_destroy(jp2_box_t *box)
 {
-	jp2_pclr_t *pclr = &box->data.pclr;
-	if (pclr->lutdata) {
-		jas_free(pclr->lutdata);
-	}
+    jp2_pclr_t *pclr = &box->data.pclr;
+    if (pclr->lutdata) {
+        jas_free(pclr->lutdata);
+    }
 }
 
 static int jp2_pclr_getdata(jp2_box_t *box, jas_stream_t *in)
 {
-	jp2_pclr_t *pclr = &box->data.pclr;
-	int lutsize;
-	int i;
-	int j;
-	int_fast32_t x;
-
-	pclr->lutdata = 0;
-
-	if (jp2_getuint16(in, &pclr->numlutents) ||
-	  jp2_getuint8(in, &pclr->numchans)) {
-		return -1;
-	}
-	lutsize = pclr->numlutents * pclr->numchans;
-	if (!(pclr->lutdata = jas_malloc(lutsize * sizeof(int_fast32_t)))) {
-		return -1;
-	}
-	if (!(pclr->bpc = jas_malloc(pclr->numchans * sizeof(uint_fast8_t)))) {
-		return -1;
-	}
-	for (i = 0; i < pclr->numchans; ++i) {
-		if (jp2_getuint8(in, &pclr->bpc[i])) {
-			return -1;
-		}
-	}
-	for (i = 0; i < pclr->numlutents; ++i) {
-		for (j = 0; j < pclr->numchans; ++j) {
-			if (jp2_getint(in, (pclr->bpc[j] & 0x80) != 0,
-			  (pclr->bpc[j] & 0x7f) + 1, &x)) {
-				return -1;
-			}
-			pclr->lutdata[i * pclr->numchans + j] = x;
-		}
-	}
-	return 0;
+    jp2_pclr_t *pclr = &box->data.pclr;
+    int lutsize;
+    int i;
+    int j;
+    int_fast32_t x;
+
+    pclr->lutdata = 0;
+
+    if (jp2_getuint16(in, &pclr->numlutents) ||
+      jp2_getuint8(in, &pclr->numchans)) {
+        return -1;
+    }
+    lutsize = pclr->numlutents * pclr->numchans;
+    if (!(pclr->lutdata = jas_malloc(lutsize * sizeof(int_fast32_t)))) {
+        return -1;
+    }
+    if (!(pclr->bpc = jas_malloc(pclr->numchans * sizeof(uint_fast8_t)))) {
+        return -1;
+    }
+    for (i = 0; i < pclr->numchans; ++i) {
+        if (jp2_getuint8(in, &pclr->bpc[i])) {
+            return -1;
+        }
+    }
+    for (i = 0; i < pclr->numlutents; ++i) {
+        for (j = 0; j < pclr->numchans; ++j) {
+            if (jp2_getint(in, (pclr->bpc[j] & 0x80) != 0,
+              (pclr->bpc[j] & 0x7f) + 1, &x)) {
+                return -1;
+            }
+            pclr->lutdata[i * pclr->numchans + j] = x;
+        }
+    }
+    return 0;
 }
 
 static int jp2_pclr_putdata(jp2_box_t *box, jas_stream_t *out)
 {
     /* This code from official Jasper must be part of unfinished work.
        It generates an unused variable warning.
-	jp2_pclr_t *pclr = &box->data.pclr;
+    jp2_pclr_t *pclr = &box->data.pclr;
     */
-	return -1;
+    return -1;
 }
 
 static void jp2_pclr_dumpdata(jp2_box_t *box, FILE *out)
 {
-	jp2_pclr_t *pclr = &box->data.pclr;
-	int i;
-	int j;
-	fprintf(out, "numents=%d; numchans=%d\n", (int) pclr->numlutents,
-	  (int) pclr->numchans);
-	for (i = 0; i < pclr->numlutents; ++i) {
-		for (j = 0; j < pclr->numchans; ++j) {
-			fprintf(out, "LUT[%d][%d]=%d\n", i, j, pclr->lutdata[i * pclr->numchans + j]);
-		}
-	}
+    jp2_pclr_t *pclr = &box->data.pclr;
+    int i;
+    int j;
+    fprintf(out, "numents=%d; numchans=%d\n", (int) pclr->numlutents,
+      (int) pclr->numchans);
+    for (i = 0; i < pclr->numlutents; ++i) {
+        for (j = 0; j < pclr->numchans; ++j) {
+            fprintf(out, "LUT[%d][%d]=%d\n", i, j,
+                    (int)pclr->lutdata[i * pclr->numchans + j]);
+        }
+    }
 }
 
 static int jp2_getint(jas_stream_t *in, int s, int n, int_fast32_t *val)
 {
-	int c;
-	int i;
-	uint_fast32_t v;
-	int m;
-
-	m = (n + 7) / 8;
-
-	v = 0;
-	for (i = 0; i < m; ++i) {
-		if ((c = jas_stream_getc(in)) == EOF) {
-			return -1;
-		}
-		v = (v << 8) | c;
-	}
-	v &= ONES(n);
-	if (s) {
-		int sb;
-		sb = v & (1 << (8 * m - 1));
-		*val = ((~v) + 1) & ONES(8 * m);
-		if (sb) {
-			*val = -*val;
-		}
-	} else {
-		*val = v;
-	}
-
-	return 0;
+    int c;
+    int i;
+    uint_fast32_t v;
+    int m;
+
+    m = (n + 7) / 8;
+
+    v = 0;
+    for (i = 0; i < m; ++i) {
+        if ((c = jas_stream_getc(in)) == EOF) {
+            return -1;
+        }
+        v = (v << 8) | c;
+    }
+    v &= ONES(n);
+    if (s) {
+        int sb;
+        sb = v & (1 << (8 * m - 1));
+        *val = ((~v) + 1) & ONES(8 * m);
+        if (sb) {
+            *val = -*val;
+        }
+    } else {
+        *val = v;
+    }
+
+    return 0;
 }
 
 jp2_cdefchan_t *jp2_cdef_lookup(jp2_cdef_t *cdef, int channo)
 {
-	int i;
-	jp2_cdefchan_t *cdefent;
-	for (i = 0; i < cdef->numchans; ++i) {
-		cdefent = &cdef->ents[i];
-		if (cdefent->channo == channo) {
-			return cdefent;
-		}
-	}
-	return 0;
+    int i;
+    jp2_cdefchan_t *cdefent;
+    for (i = 0; i < cdef->numchans; ++i) {
+        cdefent = &cdef->ents[i];
+        if (cdefent->channo == channo) {
+            return cdefent;
+        }
+    }
+    return 0;
 }
diff --git a/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c b/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c
index 3cce9278..91ce6c51 100644
--- a/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c
+++ b/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c
@@ -297,9 +297,9 @@ jas_image_t *jp2_decode(jas_stream_t *in, char *optstr)
 		jas_eprintf("warning: component data type mismatch\n");
 	}
 
-	/* Is the compression type supported? */
+	/* Can we handle the compression type? */
 	if (dec->ihdr->data.ihdr.comptype != JP2_IHDR_COMPTYPE) {
-		jas_eprintf("error: unsupported compression type\n");
+		jas_eprintf("error: not capable of this compression type\n");
 		goto error;
 	}
 
@@ -340,7 +340,8 @@ jas_image_t *jp2_decode(jas_stream_t *in, char *optstr)
 		iccp = dec->colr->data.colr.iccp;
 		cs = (iccp[16] << 24) | (iccp[17] << 16) | (iccp[18] << 8) |
 		  iccp[19];
-		jas_eprintf("ICC Profile CS %08x\n", cs);
+        if (jas_getdbglevel() > 1)
+            jas_eprintf("ICC Profile CS %08x\n", cs);
 		jas_image_setcolorspace(dec->image, fromiccpcs(cs));
 		break;
 	}
@@ -454,7 +455,6 @@ jas_image_t *jp2_decode(jas_stream_t *in, char *optstr)
 		jas_eprintf("error: no components\n");
 		goto error;
 	}
-fprintf(stderr, "no of components is %d\n", jas_image_numcmpts(dec->image));
 
 	/* Prevent the image from being destroyed later. */
 	image = dec->image;
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_bs.c b/converter/other/jpeg2000/libjasper/jpc/jpc_bs.c
index 54f0a819..c66fcd99 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_bs.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_bs.c
@@ -157,10 +157,6 @@ jpc_bitstream_t *jpc_bitstream_sopen(jas_stream_t *stream, const char *mode)
 {
 	jpc_bitstream_t *bitstream;
 
-	/* Ensure that the open mode is valid. */
-	assert(!strcmp(mode, "r") || !strcmp(mode, "w") || !strcmp(mode, "r+")
-	  || !strcmp(mode, "w+"));
-
 	if (!(bitstream = jpc_bitstream_alloc())) {
 		return 0;
 	}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_bs.h b/converter/other/jpeg2000/libjasper/jpc/jpc_bs.h
index f515972b..edb0a2df 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_bs.h
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_bs.h
@@ -149,7 +149,7 @@
 #define	JPC_BITSTREAM_NOCLOSE	0x01
 /* End of file has been reached while reading. */
 #define	JPC_BITSTREAM_EOF	0x02
-/* An I/O error has occured. */
+/* An I/O error has occurred. */
 #define	JPC_BITSTREAM_ERR	0x04
 
 /******************************************************************************\
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_cs.c b/converter/other/jpeg2000/libjasper/jpc/jpc_cs.c
index 63cf8ba7..559f36cf 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_cs.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_cs.c
@@ -135,9 +135,9 @@
 
 /* Marker segment table entry. */
 typedef struct {
-	int id;
-	const char *name;
-	jpc_msops_t ops;
+    int id;
+    const char *name;
+    jpc_msops_t ops;
 } jpc_mstabent_t;
 
 /******************************************************************************\
@@ -220,40 +220,40 @@ static int jpc_cox_putcompparms(jpc_ms_t *ms, jpc_cstate_t *cstate,
 \******************************************************************************/
 
 static jpc_mstabent_t jpc_mstab[] = {
-	{JPC_MS_SOC, "SOC", {0, 0, 0, 0}},
-	{JPC_MS_SOT, "SOT", {0, jpc_sot_getparms, jpc_sot_putparms,
-	  jpc_sot_dumpparms}},
-	{JPC_MS_SOD, "SOD", {0, 0, 0, 0}},
-	{JPC_MS_EOC, "EOC", {0, 0, 0, 0}},
-	{JPC_MS_SIZ, "SIZ", {jpc_siz_destroyparms, jpc_siz_getparms,
-	  jpc_siz_putparms, jpc_siz_dumpparms}},
-	{JPC_MS_COD, "COD", {jpc_cod_destroyparms, jpc_cod_getparms,
-	  jpc_cod_putparms, jpc_cod_dumpparms}},
-	{JPC_MS_COC, "COC", {jpc_coc_destroyparms, jpc_coc_getparms,
-	  jpc_coc_putparms, jpc_coc_dumpparms}},
-	{JPC_MS_RGN, "RGN", {0, jpc_rgn_getparms, jpc_rgn_putparms,
-	  jpc_rgn_dumpparms}},
-	{JPC_MS_QCD, "QCD", {jpc_qcd_destroyparms, jpc_qcd_getparms,
-	  jpc_qcd_putparms, jpc_qcd_dumpparms}},
-	{JPC_MS_QCC, "QCC", {jpc_qcc_destroyparms, jpc_qcc_getparms,
-	  jpc_qcc_putparms, jpc_qcc_dumpparms}},
-	{JPC_MS_POC, "POC", {jpc_poc_destroyparms, jpc_poc_getparms,
-	  jpc_poc_putparms, jpc_poc_dumpparms}},
-	{JPC_MS_TLM, "TLM", {0, jpc_unk_getparms, jpc_unk_putparms, 0}},
-	{JPC_MS_PLM, "PLM", {0, jpc_unk_getparms, jpc_unk_putparms, 0}},
-	{JPC_MS_PPM, "PPM", {jpc_ppm_destroyparms, jpc_ppm_getparms,
-	  jpc_ppm_putparms, jpc_ppm_dumpparms}},
-	{JPC_MS_PPT, "PPT", {jpc_ppt_destroyparms, jpc_ppt_getparms,
-	  jpc_ppt_putparms, jpc_ppt_dumpparms}},
-	{JPC_MS_SOP, "SOP", {0, jpc_sop_getparms, jpc_sop_putparms,
-	  jpc_sop_dumpparms}},
-	{JPC_MS_EPH, "EPH", {0, 0, 0, 0}},
-	{JPC_MS_CRG, "CRG", {0, jpc_crg_getparms, jpc_crg_putparms,
-	  jpc_crg_dumpparms}},
-	{JPC_MS_COM, "COM", {jpc_com_destroyparms, jpc_com_getparms,
-	  jpc_com_putparms, jpc_com_dumpparms}},
-	{-1, "UNKNOWN",  {jpc_unk_destroyparms, jpc_unk_getparms,
-	  jpc_unk_putparms, jpc_unk_dumpparms}}
+    {JPC_MS_SOC, "SOC", {0, 0, 0, 0}},
+    {JPC_MS_SOT, "SOT", {0, jpc_sot_getparms, jpc_sot_putparms,
+      jpc_sot_dumpparms}},
+    {JPC_MS_SOD, "SOD", {0, 0, 0, 0}},
+    {JPC_MS_EOC, "EOC", {0, 0, 0, 0}},
+    {JPC_MS_SIZ, "SIZ", {jpc_siz_destroyparms, jpc_siz_getparms,
+      jpc_siz_putparms, jpc_siz_dumpparms}},
+    {JPC_MS_COD, "COD", {jpc_cod_destroyparms, jpc_cod_getparms,
+      jpc_cod_putparms, jpc_cod_dumpparms}},
+    {JPC_MS_COC, "COC", {jpc_coc_destroyparms, jpc_coc_getparms,
+      jpc_coc_putparms, jpc_coc_dumpparms}},
+    {JPC_MS_RGN, "RGN", {0, jpc_rgn_getparms, jpc_rgn_putparms,
+      jpc_rgn_dumpparms}},
+    {JPC_MS_QCD, "QCD", {jpc_qcd_destroyparms, jpc_qcd_getparms,
+      jpc_qcd_putparms, jpc_qcd_dumpparms}},
+    {JPC_MS_QCC, "QCC", {jpc_qcc_destroyparms, jpc_qcc_getparms,
+      jpc_qcc_putparms, jpc_qcc_dumpparms}},
+    {JPC_MS_POC, "POC", {jpc_poc_destroyparms, jpc_poc_getparms,
+      jpc_poc_putparms, jpc_poc_dumpparms}},
+    {JPC_MS_TLM, "TLM", {0, jpc_unk_getparms, jpc_unk_putparms, 0}},
+    {JPC_MS_PLM, "PLM", {0, jpc_unk_getparms, jpc_unk_putparms, 0}},
+    {JPC_MS_PPM, "PPM", {jpc_ppm_destroyparms, jpc_ppm_getparms,
+      jpc_ppm_putparms, jpc_ppm_dumpparms}},
+    {JPC_MS_PPT, "PPT", {jpc_ppt_destroyparms, jpc_ppt_getparms,
+      jpc_ppt_putparms, jpc_ppt_dumpparms}},
+    {JPC_MS_SOP, "SOP", {0, jpc_sop_getparms, jpc_sop_putparms,
+      jpc_sop_dumpparms}},
+    {JPC_MS_EPH, "EPH", {0, 0, 0, 0}},
+    {JPC_MS_CRG, "CRG", {0, jpc_crg_getparms, jpc_crg_putparms,
+      jpc_crg_dumpparms}},
+    {JPC_MS_COM, "COM", {jpc_com_destroyparms, jpc_com_getparms,
+      jpc_com_putparms, jpc_com_dumpparms}},
+    {-1, "UNKNOWN",  {jpc_unk_destroyparms, jpc_unk_getparms,
+      jpc_unk_putparms, jpc_unk_dumpparms}}
 };
 
 /******************************************************************************\
@@ -263,160 +263,160 @@ static jpc_mstabent_t jpc_mstab[] = {
 /* Create a code stream state object. */
 jpc_cstate_t *jpc_cstate_create()
 {
-	jpc_cstate_t *cstate;
-	if (!(cstate = jas_malloc(sizeof(jpc_cstate_t)))) {
-		return 0;
-	}
-	cstate->numcomps = 0;
-	return cstate;
+    jpc_cstate_t *cstate;
+    if (!(cstate = jas_malloc(sizeof(jpc_cstate_t)))) {
+        return 0;
+    }
+    cstate->numcomps = 0;
+    return cstate;
 }
 
 /* Destroy a code stream state object. */
 void jpc_cstate_destroy(jpc_cstate_t *cstate)
 {
-	jas_free(cstate);
+    jas_free(cstate);
 }
 
 /* Read a marker segment from a stream. */
 jpc_ms_t *jpc_getms(jas_stream_t *in, jpc_cstate_t *cstate)
 {
-	jpc_ms_t *ms;
-	jpc_mstabent_t *mstabent;
-	jas_stream_t *tmpstream;
-
-	if (!(ms = jpc_ms_create(0))) {
-		return 0;
-	}
-
-	/* Get the marker type. */
-	if (jpc_getuint16(in, &ms->id) || ms->id < JPC_MS_MIN ||
-	  ms->id > JPC_MS_MAX) {
-		jpc_ms_destroy(ms);
-		return 0;
-	}
-
-	mstabent = jpc_mstab_lookup(ms->id);
-	ms->ops = &mstabent->ops;
-
-	/* Get the marker segment length and parameters if present. */
-	/* Note: It is tacitly assumed that a marker segment cannot have
-	  parameters unless it has a length field.  That is, there cannot
-	  be a parameters field without a length field and vice versa. */
-	if (JPC_MS_HASPARMS(ms->id)) {
-		/* Get the length of the marker segment. */
-		if (jpc_getuint16(in, &ms->len) || ms->len < 3) {
-			jpc_ms_destroy(ms);
-			return 0;
-		}
-		/* Calculate the length of the marker segment parameters. */
-		ms->len -= 2;
-		/* Create and prepare a temporary memory stream from which to
-		  read the marker segment parameters. */
-		/* Note: This approach provides a simple way of ensuring that
-		  we never read beyond the end of the marker segment (even if
-		  the marker segment length is errantly set too small). */
-		if (!(tmpstream = jas_stream_memopen(0, 0))) {
-			jpc_ms_destroy(ms);
-			return 0;
-		}
-		if (jas_stream_copy(tmpstream, in, ms->len) ||
-		  jas_stream_seek(tmpstream, 0, SEEK_SET) < 0) {
-			jas_stream_close(tmpstream);
-			jpc_ms_destroy(ms);
-			return 0;
-		}
-		/* Get the marker segment parameters. */
-		if ((*ms->ops->getparms)(ms, cstate, tmpstream)) {
-			ms->ops = 0;
-			jpc_ms_destroy(ms);
-			jas_stream_close(tmpstream);
-			return 0;
-		}
-
-		if (jas_getdbglevel() > 0) {
-			jpc_ms_dump(ms, stderr);
-		}
-
-		if (jas_stream_tell(tmpstream) != ms->len) {
-			fprintf(stderr,
-			  "warning: trailing garbage in marker segment (%ld bytes)\n",
-			  ms->len - jas_stream_tell(tmpstream));
-		}
-
-		/* Close the temporary stream. */
-		jas_stream_close(tmpstream);
-
-	} else {
-		/* There are no marker segment parameters. */
-		ms->len = 0;
-
-		if (jas_getdbglevel() > 0) {
-			jpc_ms_dump(ms, stderr);
-		}
-	}
-
-	/* Update the code stream state information based on the type of
-	  marker segment read. */
-	/* Note: This is a bit of a hack, but I'm not going to define another
-	  type of virtual function for this one special case. */
-	if (ms->id == JPC_MS_SIZ) {
-		cstate->numcomps = ms->parms.siz.numcomps;
-	}
-
-	return ms;
+    jpc_ms_t *ms;
+    jpc_mstabent_t *mstabent;
+    jas_stream_t *tmpstream;
+
+    if (!(ms = jpc_ms_create(0))) {
+        return 0;
+    }
+
+    /* Get the marker type. */
+    if (jpc_getuint16(in, &ms->id) || ms->id < JPC_MS_MIN ||
+      ms->id > JPC_MS_MAX) {
+        jpc_ms_destroy(ms);
+        return 0;
+    }
+
+    mstabent = jpc_mstab_lookup(ms->id);
+    ms->ops = &mstabent->ops;
+
+    /* Get the marker segment length and parameters if present. */
+    /* Note: It is tacitly assumed that a marker segment cannot have
+      parameters unless it has a length field.  That is, there cannot
+      be a parameters field without a length field and vice versa. */
+    if (JPC_MS_HASPARMS(ms->id)) {
+        /* Get the length of the marker segment. */
+        if (jpc_getuint16(in, &ms->len) || ms->len < 3) {
+            jpc_ms_destroy(ms);
+            return 0;
+        }
+        /* Calculate the length of the marker segment parameters. */
+        ms->len -= 2;
+        /* Create and prepare a temporary memory stream from which to
+          read the marker segment parameters. */
+        /* Note: This approach provides a simple way of ensuring that
+          we never read beyond the end of the marker segment (even if
+          the marker segment length is errantly set too small). */
+        if (!(tmpstream = jas_stream_memopen(0, 0))) {
+            jpc_ms_destroy(ms);
+            return 0;
+        }
+        if (jas_stream_copy(tmpstream, in, ms->len) ||
+          jas_stream_seek(tmpstream, 0, SEEK_SET) < 0) {
+            jas_stream_close(tmpstream);
+            jpc_ms_destroy(ms);
+            return 0;
+        }
+        /* Get the marker segment parameters. */
+        if ((*ms->ops->getparms)(ms, cstate, tmpstream)) {
+            ms->ops = 0;
+            jpc_ms_destroy(ms);
+            jas_stream_close(tmpstream);
+            return 0;
+        }
+
+        if (jas_getdbglevel() > 0) {
+            jpc_ms_dump(ms, stderr);
+        }
+
+        if (jas_stream_tell(tmpstream) != ms->len) {
+            fprintf(stderr,
+              "warning: trailing garbage in marker segment (%ld bytes)\n",
+              ms->len - jas_stream_tell(tmpstream));
+        }
+
+        /* Close the temporary stream. */
+        jas_stream_close(tmpstream);
+
+    } else {
+        /* There are no marker segment parameters. */
+        ms->len = 0;
+
+        if (jas_getdbglevel() > 0) {
+            jpc_ms_dump(ms, stderr);
+        }
+    }
+
+    /* Update the code stream state information based on the type of
+      marker segment read. */
+    /* Note: This is a bit of a hack, but I'm not going to define another
+      type of virtual function for this one special case. */
+    if (ms->id == JPC_MS_SIZ) {
+        cstate->numcomps = ms->parms.siz.numcomps;
+    }
+
+    return ms;
 }
 
 /* Write a marker segment to a stream. */
 int jpc_putms(jas_stream_t *out, jpc_cstate_t *cstate, jpc_ms_t *ms)
 {
-	jas_stream_t *tmpstream;
-	int len;
-
-	/* Output the marker segment type. */
-	if (jpc_putuint16(out, ms->id)) {
-		return -1;
-	}
-
-	/* Output the marker segment length and parameters if necessary. */
-	if (ms->ops->putparms) {
-		/* Create a temporary stream in which to buffer the
-		  parameter data. */
-		if (!(tmpstream = jas_stream_memopen(0, 0))) {
-			return -1;
-		}
-		if ((*ms->ops->putparms)(ms, cstate, tmpstream)) {
-			jas_stream_close(tmpstream);
-			return -1;
-		}
-		/* Get the number of bytes of parameter data written. */
-		if ((len = jas_stream_tell(tmpstream)) < 0) {
-			jas_stream_close(tmpstream);
-			return -1;
-		}
-		ms->len = len;
-		/* Write the marker segment length and parameter data to
-		  the output stream. */
-		if (jas_stream_seek(tmpstream, 0, SEEK_SET) < 0 ||
-		  jpc_putuint16(out, ms->len + 2) ||
-		  jas_stream_copy(out, tmpstream, ms->len) < 0) {
-			jas_stream_close(tmpstream);
-			return -1;
-		}
-		/* Close the temporary stream. */
-		jas_stream_close(tmpstream);
-	}
-
-	/* This is a bit of a hack, but I'm not going to define another
-	  type of virtual function for this one special case. */
-	if (ms->id == JPC_MS_SIZ) {
-		cstate->numcomps = ms->parms.siz.numcomps;
-	}
-
-	if (jas_getdbglevel() > 0) {
-		jpc_ms_dump(ms, stderr);
-	}
-
-	return 0;
+    jas_stream_t *tmpstream;
+    int len;
+
+    /* Output the marker segment type. */
+    if (jpc_putuint16(out, ms->id)) {
+        return -1;
+    }
+
+    /* Output the marker segment length and parameters if necessary. */
+    if (ms->ops->putparms) {
+        /* Create a temporary stream in which to buffer the
+          parameter data. */
+        if (!(tmpstream = jas_stream_memopen(0, 0))) {
+            return -1;
+        }
+        if ((*ms->ops->putparms)(ms, cstate, tmpstream)) {
+            jas_stream_close(tmpstream);
+            return -1;
+        }
+        /* Get the number of bytes of parameter data written. */
+        if ((len = jas_stream_tell(tmpstream)) < 0) {
+            jas_stream_close(tmpstream);
+            return -1;
+        }
+        ms->len = len;
+        /* Write the marker segment length and parameter data to
+          the output stream. */
+        if (jas_stream_seek(tmpstream, 0, SEEK_SET) < 0 ||
+          jpc_putuint16(out, ms->len + 2) ||
+          jas_stream_copy(out, tmpstream, ms->len) < 0) {
+            jas_stream_close(tmpstream);
+            return -1;
+        }
+        /* Close the temporary stream. */
+        jas_stream_close(tmpstream);
+    }
+
+    /* This is a bit of a hack, but I'm not going to define another
+      type of virtual function for this one special case. */
+    if (ms->id == JPC_MS_SIZ) {
+        cstate->numcomps = ms->parms.siz.numcomps;
+    }
+
+    if (jas_getdbglevel() > 0) {
+        jpc_ms_dump(ms, stderr);
+    }
+
+    return 0;
 }
 
 /******************************************************************************\
@@ -426,45 +426,46 @@ int jpc_putms(jas_stream_t *out, jpc_cstate_t *cstate, jpc_ms_t *ms)
 /* Create a marker segment of the specified type. */
 jpc_ms_t *jpc_ms_create(int type)
 {
-	jpc_ms_t *ms;
-	jpc_mstabent_t *mstabent;
+    jpc_ms_t *ms;
+    jpc_mstabent_t *mstabent;
 
-	if (!(ms = jas_malloc(sizeof(jpc_ms_t)))) {
-		return 0;
-	}
-	ms->id = type;
-	ms->len = 0;
-	mstabent = jpc_mstab_lookup(ms->id);
-	ms->ops = &mstabent->ops;
-	memset(&ms->parms, 0, sizeof(jpc_msparms_t));
-	return ms;
+    if (!(ms = jas_malloc(sizeof(jpc_ms_t)))) {
+        return 0;
+    }
+    ms->id = type;
+    ms->len = 0;
+    mstabent = jpc_mstab_lookup(ms->id);
+    ms->ops = &mstabent->ops;
+    memset(&ms->parms, 0, sizeof(jpc_msparms_t));
+    return ms;
 }
 
 /* Destroy a marker segment. */
 void jpc_ms_destroy(jpc_ms_t *ms)
 {
-	if (ms->ops && ms->ops->destroyparms) {
-		(*ms->ops->destroyparms)(ms);
-	}
-	jas_free(ms);
+    if (ms->ops && ms->ops->destroyparms) {
+        (*ms->ops->destroyparms)(ms);
+    }
+    jas_free(ms);
 }
 
 /* Dump a marker segment to a stream for debugging. */
 void jpc_ms_dump(jpc_ms_t *ms, FILE *out)
 {
-	jpc_mstabent_t *mstabent;
-	mstabent = jpc_mstab_lookup(ms->id);
-	fprintf(out, "type = 0x%04x (%s);", ms->id, mstabent->name);
-	if (JPC_MS_HASPARMS(ms->id)) {
-		fprintf(out, " len = %d;", ms->len + 2);
-		if (ms->ops->dumpparms) {
-			(*ms->ops->dumpparms)(ms, out);
-		} else {
-			fprintf(out, "\n");
-		}
-	} else {
-		fprintf(out, "\n");
-	}
+    jpc_mstabent_t *mstabent;
+    mstabent = jpc_mstab_lookup(ms->id);
+    fprintf(out, "type = 0x%04x (%s);",
+            (unsigned int)ms->id, mstabent->name);
+    if (JPC_MS_HASPARMS(ms->id)) {
+        fprintf(out, " len = %d;", (int)ms->len + 2);
+        if (ms->ops->dumpparms) {
+            (*ms->ops->dumpparms)(ms, out);
+        } else {
+            fprintf(out, "\n");
+        }
+    } else {
+        fprintf(out, "\n");
+    }
 }
 
 /******************************************************************************\
@@ -473,37 +474,37 @@ void jpc_ms_dump(jpc_ms_t *ms, FILE *out)
 
 static int jpc_sot_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_sot_t *sot = &ms->parms.sot;
-	if (jpc_getuint16(in, &sot->tileno) ||
-	  jpc_getuint32(in, &sot->len) ||
-	  jpc_getuint8(in, &sot->partno) ||
-	  jpc_getuint8(in, &sot->numparts)) {
-		return -1;
-	}
-	if (jas_stream_eof(in)) {
-		return -1;
-	}
-	return 0;
+    jpc_sot_t *sot = &ms->parms.sot;
+    if (jpc_getuint16(in, &sot->tileno) ||
+      jpc_getuint32(in, &sot->len) ||
+      jpc_getuint8(in, &sot->partno) ||
+      jpc_getuint8(in, &sot->numparts)) {
+        return -1;
+    }
+    if (jas_stream_eof(in)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_sot_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	jpc_sot_t *sot = &ms->parms.sot;
-	if (jpc_putuint16(out, sot->tileno) ||
-	  jpc_putuint32(out, sot->len) ||
-	  jpc_putuint8(out, sot->partno) ||
-	  jpc_putuint8(out, sot->numparts)) {
-		return -1;
-	}
-	return 0;
+    jpc_sot_t *sot = &ms->parms.sot;
+    if (jpc_putuint16(out, sot->tileno) ||
+      jpc_putuint32(out, sot->len) ||
+      jpc_putuint8(out, sot->partno) ||
+      jpc_putuint8(out, sot->numparts)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_sot_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	jpc_sot_t *sot = &ms->parms.sot;
-	fprintf(out, "tileno = %d; len = %d; partno = %d; numparts = %d\n",
-	  sot->tileno, sot->len, sot->partno, sot->numparts);
-	return 0;
+    jpc_sot_t *sot = &ms->parms.sot;
+    fprintf(out, "tileno = %d; len = %d; partno = %d; numparts = %d\n",
+            (int)sot->tileno, (int)sot->len, sot->partno, sot->numparts);
+    return 0;
 }
 
 /******************************************************************************\
@@ -512,242 +513,244 @@ static int jpc_sot_dumpparms(jpc_ms_t *ms, FILE *out)
 
 static void jpc_siz_destroyparms(jpc_ms_t *ms)
 {
-	jpc_siz_t *siz = &ms->parms.siz;
-	if (siz->comps) {
-		jas_free(siz->comps);
-	}
+    jpc_siz_t *siz = &ms->parms.siz;
+    if (siz->comps) {
+        jas_free(siz->comps);
+    }
 }
 
 static int jpc_siz_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_siz_t *siz = &ms->parms.siz;
-	int i;
-	uint_fast8_t tmp;
-	if (jpc_getuint16(in, &siz->caps) ||
-	  jpc_getuint32(in, &siz->width) ||
-	  jpc_getuint32(in, &siz->height) ||
-	  jpc_getuint32(in, &siz->xoff) ||
-	  jpc_getuint32(in, &siz->yoff) ||
-	  jpc_getuint32(in, &siz->tilewidth) ||
-	  jpc_getuint32(in, &siz->tileheight) ||
-	  jpc_getuint32(in, &siz->tilexoff) ||
-	  jpc_getuint32(in, &siz->tileyoff) ||
-	  jpc_getuint16(in, &siz->numcomps)) {
-		return -1;
-	}
-	if (!siz->width || !siz->height || !siz->tilewidth ||
-	  !siz->tileheight || !siz->numcomps) {
-		return -1;
-	}
-	if (!(siz->comps = jas_malloc(siz->numcomps * sizeof(jpc_sizcomp_t)))) {
-		return -1;
-	}
-	for (i = 0; i < siz->numcomps; ++i) {
-		if (jpc_getuint8(in, &tmp) ||
-		  jpc_getuint8(in, &siz->comps[i].hsamp) ||
-		  jpc_getuint8(in, &siz->comps[i].vsamp)) {
-			jas_free(siz->comps);
-			return -1;
-		}
-		siz->comps[i].sgnd = (tmp >> 7) & 1;
-		siz->comps[i].prec = (tmp & 0x7f) + 1;
-	}
-	if (jas_stream_eof(in)) {
-		jas_free(siz->comps);
-		return -1;
-	}
-	return 0;
+    jpc_siz_t *siz = &ms->parms.siz;
+    int i;
+    uint_fast8_t tmp;
+    if (jpc_getuint16(in, &siz->caps) ||
+      jpc_getuint32(in, &siz->width) ||
+      jpc_getuint32(in, &siz->height) ||
+      jpc_getuint32(in, &siz->xoff) ||
+      jpc_getuint32(in, &siz->yoff) ||
+      jpc_getuint32(in, &siz->tilewidth) ||
+      jpc_getuint32(in, &siz->tileheight) ||
+      jpc_getuint32(in, &siz->tilexoff) ||
+      jpc_getuint32(in, &siz->tileyoff) ||
+      jpc_getuint16(in, &siz->numcomps)) {
+        return -1;
+    }
+    if (!siz->width || !siz->height || !siz->tilewidth ||
+      !siz->tileheight || !siz->numcomps) {
+        return -1;
+    }
+    if (!(siz->comps = jas_malloc(siz->numcomps * sizeof(jpc_sizcomp_t)))) {
+        return -1;
+    }
+    for (i = 0; i < siz->numcomps; ++i) {
+        if (jpc_getuint8(in, &tmp) ||
+          jpc_getuint8(in, &siz->comps[i].hsamp) ||
+          jpc_getuint8(in, &siz->comps[i].vsamp)) {
+            jas_free(siz->comps);
+            return -1;
+        }
+        siz->comps[i].sgnd = (tmp >> 7) & 1;
+        siz->comps[i].prec = (tmp & 0x7f) + 1;
+    }
+    if (jas_stream_eof(in)) {
+        jas_free(siz->comps);
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_siz_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	jpc_siz_t *siz = &ms->parms.siz;
-	int i;
-	assert(siz->width && siz->height && siz->tilewidth &&
-	  siz->tileheight && siz->numcomps);
-	if (jpc_putuint16(out, siz->caps) ||
-	  jpc_putuint32(out, siz->width) ||
-	  jpc_putuint32(out, siz->height) ||
-	  jpc_putuint32(out, siz->xoff) ||
-	  jpc_putuint32(out, siz->yoff) ||
-	  jpc_putuint32(out, siz->tilewidth) ||
-	  jpc_putuint32(out, siz->tileheight) ||
-	  jpc_putuint32(out, siz->tilexoff) ||
-	  jpc_putuint32(out, siz->tileyoff) ||
-	  jpc_putuint16(out, siz->numcomps)) {
-		return -1;
-	}
-	for (i = 0; i < siz->numcomps; ++i) {
-		if (jpc_putuint8(out, ((siz->comps[i].sgnd & 1) << 7) |
-		  ((siz->comps[i].prec - 1) & 0x7f)) ||
-		  jpc_putuint8(out, siz->comps[i].hsamp) ||
-		  jpc_putuint8(out, siz->comps[i].vsamp)) {
-			return -1;
-		}
-	}
-	return 0;
+    jpc_siz_t *siz = &ms->parms.siz;
+    int i;
+    assert(siz->width && siz->height && siz->tilewidth &&
+      siz->tileheight && siz->numcomps);
+    if (jpc_putuint16(out, siz->caps) ||
+      jpc_putuint32(out, siz->width) ||
+      jpc_putuint32(out, siz->height) ||
+      jpc_putuint32(out, siz->xoff) ||
+      jpc_putuint32(out, siz->yoff) ||
+      jpc_putuint32(out, siz->tilewidth) ||
+      jpc_putuint32(out, siz->tileheight) ||
+      jpc_putuint32(out, siz->tilexoff) ||
+      jpc_putuint32(out, siz->tileyoff) ||
+      jpc_putuint16(out, siz->numcomps)) {
+        return -1;
+    }
+    for (i = 0; i < siz->numcomps; ++i) {
+        if (jpc_putuint8(out, ((siz->comps[i].sgnd & 1) << 7) |
+          ((siz->comps[i].prec - 1) & 0x7f)) ||
+          jpc_putuint8(out, siz->comps[i].hsamp) ||
+          jpc_putuint8(out, siz->comps[i].vsamp)) {
+            return -1;
+        }
+    }
+    return 0;
 }
 
 static int jpc_siz_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	jpc_siz_t *siz = &ms->parms.siz;
-	int i;
-	fprintf(out, "caps = 0x%02x;\n", siz->caps);
-	fprintf(out, "width = %d; height = %d; xoff = %d; yoff = %d;\n",
-	  siz->width, siz->height, siz->xoff, siz->yoff);
-	fprintf(out, "tilewidth = %d; tileheight = %d; tilexoff = %d; "
-	  "tileyoff = %d;\n", siz->tilewidth, siz->tileheight, siz->tilexoff,
-	  siz->tileyoff);
-	for (i = 0; i < siz->numcomps; ++i) {
-		fprintf(out, "prec[%d] = %d; sgnd[%d] = %d; hsamp[%d] = %d; "
-		  "vsamp[%d] = %d\n", i, siz->comps[i].prec, i,
-		  siz->comps[i].sgnd, i, siz->comps[i].hsamp, i,
-		  siz->comps[i].vsamp);
-	}
-	return 0;
-}
-
-/******************************************************************************\
+    jpc_siz_t *siz = &ms->parms.siz;
+    int i;
+    fprintf(out, "caps = 0x%02x;\n", (unsigned int)siz->caps);
+    fprintf(out, "width = %d; height = %d; xoff = %d; yoff = %d;\n",
+            (int)siz->width, (int)siz->height, (int)siz->xoff, (int)siz->yoff);
+    fprintf(out, "tilewidth = %d; tileheight = %d; tilexoff = %d; "
+            "tileyoff = %d;\n",
+            (int)siz->tilewidth, (int)siz->tileheight,
+            (int)siz->tilexoff, (int)siz->tileyoff);
+    for (i = 0; i < siz->numcomps; ++i) {
+        fprintf(out, "prec[%d] = %d; sgnd[%d] = %d; hsamp[%d] = %d; "
+                "vsamp[%d] = %d\n", i, siz->comps[i].prec, i,
+                siz->comps[i].sgnd, i, siz->comps[i].hsamp, i,
+                siz->comps[i].vsamp);
+    }
+    return 0;
+}
+
+/*****************************************************************************\
 * COD marker segment operations.
-\******************************************************************************/
+\*****************************************************************************/
 
 static void jpc_cod_destroyparms(jpc_ms_t *ms)
 {
-	jpc_cod_t *cod = &ms->parms.cod;
-	jpc_cox_destroycompparms(&cod->compparms);
+    jpc_cod_t *cod = &ms->parms.cod;
+    jpc_cox_destroycompparms(&cod->compparms);
 }
 
 static int jpc_cod_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_cod_t *cod = &ms->parms.cod;
-	if (jpc_getuint8(in, &cod->csty)) {
-		return -1;
-	}
-	if (jpc_getuint8(in, &cod->prg) ||
-	  jpc_getuint16(in, &cod->numlyrs) ||
-	  jpc_getuint8(in, &cod->mctrans)) {
-		return -1;
-	}
-	if (jpc_cox_getcompparms(ms, cstate, in,
-	  (cod->csty & JPC_COX_PRT) != 0, &cod->compparms)) {
-		return -1;
-	}
-	if (jas_stream_eof(in)) {
-		jpc_cod_destroyparms(ms);
-		return -1;
-	}
-	return 0;
+    jpc_cod_t *cod = &ms->parms.cod;
+    if (jpc_getuint8(in, &cod->csty)) {
+        return -1;
+    }
+    if (jpc_getuint8(in, &cod->prg) ||
+      jpc_getuint16(in, &cod->numlyrs) ||
+      jpc_getuint8(in, &cod->mctrans)) {
+        return -1;
+    }
+    if (jpc_cox_getcompparms(ms, cstate, in,
+      (cod->csty & JPC_COX_PRT) != 0, &cod->compparms)) {
+        return -1;
+    }
+    if (jas_stream_eof(in)) {
+        jpc_cod_destroyparms(ms);
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_cod_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	jpc_cod_t *cod = &ms->parms.cod;
-	assert(cod->numlyrs > 0 && cod->compparms.numdlvls <= 32);
-	assert(cod->compparms.numdlvls == cod->compparms.numrlvls - 1);
-	if (jpc_putuint8(out, cod->compparms.csty) ||
-	  jpc_putuint8(out, cod->prg) ||
-	  jpc_putuint16(out, cod->numlyrs) ||
-	  jpc_putuint8(out, cod->mctrans)) {
-		return -1;
-	}
-	if (jpc_cox_putcompparms(ms, cstate, out,
-	  (cod->csty & JPC_COX_PRT) != 0, &cod->compparms)) {
-		return -1;
-	}
-	return 0;
+    jpc_cod_t *cod = &ms->parms.cod;
+    assert(cod->numlyrs > 0 && cod->compparms.numdlvls <= 32);
+    assert(cod->compparms.numdlvls == cod->compparms.numrlvls - 1);
+    if (jpc_putuint8(out, cod->compparms.csty) ||
+      jpc_putuint8(out, cod->prg) ||
+      jpc_putuint16(out, cod->numlyrs) ||
+      jpc_putuint8(out, cod->mctrans)) {
+        return -1;
+    }
+    if (jpc_cox_putcompparms(ms, cstate, out,
+      (cod->csty & JPC_COX_PRT) != 0, &cod->compparms)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_cod_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	jpc_cod_t *cod = &ms->parms.cod;
-	int i;
-	fprintf(out, "csty = 0x%02x;\n", cod->compparms.csty);
-	fprintf(out, "numdlvls = %d; qmfbid = %d; mctrans = %d\n",
-	  cod->compparms.numdlvls, cod->compparms.qmfbid, cod->mctrans);
-	fprintf(out, "prg = %d; numlyrs = %d;\n",
-	  cod->prg, cod->numlyrs);
-	fprintf(out, "cblkwidthval = %d; cblkheightval = %d; "
-	  "cblksty = 0x%02x;\n", cod->compparms.cblkwidthval, cod->compparms.cblkheightval,
-	  cod->compparms.cblksty);
-	if (cod->csty & JPC_COX_PRT) {
-		for (i = 0; i < cod->compparms.numrlvls; ++i) {
-			fprintf(stderr, "prcwidth[%d] = %d, prcheight[%d] = %d\n",
-			  i, cod->compparms.rlvls[i].parwidthval,
-			  i, cod->compparms.rlvls[i].parheightval);
-		}
-	}
-	return 0;
-}
-
-/******************************************************************************\
+    jpc_cod_t *cod = &ms->parms.cod;
+    int i;
+    fprintf(out, "csty = 0x%02x;\n", cod->compparms.csty);
+    fprintf(out, "numdlvls = %d; qmfbid = %d; mctrans = %d\n",
+            cod->compparms.numdlvls, cod->compparms.qmfbid, cod->mctrans);
+    fprintf(out, "prg = %d; numlyrs = %d;\n",
+            cod->prg, (int)cod->numlyrs);
+    fprintf(out, "cblkwidthval = %d; cblkheightval = %d; "
+            "cblksty = 0x%02x;\n", cod->compparms.cblkwidthval, cod->compparms.cblkheightval,
+            cod->compparms.cblksty);
+    if (cod->csty & JPC_COX_PRT) {
+        for (i = 0; i < cod->compparms.numrlvls; ++i) {
+            fprintf(stderr, "prcwidth[%d] = %d, prcheight[%d] = %d\n",
+                    i, cod->compparms.rlvls[i].parwidthval,
+                    i, cod->compparms.rlvls[i].parheightval);
+        }
+    }
+    return 0;
+}
+
+/*****************************************************************************\
 * COC marker segment operations.
-\******************************************************************************/
+\*****************************************************************************/
 
 static void jpc_coc_destroyparms(jpc_ms_t *ms)
 {
-	jpc_coc_t *coc = &ms->parms.coc;
-	jpc_cox_destroycompparms(&coc->compparms);
+    jpc_coc_t *coc = &ms->parms.coc;
+    jpc_cox_destroycompparms(&coc->compparms);
 }
 
 static int jpc_coc_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_coc_t *coc = &ms->parms.coc;
-	uint_fast8_t tmp;
-	if (cstate->numcomps <= 256) {
-		if (jpc_getuint8(in, &tmp)) {
-			return -1;
-		}
-		coc->compno = tmp;
-	} else {
-		if (jpc_getuint16(in, &coc->compno)) {
-			return -1;
-		}
-	}
-	if (jpc_getuint8(in, &coc->compparms.csty)) {
-		return -1;
-	}
-	if (jpc_cox_getcompparms(ms, cstate, in,
-	  (coc->compparms.csty & JPC_COX_PRT) != 0, &coc->compparms)) {
-		return -1;
-	}
-	if (jas_stream_eof(in)) {
-		return -1;
-	}
-	return 0;
+    jpc_coc_t *coc = &ms->parms.coc;
+    uint_fast8_t tmp;
+    if (cstate->numcomps <= 256) {
+        if (jpc_getuint8(in, &tmp)) {
+            return -1;
+        }
+        coc->compno = tmp;
+    } else {
+        if (jpc_getuint16(in, &coc->compno)) {
+            return -1;
+        }
+    }
+    if (jpc_getuint8(in, &coc->compparms.csty)) {
+        return -1;
+    }
+    if (jpc_cox_getcompparms(ms, cstate, in,
+      (coc->compparms.csty & JPC_COX_PRT) != 0, &coc->compparms)) {
+        return -1;
+    }
+    if (jas_stream_eof(in)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_coc_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	jpc_coc_t *coc = &ms->parms.coc;
-	assert(coc->compparms.numdlvls <= 32);
-	if (cstate->numcomps <= 256) {
-		if (jpc_putuint8(out, coc->compno)) {
-			return -1;
-		}
-	} else {
-		if (jpc_putuint16(out, coc->compno)) {
-			return -1;
-		}
-	}
-	if (jpc_putuint8(out, coc->compparms.csty)) {
-		return -1;
-	}
-	if (jpc_cox_putcompparms(ms, cstate, out,
-	  (coc->compparms.csty & JPC_COX_PRT) != 0, &coc->compparms)) {
-		return -1;
-	}
-	return 0;
+    jpc_coc_t *coc = &ms->parms.coc;
+    assert(coc->compparms.numdlvls <= 32);
+    if (cstate->numcomps <= 256) {
+        if (jpc_putuint8(out, coc->compno)) {
+            return -1;
+        }
+    } else {
+        if (jpc_putuint16(out, coc->compno)) {
+            return -1;
+        }
+    }
+    if (jpc_putuint8(out, coc->compparms.csty)) {
+        return -1;
+    }
+    if (jpc_cox_putcompparms(ms, cstate, out,
+      (coc->compparms.csty & JPC_COX_PRT) != 0, &coc->compparms)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_coc_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	jpc_coc_t *coc = &ms->parms.coc;
-	fprintf(out, "compno = %d; csty = 0x%02x; numdlvls = %d;\n",
-	  coc->compno, coc->compparms.csty, coc->compparms.numdlvls);
-	fprintf(out, "cblkwidthval = %d; cblkheightval = %d; "
-	  "cblksty = 0x%02x; qmfbid = %d;\n", coc->compparms.cblkwidthval,
-	  coc->compparms.cblkheightval, coc->compparms.cblksty, coc->compparms.qmfbid);
-	return 0;
+    jpc_coc_t *coc = &ms->parms.coc;
+    fprintf(out, "compno = %d; csty = 0x%02x; numdlvls = %d;\n",
+            (int)coc->compno, coc->compparms.csty, coc->compparms.numdlvls);
+    fprintf(out, "cblkwidthval = %d; cblkheightval = %d; "
+            "cblksty = 0x%02x; qmfbid = %d;\n", coc->compparms.cblkwidthval,
+            coc->compparms.cblkheightval, coc->compparms.cblksty,
+            coc->compparms.qmfbid);
+    return 0;
 }
 /******************************************************************************\
 * COD/COC marker segment operation helper functions.
@@ -760,58 +763,58 @@ static void jpc_cox_destroycompparms(jpc_coxcp_t *compparms)
 static int jpc_cox_getcompparms(jpc_ms_t *ms, jpc_cstate_t *cstate,
   jas_stream_t *in, int prtflag, jpc_coxcp_t *compparms)
 {
-	uint_fast8_t tmp;
-	int i;
-	if (jpc_getuint8(in, &compparms->numdlvls) ||
-	  jpc_getuint8(in, &compparms->cblkwidthval) ||
-	  jpc_getuint8(in, &compparms->cblkheightval) ||
-	  jpc_getuint8(in, &compparms->cblksty) ||
-	  jpc_getuint8(in, &compparms->qmfbid)) {
-		return -1;
-	}
-	compparms->numrlvls = compparms->numdlvls + 1;
-	if (prtflag) {
-		for (i = 0; i < compparms->numrlvls; ++i) {
-			if (jpc_getuint8(in, &tmp)) {
-				jpc_cox_destroycompparms(compparms);
-				return -1;
-			}
-			compparms->rlvls[i].parwidthval = tmp & 0xf;
-			compparms->rlvls[i].parheightval = (tmp >> 4) & 0xf;
-		}
+    uint_fast8_t tmp;
+    int i;
+    if (jpc_getuint8(in, &compparms->numdlvls) ||
+      jpc_getuint8(in, &compparms->cblkwidthval) ||
+      jpc_getuint8(in, &compparms->cblkheightval) ||
+      jpc_getuint8(in, &compparms->cblksty) ||
+      jpc_getuint8(in, &compparms->qmfbid)) {
+        return -1;
+    }
+    compparms->numrlvls = compparms->numdlvls + 1;
+    if (prtflag) {
+        for (i = 0; i < compparms->numrlvls; ++i) {
+            if (jpc_getuint8(in, &tmp)) {
+                jpc_cox_destroycompparms(compparms);
+                return -1;
+            }
+            compparms->rlvls[i].parwidthval = tmp & 0xf;
+            compparms->rlvls[i].parheightval = (tmp >> 4) & 0xf;
+        }
 /* Sigh.  This bit should be in the same field in both COC and COD mrk segs. */
 compparms->csty |= JPC_COX_PRT;
-	} else {
-	}
-	if (jas_stream_eof(in)) {
-		jpc_cox_destroycompparms(compparms);
-		return -1;
-	}
-	return 0;
+    } else {
+    }
+    if (jas_stream_eof(in)) {
+        jpc_cox_destroycompparms(compparms);
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_cox_putcompparms(jpc_ms_t *ms, jpc_cstate_t *cstate,
   jas_stream_t *out, int prtflag, jpc_coxcp_t *compparms)
 {
-	int i;
-	assert(compparms->numdlvls <= 32);
-	if (jpc_putuint8(out, compparms->numdlvls) ||
-	  jpc_putuint8(out, compparms->cblkwidthval) ||
-	  jpc_putuint8(out, compparms->cblkheightval) ||
-	  jpc_putuint8(out, compparms->cblksty) ||
-	  jpc_putuint8(out, compparms->qmfbid)) {
-		return -1;
-	}
-	if (prtflag) {
-		for (i = 0; i < compparms->numrlvls; ++i) {
-			if (jpc_putuint8(out,
-			  ((compparms->rlvls[i].parheightval & 0xf) << 4) |
-			  (compparms->rlvls[i].parwidthval & 0xf))) {
-				return -1;
-			}
-		}
-	}
-	return 0;
+    int i;
+    assert(compparms->numdlvls <= 32);
+    if (jpc_putuint8(out, compparms->numdlvls) ||
+      jpc_putuint8(out, compparms->cblkwidthval) ||
+      jpc_putuint8(out, compparms->cblkheightval) ||
+      jpc_putuint8(out, compparms->cblksty) ||
+      jpc_putuint8(out, compparms->qmfbid)) {
+        return -1;
+    }
+    if (prtflag) {
+        for (i = 0; i < compparms->numrlvls; ++i) {
+            if (jpc_putuint8(out,
+              ((compparms->rlvls[i].parheightval & 0xf) << 4) |
+              (compparms->rlvls[i].parwidthval & 0xf))) {
+                return -1;
+            }
+        }
+    }
+    return 0;
 }
 
 /******************************************************************************\
@@ -820,50 +823,50 @@ static int jpc_cox_putcompparms(jpc_ms_t *ms, jpc_cstate_t *cstate,
 
 static int jpc_rgn_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_rgn_t *rgn = &ms->parms.rgn;
-	uint_fast8_t tmp;
-	if (cstate->numcomps <= 256) {
-		if (jpc_getuint8(in, &tmp)) {
-			return -1;
-		}
-		rgn->compno = tmp;
-	} else {
-		if (jpc_getuint16(in, &rgn->compno)) {
-			return -1;
-		}
-	}
-	if (jpc_getuint8(in, &rgn->roisty) ||
-	  jpc_getuint8(in, &rgn->roishift)) {
-		return -1;
-	}
-	return 0;
+    jpc_rgn_t *rgn = &ms->parms.rgn;
+    uint_fast8_t tmp;
+    if (cstate->numcomps <= 256) {
+        if (jpc_getuint8(in, &tmp)) {
+            return -1;
+        }
+        rgn->compno = tmp;
+    } else {
+        if (jpc_getuint16(in, &rgn->compno)) {
+            return -1;
+        }
+    }
+    if (jpc_getuint8(in, &rgn->roisty) ||
+      jpc_getuint8(in, &rgn->roishift)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_rgn_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	jpc_rgn_t *rgn = &ms->parms.rgn;
-	if (cstate->numcomps <= 256) {
-		if (jpc_putuint8(out, rgn->compno)) {
-			return -1;
-		}
-	} else {
-		if (jpc_putuint16(out, rgn->compno)) {
-			return -1;
-		}
-	}
-	if (jpc_putuint8(out, rgn->roisty) ||
-	  jpc_putuint8(out, rgn->roishift)) {
-		return -1;
-	}
-	return 0;
+    jpc_rgn_t *rgn = &ms->parms.rgn;
+    if (cstate->numcomps <= 256) {
+        if (jpc_putuint8(out, rgn->compno)) {
+            return -1;
+        }
+    } else {
+        if (jpc_putuint16(out, rgn->compno)) {
+            return -1;
+        }
+    }
+    if (jpc_putuint8(out, rgn->roisty) ||
+      jpc_putuint8(out, rgn->roishift)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_rgn_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	jpc_rgn_t *rgn = &ms->parms.rgn;
-	fprintf(out, "compno = %d; roisty = %d; roishift = %d\n",
-	  rgn->compno, rgn->roisty, rgn->roishift);
-	return 0;
+    jpc_rgn_t *rgn = &ms->parms.rgn;
+    fprintf(out, "compno = %d; roisty = %d; roishift = %d\n",
+            (int)rgn->compno, rgn->roisty, rgn->roishift);
+    return 0;
 }
 
 /******************************************************************************\
@@ -872,34 +875,34 @@ static int jpc_rgn_dumpparms(jpc_ms_t *ms, FILE *out)
 
 static void jpc_qcd_destroyparms(jpc_ms_t *ms)
 {
-	jpc_qcd_t *qcd = &ms->parms.qcd;
-	jpc_qcx_destroycompparms(&qcd->compparms);
+    jpc_qcd_t *qcd = &ms->parms.qcd;
+    jpc_qcx_destroycompparms(&qcd->compparms);
 }
 
 static int jpc_qcd_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_qcxcp_t *compparms = &ms->parms.qcd.compparms;
-	return jpc_qcx_getcompparms(compparms, cstate, in, ms->len);
+    jpc_qcxcp_t *compparms = &ms->parms.qcd.compparms;
+    return jpc_qcx_getcompparms(compparms, cstate, in, ms->len);
 }
 
 static int jpc_qcd_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	jpc_qcxcp_t *compparms = &ms->parms.qcd.compparms;
-	return jpc_qcx_putcompparms(compparms, cstate, out);
+    jpc_qcxcp_t *compparms = &ms->parms.qcd.compparms;
+    return jpc_qcx_putcompparms(compparms, cstate, out);
 }
 
 static int jpc_qcd_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	jpc_qcd_t *qcd = &ms->parms.qcd;
-	int i;
-	fprintf(out, "qntsty = %d; numguard = %d; numstepsizes = %d\n",
-	  (int) qcd->compparms.qntsty, qcd->compparms.numguard, qcd->compparms.numstepsizes);
-	for (i = 0; i < qcd->compparms.numstepsizes; ++i) {
-		fprintf(out, "expn[%d] = 0x%04x; mant[%d] = 0x%04x;\n",
-		  i, (unsigned) JPC_QCX_GETEXPN(qcd->compparms.stepsizes[i]),
-		  i, (unsigned) JPC_QCX_GETMANT(qcd->compparms.stepsizes[i]));
-	}
-	return 0;
+    jpc_qcd_t *qcd = &ms->parms.qcd;
+    int i;
+    fprintf(out, "qntsty = %d; numguard = %d; numstepsizes = %d\n",
+      (int) qcd->compparms.qntsty, qcd->compparms.numguard, qcd->compparms.numstepsizes);
+    for (i = 0; i < qcd->compparms.numstepsizes; ++i) {
+        fprintf(out, "expn[%d] = 0x%04x; mant[%d] = 0x%04x;\n",
+          i, (unsigned) JPC_QCX_GETEXPN(qcd->compparms.stepsizes[i]),
+          i, (unsigned) JPC_QCX_GETMANT(qcd->compparms.stepsizes[i]));
+    }
+    return 0;
 }
 
 /******************************************************************************\
@@ -908,133 +911,134 @@ static int jpc_qcd_dumpparms(jpc_ms_t *ms, FILE *out)
 
 static void jpc_qcc_destroyparms(jpc_ms_t *ms)
 {
-	jpc_qcc_t *qcc = &ms->parms.qcc;
-	jpc_qcx_destroycompparms(&qcc->compparms);
+    jpc_qcc_t *qcc = &ms->parms.qcc;
+    jpc_qcx_destroycompparms(&qcc->compparms);
 }
 
 static int jpc_qcc_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_qcc_t *qcc = &ms->parms.qcc;
-	uint_fast8_t tmp;
-	int len;
-	len = ms->len;
-	if (cstate->numcomps <= 256) {
-		jpc_getuint8(in, &tmp);
-		qcc->compno = tmp;
-		--len;
-	} else {
-		jpc_getuint16(in, &qcc->compno);
-		len -= 2;
-	}
-	if (jpc_qcx_getcompparms(&qcc->compparms, cstate, in, len)) {
-		return -1;
-	}
-	if (jas_stream_eof(in)) {
-		jpc_qcc_destroyparms(ms);
-		return -1;
-	}
-	return 0;
+    jpc_qcc_t *qcc = &ms->parms.qcc;
+    uint_fast8_t tmp;
+    int len;
+    len = ms->len;
+    if (cstate->numcomps <= 256) {
+        jpc_getuint8(in, &tmp);
+        qcc->compno = tmp;
+        --len;
+    } else {
+        jpc_getuint16(in, &qcc->compno);
+        len -= 2;
+    }
+    if (jpc_qcx_getcompparms(&qcc->compparms, cstate, in, len)) {
+        return -1;
+    }
+    if (jas_stream_eof(in)) {
+        jpc_qcc_destroyparms(ms);
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_qcc_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	jpc_qcc_t *qcc = &ms->parms.qcc;
-	if (cstate->numcomps <= 256) {
-		jpc_putuint8(out, qcc->compno);
-	} else {
-		jpc_putuint16(out, qcc->compno);
-	}
-	if (jpc_qcx_putcompparms(&qcc->compparms, cstate, out)) {
-		return -1;
-	}
-	return 0;
+    jpc_qcc_t *qcc = &ms->parms.qcc;
+    if (cstate->numcomps <= 256) {
+        jpc_putuint8(out, qcc->compno);
+    } else {
+        jpc_putuint16(out, qcc->compno);
+    }
+    if (jpc_qcx_putcompparms(&qcc->compparms, cstate, out)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_qcc_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	jpc_qcc_t *qcc = &ms->parms.qcc;
-	int i;
-	fprintf(out, "compno = %d; qntsty = %d; numguard = %d; "
-	  "numstepsizes = %d\n", qcc->compno, qcc->compparms.qntsty, qcc->compparms.numguard,
-	  qcc->compparms.numstepsizes);
-	for (i = 0; i < qcc->compparms.numstepsizes; ++i) {
-		fprintf(out, "expn[%d] = 0x%04x; mant[%d] = 0x%04x;\n",
-		  i, (unsigned) JPC_QCX_GETEXPN(qcc->compparms.stepsizes[i]),
-		  i, (unsigned) JPC_QCX_GETMANT(qcc->compparms.stepsizes[i]));
-	}
-	return 0;
-}
-
-/******************************************************************************\
+    jpc_qcc_t *qcc = &ms->parms.qcc;
+    int i;
+    fprintf(out, "compno = %d; qntsty = %d; numguard = %d; "
+            "numstepsizes = %d\n",
+            (int)qcc->compno, qcc->compparms.qntsty, qcc->compparms.numguard,
+            qcc->compparms.numstepsizes);
+    for (i = 0; i < qcc->compparms.numstepsizes; ++i) {
+        fprintf(out, "expn[%d] = 0x%04x; mant[%d] = 0x%04x;\n",
+          i, (unsigned) JPC_QCX_GETEXPN(qcc->compparms.stepsizes[i]),
+          i, (unsigned) JPC_QCX_GETMANT(qcc->compparms.stepsizes[i]));
+    }
+    return 0;
+}
+
+/*****************************************************************************\
 * QCD/QCC marker segment helper functions.
-\******************************************************************************/
+\*****************************************************************************/
 
 static void jpc_qcx_destroycompparms(jpc_qcxcp_t *compparms)
 {
-	if (compparms->stepsizes) {
-		jas_free(compparms->stepsizes);
-	}
+    if (compparms->stepsizes) {
+        jas_free(compparms->stepsizes);
+    }
 }
 
 static int jpc_qcx_getcompparms(jpc_qcxcp_t *compparms, jpc_cstate_t *cstate,
   jas_stream_t *in, uint_fast16_t len)
 {
-	uint_fast8_t tmp;
-	int n;
-	int i;
-	n = 0;
-	jpc_getuint8(in, &tmp);
-	++n;
-	compparms->qntsty = tmp & 0x1f;
-	compparms->numguard = (tmp >> 5) & 7;
-	switch (compparms->qntsty) {
-	case JPC_QCX_SIQNT:
-		compparms->numstepsizes = 1;
-		break;
-	case JPC_QCX_NOQNT:
-		compparms->numstepsizes = (len - n);
-		break;
-	case JPC_QCX_SEQNT:
-		/* XXX - this is a hack */
-		compparms->numstepsizes = (len - n) / 2;
-		break;
-	}
+    uint_fast8_t tmp;
+    int n;
+    int i;
+    n = 0;
+    jpc_getuint8(in, &tmp);
+    ++n;
+    compparms->qntsty = tmp & 0x1f;
+    compparms->numguard = (tmp >> 5) & 7;
+    switch (compparms->qntsty) {
+    case JPC_QCX_SIQNT:
+        compparms->numstepsizes = 1;
+        break;
+    case JPC_QCX_NOQNT:
+        compparms->numstepsizes = (len - n);
+        break;
+    case JPC_QCX_SEQNT:
+        /* XXX - this is a hack */
+        compparms->numstepsizes = (len - n) / 2;
+        break;
+    }
 if (compparms->numstepsizes > 0) {
-	compparms->stepsizes = jas_malloc(compparms->numstepsizes *
-	  sizeof(uint_fast32_t));
-	assert(compparms->stepsizes);
-	for (i = 0; i < compparms->numstepsizes; ++i) {
-		if (compparms->qntsty == JPC_QCX_NOQNT) {
-			jpc_getuint8(in, &tmp);
-			compparms->stepsizes[i] = JPC_QCX_EXPN(tmp >> 3);
-		} else {
-			jpc_getuint16(in, &compparms->stepsizes[i]);
-		}
-	}
+    compparms->stepsizes = jas_malloc(compparms->numstepsizes *
+      sizeof(uint_fast32_t));
+    assert(compparms->stepsizes);
+    for (i = 0; i < compparms->numstepsizes; ++i) {
+        if (compparms->qntsty == JPC_QCX_NOQNT) {
+            jpc_getuint8(in, &tmp);
+            compparms->stepsizes[i] = JPC_QCX_EXPN(tmp >> 3);
+        } else {
+            jpc_getuint16(in, &compparms->stepsizes[i]);
+        }
+    }
 } else {
-	compparms->stepsizes = 0;
+    compparms->stepsizes = 0;
 }
-	if (jas_stream_error(in) || jas_stream_eof(in)) {
-		jpc_qcx_destroycompparms(compparms);
-		return -1;
-	}
-	return 0;
+    if (jas_stream_error(in) || jas_stream_eof(in)) {
+        jpc_qcx_destroycompparms(compparms);
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_qcx_putcompparms(jpc_qcxcp_t *compparms, jpc_cstate_t *cstate,
   jas_stream_t *out)
 {
-	int i;
-	jpc_putuint8(out, ((compparms->numguard & 7) << 5) | compparms->qntsty);
-	for (i = 0; i < compparms->numstepsizes; ++i) {
-		if (compparms->qntsty == JPC_QCX_NOQNT) {
-			jpc_putuint8(out, JPC_QCX_GETEXPN(
-			  compparms->stepsizes[i]) << 3);
-		} else {
-			jpc_putuint16(out, compparms->stepsizes[i]);
-		}
-	}
-	return 0;
+    int i;
+    jpc_putuint8(out, ((compparms->numguard & 7) << 5) | compparms->qntsty);
+    for (i = 0; i < compparms->numstepsizes; ++i) {
+        if (compparms->qntsty == JPC_QCX_NOQNT) {
+            jpc_putuint8(out, JPC_QCX_GETEXPN(
+              compparms->stepsizes[i]) << 3);
+        } else {
+            jpc_putuint16(out, compparms->stepsizes[i]);
+        }
+    }
+    return 0;
 }
 
 /******************************************************************************\
@@ -1043,27 +1047,27 @@ static int jpc_qcx_putcompparms(jpc_qcxcp_t *compparms, jpc_cstate_t *cstate,
 
 static int jpc_sop_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_sop_t *sop = &ms->parms.sop;
-	if (jpc_getuint16(in, &sop->seqno)) {
-		return -1;
-	}
-	return 0;
+    jpc_sop_t *sop = &ms->parms.sop;
+    if (jpc_getuint16(in, &sop->seqno)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_sop_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	jpc_sop_t *sop = &ms->parms.sop;
-	if (jpc_putuint16(out, sop->seqno)) {
-		return -1;
-	}
-	return 0;
+    jpc_sop_t *sop = &ms->parms.sop;
+    if (jpc_putuint16(out, sop->seqno)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_sop_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	jpc_sop_t *sop = &ms->parms.sop;
-	fprintf(out, "seqno = %d;\n", sop->seqno);
-	return 0;
+    jpc_sop_t *sop = &ms->parms.sop;
+    fprintf(out, "seqno = %d;\n", (int)sop->seqno);
+    return 0;
 }
 
 /******************************************************************************\
@@ -1072,61 +1076,61 @@ static int jpc_sop_dumpparms(jpc_ms_t *ms, FILE *out)
 
 static void jpc_ppm_destroyparms(jpc_ms_t *ms)
 {
-	jpc_ppm_t *ppm = &ms->parms.ppm;
-	if (ppm->data) {
-		jas_free(ppm->data);
-	}
+    jpc_ppm_t *ppm = &ms->parms.ppm;
+    if (ppm->data) {
+        jas_free(ppm->data);
+    }
 }
 
 static int jpc_ppm_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_ppm_t *ppm = &ms->parms.ppm;
-
-	ppm->data = 0;
-
-	if (ms->len < 1) {
-		goto error;
-	}
-	if (jpc_getuint8(in, &ppm->ind)) {
-		goto error;
-	}
-
-	ppm->len = ms->len - 1;
-	if (ppm->len > 0) {
-		if (!(ppm->data = jas_malloc(ppm->len * sizeof(unsigned char)))) {
-			goto error;
-		}
-		if (jas_stream_read(in, ppm->data, ppm->len) != ppm->len) {
-			goto error;
-		}
-	} else {
-		ppm->data = 0;
-	}
-	return 0;
+    jpc_ppm_t *ppm = &ms->parms.ppm;
+
+    ppm->data = 0;
+
+    if (ms->len < 1) {
+        goto error;
+    }
+    if (jpc_getuint8(in, &ppm->ind)) {
+        goto error;
+    }
+
+    ppm->len = ms->len - 1;
+    if (ppm->len > 0) {
+        if (!(ppm->data = jas_malloc(ppm->len * sizeof(unsigned char)))) {
+            goto error;
+        }
+        if (jas_stream_read(in, ppm->data, ppm->len) != ppm->len) {
+            goto error;
+        }
+    } else {
+        ppm->data = 0;
+    }
+    return 0;
 
 error:
-	jpc_ppm_destroyparms(ms);
-	return -1;
+    jpc_ppm_destroyparms(ms);
+    return -1;
 }
 
 static int jpc_ppm_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	jpc_ppm_t *ppm = &ms->parms.ppm;
-	if (jas_stream_write(out, (char *) ppm->data, ppm->len) != ppm->len) {
-		return -1;
-	}
-	return 0;
+    jpc_ppm_t *ppm = &ms->parms.ppm;
+    if (jas_stream_write(out, (char *) ppm->data, ppm->len) != ppm->len) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_ppm_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	jpc_ppm_t *ppm = &ms->parms.ppm;
-	fprintf(out, "ind=%d; len = %d;\n", ppm->ind, ppm->len);
-	if (ppm->len > 0) {
-		fprintf(out, "data =\n");
-		jas_memdump(out, ppm->data, ppm->len);
-	}
-	return 0;
+    jpc_ppm_t *ppm = &ms->parms.ppm;
+    fprintf(out, "ind=%d; len = %d;\n", ppm->ind, (int)ppm->len);
+    if (ppm->len > 0) {
+        fprintf(out, "data =\n");
+        jas_memdump(out, ppm->data, ppm->len);
+    }
+    return 0;
 }
 
 /******************************************************************************\
@@ -1135,169 +1139,169 @@ static int jpc_ppm_dumpparms(jpc_ms_t *ms, FILE *out)
 
 static void jpc_ppt_destroyparms(jpc_ms_t *ms)
 {
-	jpc_ppt_t *ppt = &ms->parms.ppt;
-	if (ppt->data) {
-		jas_free(ppt->data);
-	}
+    jpc_ppt_t *ppt = &ms->parms.ppt;
+    if (ppt->data) {
+        jas_free(ppt->data);
+    }
 }
 
 static int jpc_ppt_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_ppt_t *ppt = &ms->parms.ppt;
-	ppt->data = 0;
-
-	if (ms->len < 1) {
-		goto error;
-	}
-	if (jpc_getuint8(in, &ppt->ind)) {
-		goto error;
-	}
-	ppt->len = ms->len - 1;
-	if (ppt->len > 0) {
-		if (!(ppt->data = jas_malloc(ppt->len * sizeof(unsigned char)))) {
-			goto error;
-		}
-		if (jas_stream_read(in, (char *) ppt->data, ppt->len) != ppt->len) {
-			goto error;
-		}
-	} else {
-		ppt->data = 0;
-	}
-	return 0;
+    jpc_ppt_t *ppt = &ms->parms.ppt;
+    ppt->data = 0;
+
+    if (ms->len < 1) {
+        goto error;
+    }
+    if (jpc_getuint8(in, &ppt->ind)) {
+        goto error;
+    }
+    ppt->len = ms->len - 1;
+    if (ppt->len > 0) {
+        if (!(ppt->data = jas_malloc(ppt->len * sizeof(unsigned char)))) {
+            goto error;
+        }
+        if (jas_stream_read(in, (char *) ppt->data, ppt->len) != ppt->len) {
+            goto error;
+        }
+    } else {
+        ppt->data = 0;
+    }
+    return 0;
 
 error:
-	jpc_ppt_destroyparms(ms);
-	return -1;
+    jpc_ppt_destroyparms(ms);
+    return -1;
 }
 
 static int jpc_ppt_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	jpc_ppt_t *ppt = &ms->parms.ppt;
-	if (jpc_putuint8(out, ppt->ind)) {
-		return -1;
-	}
-	if (jas_stream_write(out, (char *) ppt->data, ppt->len) != ppt->len) {
-		return -1;
-	}
-	return 0;
+    jpc_ppt_t *ppt = &ms->parms.ppt;
+    if (jpc_putuint8(out, ppt->ind)) {
+        return -1;
+    }
+    if (jas_stream_write(out, (char *) ppt->data, ppt->len) != ppt->len) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_ppt_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	jpc_ppt_t *ppt = &ms->parms.ppt;
-	fprintf(out, "ind=%d; len = %d;\n", ppt->ind, ppt->len);
-	if (ppt->len > 0) {
-		fprintf(out, "data =\n");
-		jas_memdump(out, ppt->data, ppt->len);
-	}
-	return 0;
+    jpc_ppt_t *ppt = &ms->parms.ppt;
+    fprintf(out, "ind=%d; len = %d;\n", ppt->ind, (int)ppt->len);
+    if (ppt->len > 0) {
+        fprintf(out, "data =\n");
+        jas_memdump(out, ppt->data, ppt->len);
+    }
+    return 0;
 }
 
-/******************************************************************************\
+/*****************************************************************************\
 * POC marker segment operations.
-\******************************************************************************/
+\*****************************************************************************/
 
 static void jpc_poc_destroyparms(jpc_ms_t *ms)
 {
-	jpc_poc_t *poc = &ms->parms.poc;
-	if (poc->pchgs) {
-		jas_free(poc->pchgs);
-	}
+    jpc_poc_t *poc = &ms->parms.poc;
+    if (poc->pchgs) {
+        jas_free(poc->pchgs);
+    }
 }
 
 static int jpc_poc_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_poc_t *poc = &ms->parms.poc;
-	jpc_pocpchg_t *pchg;
-	int pchgno;
-	uint_fast8_t tmp;
-	poc->numpchgs = (cstate->numcomps > 256) ? (ms->len / 9) :
-	  (ms->len / 7);
-	if (!(poc->pchgs = jas_malloc(poc->numpchgs * sizeof(jpc_pocpchg_t)))) {
-		goto error;
-	}
-	for (pchgno = 0, pchg = poc->pchgs; pchgno < poc->numpchgs; ++pchgno,
-	  ++pchg) {
-		if (jpc_getuint8(in, &pchg->rlvlnostart)) {
-			goto error;
-		}
-		if (cstate->numcomps > 256) {
-			if (jpc_getuint16(in, &pchg->compnostart)) {
-				goto error;
-			}
-		} else {
-			if (jpc_getuint8(in, &tmp)) {
-				goto error;
-			};
-			pchg->compnostart = tmp;
-		}
-		if (jpc_getuint16(in, &pchg->lyrnoend) ||
-		  jpc_getuint8(in, &pchg->rlvlnoend)) {
-			goto error;
-		}
-		if (cstate->numcomps > 256) {
-			if (jpc_getuint16(in, &pchg->compnoend)) {
-				goto error;
-			}
-		} else {
-			if (jpc_getuint8(in, &tmp)) {
-				goto error;
-			}
-			pchg->compnoend = tmp;
-		}
-		if (jpc_getuint8(in, &pchg->prgord)) {
-			goto error;
-		}
-		if (pchg->rlvlnostart > pchg->rlvlnoend ||
-		  pchg->compnostart > pchg->compnoend) {
-			goto error;
-		}
-	}
-	return 0;
+    jpc_poc_t *poc = &ms->parms.poc;
+    jpc_pocpchg_t *pchg;
+    int pchgno;
+    uint_fast8_t tmp;
+    poc->numpchgs = (cstate->numcomps > 256) ? (ms->len / 9) :
+      (ms->len / 7);
+    if (!(poc->pchgs = jas_malloc(poc->numpchgs * sizeof(jpc_pocpchg_t)))) {
+        goto error;
+    }
+    for (pchgno = 0, pchg = poc->pchgs; pchgno < poc->numpchgs; ++pchgno,
+      ++pchg) {
+        if (jpc_getuint8(in, &pchg->rlvlnostart)) {
+            goto error;
+        }
+        if (cstate->numcomps > 256) {
+            if (jpc_getuint16(in, &pchg->compnostart)) {
+                goto error;
+            }
+        } else {
+            if (jpc_getuint8(in, &tmp)) {
+                goto error;
+            };
+            pchg->compnostart = tmp;
+        }
+        if (jpc_getuint16(in, &pchg->lyrnoend) ||
+          jpc_getuint8(in, &pchg->rlvlnoend)) {
+            goto error;
+        }
+        if (cstate->numcomps > 256) {
+            if (jpc_getuint16(in, &pchg->compnoend)) {
+                goto error;
+            }
+        } else {
+            if (jpc_getuint8(in, &tmp)) {
+                goto error;
+            }
+            pchg->compnoend = tmp;
+        }
+        if (jpc_getuint8(in, &pchg->prgord)) {
+            goto error;
+        }
+        if (pchg->rlvlnostart > pchg->rlvlnoend ||
+          pchg->compnostart > pchg->compnoend) {
+            goto error;
+        }
+    }
+    return 0;
 
 error:
-	jpc_poc_destroyparms(ms);
-	return -1;
+    jpc_poc_destroyparms(ms);
+    return -1;
 }
 
 static int jpc_poc_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	jpc_poc_t *poc = &ms->parms.poc;
-	jpc_pocpchg_t *pchg;
-	int pchgno;
-	for (pchgno = 0, pchg = poc->pchgs; pchgno < poc->numpchgs; ++pchgno,
-	  ++pchg) {
-		if (jpc_putuint8(out, pchg->rlvlnostart) ||
-		  ((cstate->numcomps > 256) ?
-		  jpc_putuint16(out, pchg->compnostart) :
-		  jpc_putuint8(out, pchg->compnostart)) ||
-		  jpc_putuint16(out, pchg->lyrnoend) ||
-		  jpc_putuint8(out, pchg->rlvlnoend) ||
-		  ((cstate->numcomps > 256) ?
-		  jpc_putuint16(out, pchg->compnoend) :
-		  jpc_putuint8(out, pchg->compnoend)) ||
-		  jpc_putuint8(out, pchg->prgord)) {
-			return -1;
-		}
-	}
-	return 0;
+    jpc_poc_t *poc = &ms->parms.poc;
+    jpc_pocpchg_t *pchg;
+    int pchgno;
+    for (pchgno = 0, pchg = poc->pchgs; pchgno < poc->numpchgs; ++pchgno,
+      ++pchg) {
+        if (jpc_putuint8(out, pchg->rlvlnostart) ||
+          ((cstate->numcomps > 256) ?
+          jpc_putuint16(out, pchg->compnostart) :
+          jpc_putuint8(out, pchg->compnostart)) ||
+          jpc_putuint16(out, pchg->lyrnoend) ||
+          jpc_putuint8(out, pchg->rlvlnoend) ||
+          ((cstate->numcomps > 256) ?
+          jpc_putuint16(out, pchg->compnoend) :
+          jpc_putuint8(out, pchg->compnoend)) ||
+          jpc_putuint8(out, pchg->prgord)) {
+            return -1;
+        }
+    }
+    return 0;
 }
 
 static int jpc_poc_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	jpc_poc_t *poc = &ms->parms.poc;
-	jpc_pocpchg_t *pchg;
-	int pchgno;
-	for (pchgno = 0, pchg = poc->pchgs; pchgno < poc->numpchgs;
-	  ++pchgno, ++pchg) {
-		fprintf(out, "po[%d] = %d; ", pchgno, pchg->prgord);
-		fprintf(out, "cs[%d] = %d; ce[%d] = %d; ",
-		  pchgno, pchg->compnostart, pchgno, pchg->compnoend);
-		fprintf(out, "rs[%d] = %d; re[%d] = %d; ",
-		  pchgno, pchg->rlvlnostart, pchgno, pchg->rlvlnoend);
-		fprintf(out, "le[%d] = %d\n", pchgno, pchg->lyrnoend);
-	}
-	return 0;
+    jpc_poc_t *poc = &ms->parms.poc;
+    jpc_pocpchg_t *pchg;
+    int pchgno;
+    for (pchgno = 0, pchg = poc->pchgs; pchgno < poc->numpchgs;
+      ++pchgno, ++pchg) {
+        fprintf(out, "po[%d] = %d; ", pchgno, pchg->prgord);
+        fprintf(out, "cs[%d] = %d; ce[%d] = %d; ",
+                pchgno, (int)pchg->compnostart, pchgno, (int)pchg->compnoend);
+        fprintf(out, "rs[%d] = %d; re[%d] = %d; ",
+                pchgno, pchg->rlvlnostart, pchgno, pchg->rlvlnoend);
+        fprintf(out, "le[%d] = %d\n", pchgno, (int)pchg->lyrnoend);
+    }
+    return 0;
 }
 
 /******************************************************************************\
@@ -1306,58 +1310,58 @@ static int jpc_poc_dumpparms(jpc_ms_t *ms, FILE *out)
 
 static void jpc_crg_destroyparms(jpc_ms_t *ms)
 {
-	jpc_crg_t *crg = &ms->parms.crg;
-	if (crg->comps) {
-		jas_free(crg->comps);
-	}
+    jpc_crg_t *crg = &ms->parms.crg;
+    if (crg->comps) {
+        jas_free(crg->comps);
+    }
 }
 
 static int jpc_crg_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_crg_t *crg = &ms->parms.crg;
-	jpc_crgcomp_t *comp;
-	uint_fast16_t compno;
-	crg->numcomps = cstate->numcomps;
-	if (!(crg->comps = jas_malloc(cstate->numcomps * sizeof(uint_fast16_t)))) {
-		return -1;
-	}
-	for (compno = 0, comp = crg->comps; compno < cstate->numcomps;
-	  ++compno, ++comp) {
-		if (jpc_getuint16(in, &comp->hoff) ||
-		  jpc_getuint16(in, &comp->voff)) {
-			jpc_crg_destroyparms(ms);
-			return -1;
-		}
-	}
-	return 0;
+    jpc_crg_t *crg = &ms->parms.crg;
+    jpc_crgcomp_t *comp;
+    uint_fast16_t compno;
+    crg->numcomps = cstate->numcomps;
+    if (!(crg->comps = jas_malloc(cstate->numcomps * sizeof(uint_fast16_t)))) {
+        return -1;
+    }
+    for (compno = 0, comp = crg->comps; compno < cstate->numcomps;
+      ++compno, ++comp) {
+        if (jpc_getuint16(in, &comp->hoff) ||
+          jpc_getuint16(in, &comp->voff)) {
+            jpc_crg_destroyparms(ms);
+            return -1;
+        }
+    }
+    return 0;
 }
 
 static int jpc_crg_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	jpc_crg_t *crg = &ms->parms.crg;
-	int compno;
-	jpc_crgcomp_t *comp;
-	for (compno = 0, comp = crg->comps; compno < crg->numcomps; ++compno,
-	  ++comp) {
-		if (jpc_putuint16(out, comp->hoff) ||
-		  jpc_putuint16(out, comp->voff)) {
-			return -1;
-		}
-	}
-	return 0;
+    jpc_crg_t *crg = &ms->parms.crg;
+    int compno;
+    jpc_crgcomp_t *comp;
+    for (compno = 0, comp = crg->comps; compno < crg->numcomps; ++compno,
+      ++comp) {
+        if (jpc_putuint16(out, comp->hoff) ||
+          jpc_putuint16(out, comp->voff)) {
+            return -1;
+        }
+    }
+    return 0;
 }
 
 static int jpc_crg_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	jpc_crg_t *crg = &ms->parms.crg;
-	int compno;
-	jpc_crgcomp_t *comp;
-	for (compno = 0, comp = crg->comps; compno < crg->numcomps; ++compno,
-	  ++comp) {
-		fprintf(out, "hoff[%d] = %d; voff[%d] = %d\n", compno,
-		  comp->hoff, compno, comp->voff);
-	}
-	return 0;
+    jpc_crg_t *crg = &ms->parms.crg;
+    int compno;
+    jpc_crgcomp_t *comp;
+    for (compno = 0, comp = crg->comps; compno < crg->numcomps; ++compno,
+      ++comp) {
+        fprintf(out, "hoff[%d] = %d; voff[%d] = %d\n",
+                compno, (int)comp->hoff, compno, (int)comp->voff);
+    }
+    return 0;
 }
 
 /******************************************************************************\
@@ -1366,112 +1370,108 @@ static int jpc_crg_dumpparms(jpc_ms_t *ms, FILE *out)
 
 static void jpc_com_destroyparms(jpc_ms_t *ms)
 {
-	jpc_com_t *com = &ms->parms.com;
-	if (com->data) {
-		jas_free(com->data);
-	}
+    jpc_com_t *com = &ms->parms.com;
+    if (com->data) {
+        jas_free(com->data);
+    }
 }
 
 static int jpc_com_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_com_t *com = &ms->parms.com;
-	if (jpc_getuint16(in, &com->regid)) {
-		return -1;
-	}
-	com->len = ms->len - 2;
-	if (com->len > 0) {
-		if (!(com->data = jas_malloc(com->len))) {
-			return -1;
-		}
-		if (jas_stream_read(in, com->data, com->len) != com->len) {
-			return -1;
-		}
-	} else {
-		com->data = 0;
-	}
-	return 0;
+    jpc_com_t *com = &ms->parms.com;
+    if (jpc_getuint16(in, &com->regid)) {
+        return -1;
+    }
+    com->len = ms->len - 2;
+    if (com->len > 0) {
+        if (!(com->data = jas_malloc(com->len))) {
+            return -1;
+        }
+        if (jas_stream_read(in, com->data, com->len) != com->len) {
+            return -1;
+        }
+    } else {
+        com->data = 0;
+    }
+    return 0;
 }
 
 static int jpc_com_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	jpc_com_t *com = &ms->parms.com;
-	if (jpc_putuint16(out, com->regid)) {
-		return -1;
-	}
-	if (jas_stream_write(out, com->data, com->len) != com->len) {
-		return -1;
-	}
-	return 0;
+    jpc_com_t *com = &ms->parms.com;
+    if (jpc_putuint16(out, com->regid)) {
+        return -1;
+    }
+    if (jas_stream_write(out, com->data, com->len) != com->len) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_com_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	jpc_com_t *com = &ms->parms.com;
-	int i;
-	int printable;
-	fprintf(out, "regid = %d;\n", com->regid);
-	printable = 1;
-	for (i = 0; i < com->len; ++i) {
-		if (!isprint(com->data[i])) {
-			printable = 0;
-			break;
-		}
-	}
-	if (printable) {
-		fprintf(out, "data = ");
-		fwrite(com->data, sizeof(char), com->len, out);
-		fprintf(out, "\n");
-	}
-	return 0;
-}
-
-/******************************************************************************\
+    jpc_com_t *com = &ms->parms.com;
+    int i;
+    int printable;
+    fprintf(out, "regid = %d;\n", (int)com->regid);
+    printable = 1;
+    for (i = 0, printable = 1; i < com->len && printable; ++i) {
+        if (!isprint(com->data[i]))
+            printable = 0;
+    }
+    if (printable) {
+        fprintf(out, "data = ");
+        fwrite(com->data, sizeof(char), com->len, out);
+        fprintf(out, "\n");
+    }
+    return 0;
+}
+
+/*****************************************************************************\
 * Operations for unknown types of marker segments.
-\******************************************************************************/
+\*****************************************************************************/
 
 static void jpc_unk_destroyparms(jpc_ms_t *ms)
 {
-	jpc_unk_t *unk = &ms->parms.unk;
-	if (unk->data) {
-		jas_free(unk->data);
-	}
+    jpc_unk_t *unk = &ms->parms.unk;
+    if (unk->data) {
+        jas_free(unk->data);
+    }
 }
 
 static int jpc_unk_getparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *in)
 {
-	jpc_unk_t *unk = &ms->parms.unk;
+    jpc_unk_t *unk = &ms->parms.unk;
 
-	if (ms->len > 0) {
-		if (!(unk->data = jas_malloc(ms->len * sizeof(unsigned char)))) {
-			return -1;
-		}
-		if (jas_stream_read(in, (char *) unk->data, ms->len) != ms->len) {
-			jas_free(unk->data);
-			return -1;
-		}
-		unk->len = ms->len;
-	} else {
-		unk->data = 0;
-		unk->len = 0;
-	}
-	return 0;
+    if (ms->len > 0) {
+        if (!(unk->data = jas_malloc(ms->len * sizeof(unsigned char)))) {
+            return -1;
+        }
+        if (jas_stream_read(in, (char *) unk->data, ms->len) != ms->len) {
+            jas_free(unk->data);
+            return -1;
+        }
+        unk->len = ms->len;
+    } else {
+        unk->data = 0;
+        unk->len = 0;
+    }
+    return 0;
 }
 
 static int jpc_unk_putparms(jpc_ms_t *ms, jpc_cstate_t *cstate, jas_stream_t *out)
 {
-	/* If this function is called, we are trying to write an unsupported
-	  type of marker segment.  Return with an error indication.  */
-	return -1;
+    return -1;
 }
 
 static int jpc_unk_dumpparms(jpc_ms_t *ms, FILE *out)
 {
-	int i;
-	jpc_unk_t *unk = &ms->parms.unk;
-	for (i = 0; i < unk->len; ++i) {
-		fprintf(out, "%02x ", unk->data[i]);
-	}
-	return 0;
+    int i;
+    jpc_unk_t *unk = &ms->parms.unk;
+    for (i = 0; i < unk->len; ++i) {
+        fprintf(out, "%02x ", unk->data[i]);
+    }
+    return 0;
 }
 
 /******************************************************************************\
@@ -1480,86 +1480,86 @@ static int jpc_unk_dumpparms(jpc_ms_t *ms, FILE *out)
 
 int jpc_getuint8(jas_stream_t *in, uint_fast8_t *val)
 {
-	int c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	if (val) {
-		*val = c;
-	}
-	return 0;
+    int c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    if (val) {
+        *val = c;
+    }
+    return 0;
 }
 
 int jpc_putuint8(jas_stream_t *out, uint_fast8_t val)
 {
-	if (jas_stream_putc(out, val & 0xff) == EOF) {
-		return -1;
-	}
-	return 0;
+    if (jas_stream_putc(out, val & 0xff) == EOF) {
+        return -1;
+    }
+    return 0;
 }
 
 int jpc_getuint16(jas_stream_t *in, uint_fast16_t *val)
 {
-	uint_fast16_t v;
-	int c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	v = c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	v = (v << 8) | c;
-	if (val) {
-		*val = v;
-	}
-	return 0;
+    uint_fast16_t v;
+    int c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    v = c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    v = (v << 8) | c;
+    if (val) {
+        *val = v;
+    }
+    return 0;
 }
 
 int jpc_putuint16(jas_stream_t *out, uint_fast16_t val)
 {
-	if (jas_stream_putc(out, (val >> 8) & 0xff) == EOF ||
-	  jas_stream_putc(out, val & 0xff) == EOF) {
-		return -1;
-	}
-	return 0;
+    if (jas_stream_putc(out, (val >> 8) & 0xff) == EOF ||
+      jas_stream_putc(out, val & 0xff) == EOF) {
+        return -1;
+    }
+    return 0;
 }
 
 int jpc_getuint32(jas_stream_t *in, uint_fast32_t *val)
 {
-	uint_fast32_t v;
-	int c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	v = c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	v = (v << 8) | c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	v = (v << 8) | c;
-	if ((c = jas_stream_getc(in)) == EOF) {
-		return -1;
-	}
-	v = (v << 8) | c;
-	if (val) {
-		*val = v;
-	}
-	return 0;
+    uint_fast32_t v;
+    int c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    v = c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    v = (v << 8) | c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    v = (v << 8) | c;
+    if ((c = jas_stream_getc(in)) == EOF) {
+        return -1;
+    }
+    v = (v << 8) | c;
+    if (val) {
+        *val = v;
+    }
+    return 0;
 }
 
 int jpc_putuint32(jas_stream_t *out, uint_fast32_t val)
 {
-	if (jas_stream_putc(out, (val >> 24) & 0xff) == EOF ||
-	  jas_stream_putc(out, (val >> 16) & 0xff) == EOF ||
-	  jas_stream_putc(out, (val >> 8) & 0xff) == EOF ||
-	  jas_stream_putc(out, val & 0xff) == EOF) {
-		return -1;
-	}
-	return 0;
+    if (jas_stream_putc(out, (val >> 24) & 0xff) == EOF ||
+      jas_stream_putc(out, (val >> 16) & 0xff) == EOF ||
+      jas_stream_putc(out, (val >> 8) & 0xff) == EOF ||
+      jas_stream_putc(out, val & 0xff) == EOF) {
+        return -1;
+    }
+    return 0;
 }
 
 /******************************************************************************\
@@ -1568,47 +1568,47 @@ int jpc_putuint32(jas_stream_t *out, uint_fast32_t val)
 
 static jpc_mstabent_t *jpc_mstab_lookup(int id)
 {
-	jpc_mstabent_t *mstabent;
-	for (mstabent = jpc_mstab;; ++mstabent) {
-		if (mstabent->id == id || mstabent->id < 0) {
-			return mstabent;
-		}
-	}
-	assert(0);
-	return 0;
+    jpc_mstabent_t *mstabent;
+    for (mstabent = jpc_mstab;; ++mstabent) {
+        if (mstabent->id == id || mstabent->id < 0) {
+            return mstabent;
+        }
+    }
+    assert(0);
+    return 0;
 }
 
 int jpc_validate(jas_stream_t *in)
 {
-	int n;
-	int i;
-	unsigned char buf[2];
-
-	assert(JAS_STREAM_MAXPUTBACK >= 2);
-
-	if ((n = jas_stream_read(in, (char *) buf, 2)) < 0) {
-		return -1;
-	}
-	for (i = n - 1; i >= 0; --i) {
-		if (jas_stream_ungetc(in, buf[i]) == EOF) {
-			return -1;
-		}
-	}
-	if (n < 2) {
-		return -1;
-	}
-	if (buf[0] == (JPC_MS_SOC >> 8) && buf[1] == (JPC_MS_SOC & 0xff)) {
-		return 0;
-	}
-	return -1;
+    int n;
+    int i;
+    unsigned char buf[2];
+
+    assert(JAS_STREAM_MAXPUTBACK >= 2);
+
+    if ((n = jas_stream_read(in, (char *) buf, 2)) < 0) {
+        return -1;
+    }
+    for (i = n - 1; i >= 0; --i) {
+        if (jas_stream_ungetc(in, buf[i]) == EOF) {
+            return -1;
+        }
+    }
+    if (n < 2) {
+        return -1;
+    }
+    if (buf[0] == (JPC_MS_SOC >> 8) && buf[1] == (JPC_MS_SOC & 0xff)) {
+        return 0;
+    }
+    return -1;
 }
 
 int jpc_getdata(jas_stream_t *in, jas_stream_t *out, long len)
 {
-	return jas_stream_copy(out, in, len);
+    return jas_stream_copy(out, in, len);
 }
 
 int jpc_putdata(jas_stream_t *out, jas_stream_t *in, long len)
 {
-	return jas_stream_copy(out, in, len);
+    return jas_stream_copy(out, in, len);
 }
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_cs.h b/converter/other/jpeg2000/libjasper/jpc/jpc_cs.h
index 07a046d1..4bff677c 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_cs.h
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_cs.h
@@ -188,7 +188,7 @@ typedef struct {
 	/* The tile number. */
 	uint_fast16_t tileno;
 
-	/* The combined length of the marker segment and its auxilary data
+	/* The combined length of the marker segment and its auxiliary data
 	  (i.e., packet data). */
 	uint_fast32_t len;
 
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_dec.c b/converter/other/jpeg2000/libjasper/jpc/jpc_dec.c
index 42980225..72bd0126 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_dec.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_dec.c
@@ -114,14 +114,16 @@
  * $Id$
  */
 
-/******************************************************************************\
+/*****************************************************************************\
 * Includes.
-\******************************************************************************/
+\*****************************************************************************/
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
 
+#include "pm.h"
+
 #include "jasper/jas_types.h"
 #include "jasper/jas_math.h"
 #include "jasper/jas_tvp.h"
@@ -136,34 +138,34 @@
 #include "jpc_t1dec.h"
 #include "jpc_math.h"
 
-/******************************************************************************\
+/*****************************************************************************\
 *
-\******************************************************************************/
+\*****************************************************************************/
 
-#define	JPC_MHSOC	0x0001
+#define JPC_MHSOC   0x0001
   /* In the main header, expecting a SOC marker segment. */
-#define	JPC_MHSIZ	0x0002
+#define JPC_MHSIZ   0x0002
   /* In the main header, expecting a SIZ marker segment. */
-#define	JPC_MH		0x0004
+#define JPC_MH      0x0004
   /* In the main header, expecting "other" marker segments. */
-#define	JPC_TPHSOT	0x0008
+#define JPC_TPHSOT  0x0008
   /* In a tile-part header, expecting a SOT marker segment. */
-#define	JPC_TPH		0x0010
+#define JPC_TPH     0x0010
   /* In a tile-part header, expecting "other" marker segments. */
-#define	JPC_MT		0x0020
+#define JPC_MT      0x0020
   /* In the main trailer. */
 
 typedef struct {
 
-	uint_fast16_t id;
-	/* The marker segment type. */
+    uint_fast16_t id;
+    /* The marker segment type. */
 
-	int validstates;
-	/* The states in which this type of marker segment can be
-	  validly encountered. */
+    int validstates;
+    /* The states in which this type of marker segment can be
+      validly encountered. */
 
-	int (*action)(jpc_dec_t *dec, jpc_ms_t *ms);
-	/* The action to take upon encountering this type of marker segment. */
+    int (*action)(jpc_dec_t *dec, jpc_ms_t *ms);
+    /* The action to take upon encountering this type of marker segment. */
 
 } jpc_dec_mstabent_t;
 
@@ -172,13 +174,13 @@ typedef struct {
 \******************************************************************************/
 
 /* COD/COC parameters have been specified. */
-#define	JPC_CSET	0x0001
+#define JPC_CSET    0x0001
 /* QCD/QCC parameters have been specified. */
-#define	JPC_QSET	0x0002
+#define JPC_QSET    0x0002
 /* COD/COC parameters set from a COC marker segment. */
-#define	JPC_COC	0x0004
+#define JPC_COC 0x0004
 /* QCD/QCC parameters set from a QCC marker segment. */
-#define	JPC_QCC	0x0008
+#define JPC_QCC 0x0008
 
 /******************************************************************************\
 * Local function prototypes.
@@ -253,134 +255,134 @@ static int jpc_dec_parseopts(char *optstr, jpc_dec_importopts_t *opts);
 \******************************************************************************/
 
 jpc_dec_mstabent_t jpc_dec_mstab[] = {
-	{JPC_MS_SOC, JPC_MHSOC, jpc_dec_process_soc},
-	{JPC_MS_SOT, JPC_MH | JPC_TPHSOT, jpc_dec_process_sot},
-	{JPC_MS_SOD, JPC_TPH, jpc_dec_process_sod},
-	{JPC_MS_EOC, JPC_TPHSOT, jpc_dec_process_eoc},
-	{JPC_MS_SIZ, JPC_MHSIZ, jpc_dec_process_siz},
-	{JPC_MS_COD, JPC_MH | JPC_TPH, jpc_dec_process_cod},
-	{JPC_MS_COC, JPC_MH | JPC_TPH, jpc_dec_process_coc},
-	{JPC_MS_RGN, JPC_MH | JPC_TPH, jpc_dec_process_rgn},
-	{JPC_MS_QCD, JPC_MH | JPC_TPH, jpc_dec_process_qcd},
-	{JPC_MS_QCC, JPC_MH | JPC_TPH, jpc_dec_process_qcc},
-	{JPC_MS_POC, JPC_MH | JPC_TPH, jpc_dec_process_poc},
-	{JPC_MS_TLM, JPC_MH, 0},
-	{JPC_MS_PLM, JPC_MH, 0},
-	{JPC_MS_PLT, JPC_TPH, 0},
-	{JPC_MS_PPM, JPC_MH, jpc_dec_process_ppm},
-	{JPC_MS_PPT, JPC_TPH, jpc_dec_process_ppt},
-	{JPC_MS_SOP, 0, 0},
-	{JPC_MS_CRG, JPC_MH, jpc_dec_process_crg},
-	{JPC_MS_COM, JPC_MH | JPC_TPH, jpc_dec_process_com},
-	{0, JPC_MH | JPC_TPH, jpc_dec_process_unk}
+    {JPC_MS_SOC, JPC_MHSOC, jpc_dec_process_soc},
+    {JPC_MS_SOT, JPC_MH | JPC_TPHSOT, jpc_dec_process_sot},
+    {JPC_MS_SOD, JPC_TPH, jpc_dec_process_sod},
+    {JPC_MS_EOC, JPC_TPHSOT, jpc_dec_process_eoc},
+    {JPC_MS_SIZ, JPC_MHSIZ, jpc_dec_process_siz},
+    {JPC_MS_COD, JPC_MH | JPC_TPH, jpc_dec_process_cod},
+    {JPC_MS_COC, JPC_MH | JPC_TPH, jpc_dec_process_coc},
+    {JPC_MS_RGN, JPC_MH | JPC_TPH, jpc_dec_process_rgn},
+    {JPC_MS_QCD, JPC_MH | JPC_TPH, jpc_dec_process_qcd},
+    {JPC_MS_QCC, JPC_MH | JPC_TPH, jpc_dec_process_qcc},
+    {JPC_MS_POC, JPC_MH | JPC_TPH, jpc_dec_process_poc},
+    {JPC_MS_TLM, JPC_MH, 0},
+    {JPC_MS_PLM, JPC_MH, 0},
+    {JPC_MS_PLT, JPC_TPH, 0},
+    {JPC_MS_PPM, JPC_MH, jpc_dec_process_ppm},
+    {JPC_MS_PPT, JPC_TPH, jpc_dec_process_ppt},
+    {JPC_MS_SOP, 0, 0},
+    {JPC_MS_CRG, JPC_MH, jpc_dec_process_crg},
+    {JPC_MS_COM, JPC_MH | JPC_TPH, jpc_dec_process_com},
+    {0, JPC_MH | JPC_TPH, jpc_dec_process_unk}
 };
 
-/******************************************************************************\
+/*****************************************************************************\
 * The main entry point for the JPEG-2000 decoder.
-\******************************************************************************/
+\*****************************************************************************/
 
 jas_image_t *jpc_decode(jas_stream_t *in, char *optstr)
 {
-	jpc_dec_importopts_t opts;
-	jpc_dec_t *dec;
-	jas_image_t *image;
+    jpc_dec_importopts_t opts;
+    jpc_dec_t *dec;
+    jas_image_t *image;
 
-	dec = 0;
+    dec = 0;
 
-	if (jpc_dec_parseopts(optstr, &opts)) {
-		goto error;
-	}
+    if (jpc_dec_parseopts(optstr, &opts)) {
+        goto error;
+    }
 
-	jpc_initluts();
+    jpc_initluts();
 
-	if (!(dec = jpc_dec_create(&opts, in))) {
-		goto error;
-	}
+    if (!(dec = jpc_dec_create(&opts, in))) {
+        goto error;
+    }
 
-	/* Do most of the work. */
-	if (jpc_dec_decode(dec)) {
-		goto error;
-	}
+    /* Do most of the work. */
+    if (jpc_dec_decode(dec)) {
+        goto error;
+    }
 
-	if (jas_image_numcmpts(dec->image) >= 3) {
-		jas_image_setcolorspace(dec->image, JAS_IMAGE_CS_RGB);
-		jas_image_setcmpttype(dec->image, 0,
-		  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_R));
-		jas_image_setcmpttype(dec->image, 1,
-		  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_G));
-		jas_image_setcmpttype(dec->image, 2,
-		  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_B));
-	} else {
-		jas_image_setcolorspace(dec->image, JAS_IMAGE_CS_GRAY);
-		jas_image_setcmpttype(dec->image, 0,
-		  JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y));
-	}
+    if (jas_image_numcmpts(dec->image) >= 3) {
+        jas_image_setcolorspace(dec->image, JAS_IMAGE_CS_RGB);
+        jas_image_setcmpttype(dec->image, 0,
+          JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_R));
+        jas_image_setcmpttype(dec->image, 1,
+          JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_G));
+        jas_image_setcmpttype(dec->image, 2,
+          JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_B));
+    } else {
+        jas_image_setcolorspace(dec->image, JAS_IMAGE_CS_GRAY);
+        jas_image_setcmpttype(dec->image, 0,
+          JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y));
+    }
 
-	/* Save the return value. */
-	image = dec->image;
+    /* Save the return value. */
+    image = dec->image;
 
-	/* Stop the image from being discarded. */
-	dec->image = 0;
+    /* Stop the image from being discarded. */
+    dec->image = 0;
 
-	/* Destroy decoder. */
-	jpc_dec_destroy(dec);
+    /* Destroy decoder. */
+    jpc_dec_destroy(dec);
 
-	return image;
+    return image;
 
 error:
-	if (dec) {
-		jpc_dec_destroy(dec);
-	}
-	return 0;
+    if (dec) {
+        jpc_dec_destroy(dec);
+    }
+    return 0;
 }
 
 typedef enum {
-	OPT_MAXLYRS,
-	OPT_MAXPKTS,
-	OPT_DEBUG
+    OPT_MAXLYRS,
+    OPT_MAXPKTS,
+    OPT_DEBUG
 } optid_t;
 
 jas_taginfo_t decopts[] = {
-	{OPT_MAXLYRS, "maxlyrs"},
-	{OPT_MAXPKTS, "maxpkts"},
-	{OPT_DEBUG, "debug"},
-	{-1, 0}
+    {OPT_MAXLYRS, "maxlyrs"},
+    {OPT_MAXPKTS, "maxpkts"},
+    {OPT_DEBUG, "debug"},
+    {-1, 0}
 };
 
 static int jpc_dec_parseopts(char *optstr, jpc_dec_importopts_t *opts)
 {
-	jas_tvparser_t *tvp;
+    jas_tvparser_t *tvp;
 
-	opts->debug = 0;
-	opts->maxlyrs = JPC_MAXLYRS;
-	opts->maxpkts = -1;
+    opts->debug = 0;
+    opts->maxlyrs = JPC_MAXLYRS;
+    opts->maxpkts = -1;
 
-	if (!(tvp = jas_tvparser_create(optstr ? optstr : ""))) {
-		return -1;
-	}
+    if (!(tvp = jas_tvparser_create(optstr ? optstr : ""))) {
+        return -1;
+    }
 
-	while (!jas_tvparser_next(tvp)) {
-		switch (jas_taginfo_nonull(jas_taginfos_lookup(decopts,
-		  jas_tvparser_gettag(tvp)))->id) {
-		case OPT_MAXLYRS:
-			opts->maxlyrs = atoi(jas_tvparser_getval(tvp));
-			break;
-		case OPT_DEBUG:
-			opts->debug = atoi(jas_tvparser_getval(tvp));
-			break;
-		case OPT_MAXPKTS:
-			opts->maxpkts = atoi(jas_tvparser_getval(tvp));
-			break;
-		default:
-			fprintf(stderr, "warning: ignoring invalid option %s\n",
-			  jas_tvparser_gettag(tvp));
-			break;
-		}
-	}
+    while (!jas_tvparser_next(tvp)) {
+        switch (jas_taginfo_nonull(jas_taginfos_lookup(decopts,
+          jas_tvparser_gettag(tvp)))->id) {
+        case OPT_MAXLYRS:
+            opts->maxlyrs = atoi(jas_tvparser_getval(tvp));
+            break;
+        case OPT_DEBUG:
+            opts->debug = atoi(jas_tvparser_getval(tvp));
+            break;
+        case OPT_MAXPKTS:
+            opts->maxpkts = atoi(jas_tvparser_getval(tvp));
+            break;
+        default:
+            fprintf(stderr, "warning: ignoring invalid option %s\n",
+              jas_tvparser_gettag(tvp));
+            break;
+        }
+    }
 
-	jas_tvparser_destroy(tvp);
+    jas_tvparser_destroy(tvp);
 
-	return 0;
+    return 0;
 }
 
 /******************************************************************************\
@@ -389,1121 +391,1121 @@ static int jpc_dec_parseopts(char *optstr, jpc_dec_importopts_t *opts)
 
 static jpc_dec_mstabent_t *jpc_dec_mstab_lookup(uint_fast16_t id)
 {
-	jpc_dec_mstabent_t *mstabent;
-	for (mstabent = jpc_dec_mstab; mstabent->id != 0; ++mstabent) {
-		if (mstabent->id == id) {
-			break;
-		}
-	}
-	return mstabent;
+    jpc_dec_mstabent_t *mstabent;
+    for (mstabent = jpc_dec_mstab; mstabent->id != 0; ++mstabent) {
+        if (mstabent->id == id) {
+            break;
+        }
+    }
+    return mstabent;
 }
 
 static int jpc_dec_decode(jpc_dec_t *dec)
 {
-	jpc_ms_t *ms;
-	jpc_dec_mstabent_t *mstabent;
-	int ret;
-	jpc_cstate_t *cstate;
+    jpc_ms_t *ms;
+    jpc_dec_mstabent_t *mstabent;
+    int ret;
+    jpc_cstate_t *cstate;
 
-	if (!(cstate = jpc_cstate_create())) {
-		return -1;
-	}
-	dec->cstate = cstate;
+    if (!(cstate = jpc_cstate_create())) {
+        return -1;
+    }
+    dec->cstate = cstate;
 
-	/* Initially, we should expect to encounter a SOC marker segment. */
-	dec->state = JPC_MHSOC;
+    /* Initially, we should expect to encounter a SOC marker segment. */
+    dec->state = JPC_MHSOC;
 
-	for (;;) {
+    for (;;) {
 
-		/* Get the next marker segment in the code stream. */
-		if (!(ms = jpc_getms(dec->in, cstate))) {
-			fprintf(stderr, "cannot get marker segment\n");
-			return -1;
-		}
+        /* Get the next marker segment in the code stream. */
+        if (!(ms = jpc_getms(dec->in, cstate))) {
+            fprintf(stderr, "cannot get marker segment\n");
+            return -1;
+        }
 
-		mstabent = jpc_dec_mstab_lookup(ms->id);
-		assert(mstabent);
+        mstabent = jpc_dec_mstab_lookup(ms->id);
+        assert(mstabent);
 
-		/* Ensure that this type of marker segment is permitted
-		  at this point in the code stream. */
-		if (!(dec->state & mstabent->validstates)) {
-			fprintf(stderr, "unexpected marker segment type\n");
-			jpc_ms_destroy(ms);
-			return -1;
-		}
+        /* Ensure that this type of marker segment is permitted
+          at this point in the code stream. */
+        if (!(dec->state & mstabent->validstates)) {
+            fprintf(stderr, "unexpected marker segment type\n");
+            jpc_ms_destroy(ms);
+            return -1;
+        }
 
-		/* Process the marker segment. */
-		if (mstabent->action) {
-			ret = (*mstabent->action)(dec, ms);
-		} else {
-			/* No explicit action is required. */
-			ret = 0;
-		}
+        /* Process the marker segment. */
+        if (mstabent->action) {
+            ret = (*mstabent->action)(dec, ms);
+        } else {
+            /* No explicit action is required. */
+            ret = 0;
+        }
 
-		/* Destroy the marker segment. */
-		jpc_ms_destroy(ms);
+        /* Destroy the marker segment. */
+        jpc_ms_destroy(ms);
 
-		if (ret < 0) {
-			return -1;
-		} else if (ret > 0) {
-			break;
-		}
+        if (ret < 0) {
+            return -1;
+        } else if (ret > 0) {
+            break;
+        }
 
-	}
+    }
 
-	return 0;
+    return 0;
 }
 
 static int jpc_dec_process_crg(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	uint_fast16_t cmptno;
-	jpc_dec_cmpt_t *cmpt;
-	jpc_crg_t *crg;
+    uint_fast16_t cmptno;
+    jpc_dec_cmpt_t *cmpt;
+    jpc_crg_t *crg;
 
-	crg = &ms->parms.crg;
-	for (cmptno = 0, cmpt = dec->cmpts; cmptno < dec->numcomps; ++cmptno,
-	  ++cmpt) {
-		/* Ignore the information in the CRG marker segment for now.
-		  This information serves no useful purpose for decoding anyhow.
-		  Some other parts of the code need to be changed if these lines
-		  are uncommented.
-		cmpt->hsubstep = crg->comps[cmptno].hoff;
-		cmpt->vsubstep = crg->comps[cmptno].voff;
-		*/
-	}
-	return 0;
+    crg = &ms->parms.crg;
+    for (cmptno = 0, cmpt = dec->cmpts; cmptno < dec->numcomps; ++cmptno,
+      ++cmpt) {
+        /* Ignore the information in the CRG marker segment for now.
+          This information serves no useful purpose for decoding anyhow.
+          Some other parts of the code need to be changed if these lines
+          are uncommented.
+        cmpt->hsubstep = crg->comps[cmptno].hoff;
+        cmpt->vsubstep = crg->comps[cmptno].voff;
+        */
+    }
+    return 0;
 }
 
 static int jpc_dec_process_soc(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	/* We should expect to encounter a SIZ marker segment next. */
-	dec->state = JPC_MHSIZ;
+    /* We should expect to encounter a SIZ marker segment next. */
+    dec->state = JPC_MHSIZ;
 
-	return 0;
+    return 0;
 }
 
 static int jpc_dec_process_sot(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	jpc_dec_tile_t *tile;
-	jpc_sot_t *sot = &ms->parms.sot;
-	jas_image_cmptparm_t *compinfos;
-	jas_image_cmptparm_t *compinfo;
-	jpc_dec_cmpt_t *cmpt;
-	uint_fast16_t cmptno;
-
-	if (dec->state == JPC_MH) {
-
-		compinfos = jas_malloc(dec->numcomps * sizeof(jas_image_cmptparm_t));
-		assert(compinfos);
-		for (cmptno = 0, cmpt = dec->cmpts, compinfo = compinfos;
-		  cmptno < dec->numcomps; ++cmptno, ++cmpt, ++compinfo) {
-			compinfo->tlx = 0;
-			compinfo->tly = 0;
-			compinfo->prec = cmpt->prec;
-			compinfo->sgnd = cmpt->sgnd;
-			compinfo->width = cmpt->width;
-			compinfo->height = cmpt->height;
-			compinfo->hstep = cmpt->hstep;
-			compinfo->vstep = cmpt->vstep;
-		}
-
-		if (!(dec->image = jas_image_create(dec->numcomps, compinfos,
-		  JAS_IMAGE_CS_UNKNOWN))) {
-			return -1;
-		}
-		jas_free(compinfos);
-
-		/* Is the packet header information stored in PPM marker segments in
-		  the main header? */
-		if (dec->ppmstab) {
-			/* Convert the PPM marker segment data into a collection of streams
-			  (one stream per tile-part). */
-			if (!(dec->pkthdrstreams = jpc_ppmstabtostreams(dec->ppmstab))) {
-				abort();
-			}
-			jpc_ppxstab_destroy(dec->ppmstab);
-			dec->ppmstab = 0;
-		}
-	}
-
-	if (sot->len > 0) {
-		dec->curtileendoff = jas_stream_getrwcount(dec->in) - ms->len -
-		  4 + sot->len;
-	} else {
-		dec->curtileendoff = 0;
-	}
-
-	if (sot->tileno > dec->numtiles) {
-		fprintf(stderr, "invalid tile number in SOT marker segment\n");
-		return -1;
-	}
-	/* Set the current tile. */
-	dec->curtile = &dec->tiles[sot->tileno];
-	tile = dec->curtile;
-	/* Ensure that this is the expected part number. */
-	if (sot->partno != tile->partno) {
-		return -1;
-	}
-	if (tile->numparts > 0 && sot->partno >= tile->numparts) {
-		return -1;
-	}
-	if (!tile->numparts && sot->numparts > 0) {
-		tile->numparts = sot->numparts;
-	}
-
-	tile->pptstab = 0;
-
-	switch (tile->state) {
-	case JPC_TILE_INIT:
-		/* This is the first tile-part for this tile. */
-		tile->state = JPC_TILE_ACTIVE;
-		assert(!tile->cp);
-		if (!(tile->cp = jpc_dec_cp_copy(dec->cp))) {
-			return -1;
-		}
-		jpc_dec_cp_resetflags(dec->cp);
-		break;
-	default:
-		if (sot->numparts == sot->partno - 1) {
-			tile->state = JPC_TILE_ACTIVELAST;
-		}
-		break;
-	}
-
-	/* Note: We do not increment the expected tile-part number until
-	  all processing for this tile-part is complete. */
-
-	/* We should expect to encounter other tile-part header marker
-	  segments next. */
-	dec->state = JPC_TPH;
-
-	return 0;
+    jpc_dec_tile_t *tile;
+    jpc_sot_t *sot = &ms->parms.sot;
+    jas_image_cmptparm_t *compinfos;
+    jas_image_cmptparm_t *compinfo;
+    jpc_dec_cmpt_t *cmpt;
+    uint_fast16_t cmptno;
+
+    if (dec->state == JPC_MH) {
+
+        compinfos = jas_malloc(dec->numcomps * sizeof(jas_image_cmptparm_t));
+        assert(compinfos);
+        for (cmptno = 0, cmpt = dec->cmpts, compinfo = compinfos;
+          cmptno < dec->numcomps; ++cmptno, ++cmpt, ++compinfo) {
+            compinfo->tlx = 0;
+            compinfo->tly = 0;
+            compinfo->prec = cmpt->prec;
+            compinfo->sgnd = cmpt->sgnd;
+            compinfo->width = cmpt->width;
+            compinfo->height = cmpt->height;
+            compinfo->hstep = cmpt->hstep;
+            compinfo->vstep = cmpt->vstep;
+        }
+
+        if (!(dec->image = jas_image_create(dec->numcomps, compinfos,
+          JAS_IMAGE_CS_UNKNOWN))) {
+            return -1;
+        }
+        jas_free(compinfos);
+
+        /* Is the packet header information stored in PPM marker segments in
+          the main header? */
+        if (dec->ppmstab) {
+            /* Convert the PPM marker segment data into a collection of streams
+              (one stream per tile-part). */
+            if (!(dec->pkthdrstreams = jpc_ppmstabtostreams(dec->ppmstab))) {
+                abort();
+            }
+            jpc_ppxstab_destroy(dec->ppmstab);
+            dec->ppmstab = 0;
+        }
+    }
+
+    if (sot->len > 0) {
+        dec->curtileendoff = jas_stream_getrwcount(dec->in) - ms->len -
+          4 + sot->len;
+    } else {
+        dec->curtileendoff = 0;
+    }
+
+    if (sot->tileno > dec->numtiles) {
+        fprintf(stderr, "invalid tile number in SOT marker segment\n");
+        return -1;
+    }
+    /* Set the current tile. */
+    dec->curtile = &dec->tiles[sot->tileno];
+    tile = dec->curtile;
+    /* Ensure that this is the expected part number. */
+    if (sot->partno != tile->partno) {
+        return -1;
+    }
+    if (tile->numparts > 0 && sot->partno >= tile->numparts) {
+        return -1;
+    }
+    if (!tile->numparts && sot->numparts > 0) {
+        tile->numparts = sot->numparts;
+    }
+
+    tile->pptstab = 0;
+
+    switch (tile->state) {
+    case JPC_TILE_INIT:
+        /* This is the first tile-part for this tile. */
+        tile->state = JPC_TILE_ACTIVE;
+        assert(!tile->cp);
+        if (!(tile->cp = jpc_dec_cp_copy(dec->cp))) {
+            return -1;
+        }
+        jpc_dec_cp_resetflags(dec->cp);
+        break;
+    default:
+        if (sot->numparts == sot->partno - 1) {
+            tile->state = JPC_TILE_ACTIVELAST;
+        }
+        break;
+    }
+
+    /* Note: We do not increment the expected tile-part number until
+      all processing for this tile-part is complete. */
+
+    /* We should expect to encounter other tile-part header marker
+      segments next. */
+    dec->state = JPC_TPH;
+
+    return 0;
 }
 
 static int jpc_dec_process_sod(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	jpc_dec_tile_t *tile;
-	int pos;
-
-	if (!(tile = dec->curtile)) {
-		return -1;
-	}
-
-	if (!tile->partno) {
-		if (!jpc_dec_cp_isvalid(tile->cp)) {
-			return -1;
-		}
-		jpc_dec_cp_prepare(tile->cp);
-		if (jpc_dec_tileinit(dec, tile)) {
-			return -1;
-		}
-	}
-
-	/* Are packet headers stored in the main header or tile-part header? */
-	if (dec->pkthdrstreams) {
-		/* Get the stream containing the packet header data for this
-		  tile-part. */
-		if (!(tile->pkthdrstream = jpc_streamlist_remove(dec->pkthdrstreams, 0))) {
-			return -1;
-		}
-	}
-
-	if (tile->pptstab) {
-		if (!tile->pkthdrstream) {
-			if (!(tile->pkthdrstream = jas_stream_memopen(0, 0))) {
-				return -1;
-			}
-		}
-		pos = jas_stream_tell(tile->pkthdrstream);
-		jas_stream_seek(tile->pkthdrstream, 0, SEEK_END);
-		if (jpc_pptstabwrite(tile->pkthdrstream, tile->pptstab)) {
-			return -1;
-		}
-		jas_stream_seek(tile->pkthdrstream, pos, SEEK_SET);
-		jpc_ppxstab_destroy(tile->pptstab);
-		tile->pptstab = 0;
-	}
-
-	if (jas_getdbglevel() >= 10) {
-		jpc_dec_dump(dec, stderr);
-	}
-
-	if (jpc_dec_decodepkts(dec, (tile->pkthdrstream) ? tile->pkthdrstream :
-	  dec->in, dec->in)) {
-		fprintf(stderr, "jpc_dec_decodepkts failed\n");
-		return -1;
-	}
-
-	/* Gobble any unconsumed tile data. */
-	if (dec->curtileendoff > 0) {
-		uint_fast32_t curoff;
-		uint_fast32_t n;
-		curoff = jas_stream_getrwcount(dec->in);
-		if (curoff < dec->curtileendoff) {
-			n = dec->curtileendoff - curoff;
-			fprintf(stderr,
-			  "warning: ignoring trailing garbage (%lu bytes)\n",
-			  (unsigned long) n);
-
-			while (n-- > 0) {
-				if (jas_stream_getc(dec->in) == EOF) {
-					fprintf(stderr, "read error\n");
-					return -1;
-				}
-			}
-		} else if (curoff > dec->curtileendoff) {
-			fprintf(stderr,
-			  "warning: not enough tile data (%lu bytes)\n",
-			  (unsigned long) curoff - dec->curtileendoff);
-		}
-
-	}
-
-	if (tile->numparts > 0 && tile->partno == tile->numparts - 1) {
-		if (jpc_dec_tiledecode(dec, tile)) {
-			return -1;
-		}
-		jpc_dec_tilefini(dec, tile);
-	}
-
-	dec->curtile = 0;
-
-	/* Increment the expected tile-part number. */
-	++tile->partno;
-
-	/* We should expect to encounter a SOT marker segment next. */
-	dec->state = JPC_TPHSOT;
-
-	return 0;
+    jpc_dec_tile_t *tile;
+    int pos;
+
+    if (!(tile = dec->curtile)) {
+        return -1;
+    }
+
+    if (!tile->partno) {
+        if (!jpc_dec_cp_isvalid(tile->cp)) {
+            return -1;
+        }
+        jpc_dec_cp_prepare(tile->cp);
+        if (jpc_dec_tileinit(dec, tile)) {
+            return -1;
+        }
+    }
+
+    /* Are packet headers stored in the main header or tile-part header? */
+    if (dec->pkthdrstreams) {
+        /* Get the stream containing the packet header data for this
+          tile-part. */
+        if (!(tile->pkthdrstream = jpc_streamlist_remove(dec->pkthdrstreams, 0))) {
+            return -1;
+        }
+    }
+
+    if (tile->pptstab) {
+        if (!tile->pkthdrstream) {
+            if (!(tile->pkthdrstream = jas_stream_memopen(0, 0))) {
+                return -1;
+            }
+        }
+        pos = jas_stream_tell(tile->pkthdrstream);
+        jas_stream_seek(tile->pkthdrstream, 0, SEEK_END);
+        if (jpc_pptstabwrite(tile->pkthdrstream, tile->pptstab)) {
+            return -1;
+        }
+        jas_stream_seek(tile->pkthdrstream, pos, SEEK_SET);
+        jpc_ppxstab_destroy(tile->pptstab);
+        tile->pptstab = 0;
+    }
+
+    if (jas_getdbglevel() >= 10) {
+        jpc_dec_dump(dec, stderr);
+    }
+
+    if (jpc_dec_decodepkts(dec, (tile->pkthdrstream) ? tile->pkthdrstream :
+      dec->in, dec->in)) {
+        fprintf(stderr, "jpc_dec_decodepkts failed\n");
+        return -1;
+    }
+
+    /* Gobble any unconsumed tile data. */
+    if (dec->curtileendoff > 0) {
+        uint_fast32_t curoff;
+        uint_fast32_t n;
+        curoff = jas_stream_getrwcount(dec->in);
+        if (curoff < dec->curtileendoff) {
+            n = dec->curtileendoff - curoff;
+            fprintf(stderr,
+              "warning: ignoring trailing garbage (%lu bytes)\n",
+              (unsigned long) n);
+
+            while (n-- > 0) {
+                if (jas_stream_getc(dec->in) == EOF) {
+                    fprintf(stderr, "read error\n");
+                    return -1;
+                }
+            }
+        } else if (curoff > dec->curtileendoff) {
+            fprintf(stderr,
+              "warning: not enough tile data (%lu bytes)\n",
+              (unsigned long) curoff - dec->curtileendoff);
+        }
+
+    }
+
+    if (tile->numparts > 0 && tile->partno == tile->numparts - 1) {
+        if (jpc_dec_tiledecode(dec, tile)) {
+            return -1;
+        }
+        jpc_dec_tilefini(dec, tile);
+    }
+
+    dec->curtile = 0;
+
+    /* Increment the expected tile-part number. */
+    ++tile->partno;
+
+    /* We should expect to encounter a SOT marker segment next. */
+    dec->state = JPC_TPHSOT;
+
+    return 0;
 }
 
 static int jpc_dec_tileinit(jpc_dec_t *dec, jpc_dec_tile_t *tile)
 {
-	jpc_dec_tcomp_t *tcomp;
-	uint_fast16_t compno;
-	int rlvlno;
-	jpc_dec_rlvl_t *rlvl;
-	jpc_dec_band_t *band;
-	jpc_dec_prc_t *prc;
-	int bndno;
-	jpc_tsfb_band_t *bnd;
-	int bandno;
-	jpc_dec_ccp_t *ccp;
-	int prccnt;
-	jpc_dec_cblk_t *cblk;
-	int cblkcnt;
-	uint_fast32_t tlprcxstart;
-	uint_fast32_t tlprcystart;
-	uint_fast32_t brprcxend;
-	uint_fast32_t brprcyend;
-	uint_fast32_t tlcbgxstart;
-	uint_fast32_t tlcbgystart;
-	uint_fast32_t brcbgxend;
-	uint_fast32_t brcbgyend;
-	uint_fast32_t cbgxstart;
-	uint_fast32_t cbgystart;
-	uint_fast32_t cbgxend;
-	uint_fast32_t cbgyend;
-	uint_fast32_t tlcblkxstart;
-	uint_fast32_t tlcblkystart;
-	uint_fast32_t brcblkxend;
-	uint_fast32_t brcblkyend;
-	uint_fast32_t cblkxstart;
-	uint_fast32_t cblkystart;
-	uint_fast32_t cblkxend;
-	uint_fast32_t cblkyend;
-	uint_fast32_t tmpxstart;
-	uint_fast32_t tmpystart;
-	uint_fast32_t tmpxend;
-	uint_fast32_t tmpyend;
-	jpc_dec_cp_t *cp;
-	jpc_tsfb_band_t bnds[64];
-	jpc_pchg_t *pchg;
-	int pchgno;
-	jpc_dec_cmpt_t *cmpt;
-
-	cp = tile->cp;
-	tile->realmode = 0;
-	if (cp->mctid == JPC_MCT_ICT) {
-		tile->realmode = 1;
-	}
-
-	for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
-	  dec->numcomps; ++compno, ++tcomp, ++cmpt) {
-		ccp = &tile->cp->ccps[compno];
-		if (ccp->qmfbid == JPC_COX_INS) {
-			tile->realmode = 1;
-		}
-		tcomp->numrlvls = ccp->numrlvls;
-		if (!(tcomp->rlvls = jas_malloc(tcomp->numrlvls *
-		  sizeof(jpc_dec_rlvl_t)))) {
-			return -1;
-		}
-		if (!(tcomp->data = jas_seq2d_create(JPC_CEILDIV(tile->xstart,
-		  cmpt->hstep), JPC_CEILDIV(tile->ystart, cmpt->vstep),
-		  JPC_CEILDIV(tile->xend, cmpt->hstep), JPC_CEILDIV(tile->yend,
-		  cmpt->vstep)))) {
-			return -1;
-		}
-		if (!(tcomp->tsfb = jpc_cod_gettsfb(ccp->qmfbid,
-		  tcomp->numrlvls - 1))) {
-			return -1;
-		}
-{
-	jpc_tsfb_getbands(tcomp->tsfb, jas_seq2d_xstart(tcomp->data), jas_seq2d_ystart(tcomp->data), jas_seq2d_xend(tcomp->data), jas_seq2d_yend(tcomp->data), bnds);
-}
-		for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno < tcomp->numrlvls;
-		  ++rlvlno, ++rlvl) {
+    jpc_dec_tcomp_t *tcomp;
+    uint_fast16_t compno;
+    int rlvlno;
+    jpc_dec_rlvl_t *rlvl;
+    jpc_dec_band_t *band;
+    jpc_dec_prc_t *prc;
+    int bndno;
+    jpc_tsfb_band_t *bnd;
+    int bandno;
+    jpc_dec_ccp_t *ccp;
+    int prccnt;
+    jpc_dec_cblk_t *cblk;
+    int cblkcnt;
+    uint_fast32_t tlprcxstart;
+    uint_fast32_t tlprcystart;
+    uint_fast32_t brprcxend;
+    uint_fast32_t brprcyend;
+    uint_fast32_t tlcbgxstart;
+    uint_fast32_t tlcbgystart;
+    uint_fast32_t brcbgxend;
+    uint_fast32_t brcbgyend;
+    uint_fast32_t cbgxstart;
+    uint_fast32_t cbgystart;
+    uint_fast32_t cbgxend;
+    uint_fast32_t cbgyend;
+    uint_fast32_t tlcblkxstart;
+    uint_fast32_t tlcblkystart;
+    uint_fast32_t brcblkxend;
+    uint_fast32_t brcblkyend;
+    uint_fast32_t cblkxstart;
+    uint_fast32_t cblkystart;
+    uint_fast32_t cblkxend;
+    uint_fast32_t cblkyend;
+    uint_fast32_t tmpxstart;
+    uint_fast32_t tmpystart;
+    uint_fast32_t tmpxend;
+    uint_fast32_t tmpyend;
+    jpc_dec_cp_t *cp;
+    jpc_tsfb_band_t bnds[64];
+    jpc_pchg_t *pchg;
+    int pchgno;
+    jpc_dec_cmpt_t *cmpt;
+
+    cp = tile->cp;
+    tile->realmode = 0;
+    if (cp->mctid == JPC_MCT_ICT) {
+        tile->realmode = 1;
+    }
+
+    for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
+      dec->numcomps; ++compno, ++tcomp, ++cmpt) {
+        ccp = &tile->cp->ccps[compno];
+        if (ccp->qmfbid == JPC_COX_INS) {
+            tile->realmode = 1;
+        }
+        tcomp->numrlvls = ccp->numrlvls;
+        if (!(tcomp->rlvls = jas_malloc(tcomp->numrlvls *
+          sizeof(jpc_dec_rlvl_t)))) {
+            return -1;
+        }
+        if (!(tcomp->data = jas_seq2d_create(JPC_CEILDIV(tile->xstart,
+          cmpt->hstep), JPC_CEILDIV(tile->ystart, cmpt->vstep),
+          JPC_CEILDIV(tile->xend, cmpt->hstep), JPC_CEILDIV(tile->yend,
+          cmpt->vstep)))) {
+            return -1;
+        }
+        if (!(tcomp->tsfb = jpc_cod_gettsfb(ccp->qmfbid,
+          tcomp->numrlvls - 1))) {
+            return -1;
+        }
+{
+    jpc_tsfb_getbands(tcomp->tsfb, jas_seq2d_xstart(tcomp->data), jas_seq2d_ystart(tcomp->data), jas_seq2d_xend(tcomp->data), jas_seq2d_yend(tcomp->data), bnds);
+}
+        for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno < tcomp->numrlvls;
+          ++rlvlno, ++rlvl) {
 rlvl->bands = 0;
-			rlvl->xstart = JPC_CEILDIVPOW2(tcomp->xstart,
-			  tcomp->numrlvls - 1 - rlvlno);
-			rlvl->ystart = JPC_CEILDIVPOW2(tcomp->ystart,
-			  tcomp->numrlvls - 1 - rlvlno);
-			rlvl->xend = JPC_CEILDIVPOW2(tcomp->xend,
-			  tcomp->numrlvls - 1 - rlvlno);
-			rlvl->yend = JPC_CEILDIVPOW2(tcomp->yend,
-			  tcomp->numrlvls - 1 - rlvlno);
-			rlvl->prcwidthexpn = ccp->prcwidthexpns[rlvlno];
-			rlvl->prcheightexpn = ccp->prcheightexpns[rlvlno];
-			tlprcxstart = JPC_FLOORDIVPOW2(rlvl->xstart,
-			  rlvl->prcwidthexpn) << rlvl->prcwidthexpn;
-			tlprcystart = JPC_FLOORDIVPOW2(rlvl->ystart,
-			  rlvl->prcheightexpn) << rlvl->prcheightexpn;
-			brprcxend = JPC_CEILDIVPOW2(rlvl->xend,
-			  rlvl->prcwidthexpn) << rlvl->prcwidthexpn;
-			brprcyend = JPC_CEILDIVPOW2(rlvl->yend,
-			  rlvl->prcheightexpn) << rlvl->prcheightexpn;
-			rlvl->numhprcs = (brprcxend - tlprcxstart) >>
-			  rlvl->prcwidthexpn;
-			rlvl->numvprcs = (brprcyend - tlprcystart) >>
-			  rlvl->prcheightexpn;
-			rlvl->numprcs = rlvl->numhprcs * rlvl->numvprcs;
-
-			if (rlvl->xstart >= rlvl->xend || rlvl->ystart >= rlvl->yend) {
-				rlvl->bands = 0;
-				rlvl->numprcs = 0;
-				rlvl->numhprcs = 0;
-				rlvl->numvprcs = 0;
-				continue;
-			}	
-			if (!rlvlno) {
-				tlcbgxstart = tlprcxstart;
-				tlcbgystart = tlprcystart;
-				brcbgxend = brprcxend;
-				brcbgyend = brprcyend;
-				rlvl->cbgwidthexpn = rlvl->prcwidthexpn;
-				rlvl->cbgheightexpn = rlvl->prcheightexpn;
-			} else {
-				tlcbgxstart = JPC_CEILDIVPOW2(tlprcxstart, 1);
-				tlcbgystart = JPC_CEILDIVPOW2(tlprcystart, 1);
-				brcbgxend = JPC_CEILDIVPOW2(brprcxend, 1);
-				brcbgyend = JPC_CEILDIVPOW2(brprcyend, 1);
-				rlvl->cbgwidthexpn = rlvl->prcwidthexpn - 1;
-				rlvl->cbgheightexpn = rlvl->prcheightexpn - 1;
-			}
-			rlvl->cblkwidthexpn = JAS_MIN(ccp->cblkwidthexpn,
-			  rlvl->cbgwidthexpn);
-			rlvl->cblkheightexpn = JAS_MIN(ccp->cblkheightexpn,
-			  rlvl->cbgheightexpn);
-
-			rlvl->numbands = (!rlvlno) ? 1 : 3;
-			if (!(rlvl->bands = jas_malloc(rlvl->numbands *
-			  sizeof(jpc_dec_band_t)))) {
-				return -1;
-			}
-			for (bandno = 0, band = rlvl->bands;
-			  bandno < rlvl->numbands; ++bandno, ++band) {
-				bndno = (!rlvlno) ? 0 : (3 * (rlvlno - 1) +
-				  bandno + 1);
-				bnd = &bnds[bndno];
-
-				band->orient = bnd->orient;
-				band->stepsize = ccp->stepsizes[bndno];
-				band->analgain = JPC_NOMINALGAIN(ccp->qmfbid,
-				  tcomp->numrlvls - 1, rlvlno, band->orient);
-				band->absstepsize = jpc_calcabsstepsize(band->stepsize,
-				  cmpt->prec + band->analgain);
-				band->numbps = ccp->numguardbits +
-				  JPC_QCX_GETEXPN(band->stepsize) - 1;
-				band->roishift = (ccp->roishift + band->numbps >= JPC_PREC) ?
-				  (JPC_PREC - 1 - band->numbps) : ccp->roishift;
-				band->data = 0;
-				band->prcs = 0;
-				if (bnd->xstart == bnd->xend || bnd->ystart == bnd->yend) {
-					continue;
-				}
-				if (!(band->data = jas_seq2d_create(0, 0, 0, 0))) {
-					return -1;
-				}
-				jas_seq2d_bindsub(band->data, tcomp->data, bnd->locxstart, bnd->locystart, bnd->locxend, bnd->locyend);
-				jas_seq2d_setshift(band->data, bnd->xstart, bnd->ystart);
-
-				assert(rlvl->numprcs);
-
-				if (!(band->prcs = jas_malloc(rlvl->numprcs * sizeof(jpc_dec_prc_t)))) {
-					return -1;
-				}
+            rlvl->xstart = JPC_CEILDIVPOW2(tcomp->xstart,
+              tcomp->numrlvls - 1 - rlvlno);
+            rlvl->ystart = JPC_CEILDIVPOW2(tcomp->ystart,
+              tcomp->numrlvls - 1 - rlvlno);
+            rlvl->xend = JPC_CEILDIVPOW2(tcomp->xend,
+              tcomp->numrlvls - 1 - rlvlno);
+            rlvl->yend = JPC_CEILDIVPOW2(tcomp->yend,
+              tcomp->numrlvls - 1 - rlvlno);
+            rlvl->prcwidthexpn = ccp->prcwidthexpns[rlvlno];
+            rlvl->prcheightexpn = ccp->prcheightexpns[rlvlno];
+            tlprcxstart = JPC_FLOORDIVPOW2(rlvl->xstart,
+              rlvl->prcwidthexpn) << rlvl->prcwidthexpn;
+            tlprcystart = JPC_FLOORDIVPOW2(rlvl->ystart,
+              rlvl->prcheightexpn) << rlvl->prcheightexpn;
+            brprcxend = JPC_CEILDIVPOW2(rlvl->xend,
+              rlvl->prcwidthexpn) << rlvl->prcwidthexpn;
+            brprcyend = JPC_CEILDIVPOW2(rlvl->yend,
+              rlvl->prcheightexpn) << rlvl->prcheightexpn;
+            rlvl->numhprcs = (brprcxend - tlprcxstart) >>
+              rlvl->prcwidthexpn;
+            rlvl->numvprcs = (brprcyend - tlprcystart) >>
+              rlvl->prcheightexpn;
+            rlvl->numprcs = rlvl->numhprcs * rlvl->numvprcs;
+
+            if (rlvl->xstart >= rlvl->xend || rlvl->ystart >= rlvl->yend) {
+                rlvl->bands = 0;
+                rlvl->numprcs = 0;
+                rlvl->numhprcs = 0;
+                rlvl->numvprcs = 0;
+                continue;
+            }   
+            if (!rlvlno) {
+                tlcbgxstart = tlprcxstart;
+                tlcbgystart = tlprcystart;
+                brcbgxend = brprcxend;
+                brcbgyend = brprcyend;
+                rlvl->cbgwidthexpn = rlvl->prcwidthexpn;
+                rlvl->cbgheightexpn = rlvl->prcheightexpn;
+            } else {
+                tlcbgxstart = JPC_CEILDIVPOW2(tlprcxstart, 1);
+                tlcbgystart = JPC_CEILDIVPOW2(tlprcystart, 1);
+                brcbgxend = JPC_CEILDIVPOW2(brprcxend, 1);
+                brcbgyend = JPC_CEILDIVPOW2(brprcyend, 1);
+                rlvl->cbgwidthexpn = rlvl->prcwidthexpn - 1;
+                rlvl->cbgheightexpn = rlvl->prcheightexpn - 1;
+            }
+            rlvl->cblkwidthexpn = JAS_MIN(ccp->cblkwidthexpn,
+              rlvl->cbgwidthexpn);
+            rlvl->cblkheightexpn = JAS_MIN(ccp->cblkheightexpn,
+              rlvl->cbgheightexpn);
+
+            rlvl->numbands = (!rlvlno) ? 1 : 3;
+            if (!(rlvl->bands = jas_malloc(rlvl->numbands *
+              sizeof(jpc_dec_band_t)))) {
+                return -1;
+            }
+            for (bandno = 0, band = rlvl->bands;
+              bandno < rlvl->numbands; ++bandno, ++band) {
+                bndno = (!rlvlno) ? 0 : (3 * (rlvlno - 1) +
+                  bandno + 1);
+                bnd = &bnds[bndno];
+
+                band->orient = bnd->orient;
+                band->stepsize = ccp->stepsizes[bndno];
+                band->analgain = JPC_NOMINALGAIN(ccp->qmfbid,
+                  tcomp->numrlvls - 1, rlvlno, band->orient);
+                band->absstepsize = jpc_calcabsstepsize(band->stepsize,
+                  cmpt->prec + band->analgain);
+                band->numbps = ccp->numguardbits +
+                  JPC_QCX_GETEXPN(band->stepsize) - 1;
+                band->roishift = (ccp->roishift + band->numbps >= JPC_PREC) ?
+                  (JPC_PREC - 1 - band->numbps) : ccp->roishift;
+                band->data = 0;
+                band->prcs = 0;
+                if (bnd->xstart == bnd->xend || bnd->ystart == bnd->yend) {
+                    continue;
+                }
+                if (!(band->data = jas_seq2d_create(0, 0, 0, 0))) {
+                    return -1;
+                }
+                jas_seq2d_bindsub(band->data, tcomp->data, bnd->locxstart, bnd->locystart, bnd->locxend, bnd->locyend);
+                jas_seq2d_setshift(band->data, bnd->xstart, bnd->ystart);
+
+                assert(rlvl->numprcs);
+
+                if (!(band->prcs = jas_malloc(rlvl->numprcs * sizeof(jpc_dec_prc_t)))) {
+                    return -1;
+                }
 
 /************************************************/
-	cbgxstart = tlcbgxstart;
-	cbgystart = tlcbgystart;
-	for (prccnt = rlvl->numprcs, prc = band->prcs;
-	  prccnt > 0; --prccnt, ++prc) {
-		cbgxend = cbgxstart + (1 << rlvl->cbgwidthexpn);
-		cbgyend = cbgystart + (1 << rlvl->cbgheightexpn);
-		prc->xstart = JAS_MAX(cbgxstart, jas_seq2d_xstart(band->data));
-		prc->ystart = JAS_MAX(cbgystart, jas_seq2d_ystart(band->data));
-		prc->xend = JAS_MIN(cbgxend, jas_seq2d_xend(band->data));
-		prc->yend = JAS_MIN(cbgyend, jas_seq2d_yend(band->data));
-		if (prc->xend > prc->xstart && prc->yend > prc->ystart) {
-			tlcblkxstart = JPC_FLOORDIVPOW2(prc->xstart,
-			  rlvl->cblkwidthexpn) << rlvl->cblkwidthexpn;
-			tlcblkystart = JPC_FLOORDIVPOW2(prc->ystart,
-			  rlvl->cblkheightexpn) << rlvl->cblkheightexpn;
-			brcblkxend = JPC_CEILDIVPOW2U(prc->xend,
-			  rlvl->cblkwidthexpn) << rlvl->cblkwidthexpn;
-			brcblkyend = JPC_CEILDIVPOW2U(prc->yend,
-			  rlvl->cblkheightexpn) << rlvl->cblkheightexpn;
-			prc->numhcblks = (brcblkxend - tlcblkxstart) >>
-			  rlvl->cblkwidthexpn;
-			prc->numvcblks = (brcblkyend - tlcblkystart) >>
-			  rlvl->cblkheightexpn;
-			prc->numcblks = prc->numhcblks * prc->numvcblks;
-			assert(prc->numcblks > 0);
-
-			if (!(prc->incltagtree = jpc_tagtree_create(prc->numhcblks, prc->numvcblks))) {
-				return -1;
-			}
-			if (!(prc->numimsbstagtree = jpc_tagtree_create(prc->numhcblks, prc->numvcblks))) {
-				return -1;
-			}
-			if (!(prc->cblks = jas_malloc(prc->numcblks * sizeof(jpc_dec_cblk_t)))) {
-				return -1;
-			}
-
-			cblkxstart = cbgxstart;
-			cblkystart = cbgystart;
-			for (cblkcnt = prc->numcblks, cblk = prc->cblks; cblkcnt > 0;) {
-				cblkxend = cblkxstart + (1 << rlvl->cblkwidthexpn);
-				cblkyend = cblkystart + (1 << rlvl->cblkheightexpn);
-				tmpxstart = JAS_MAX(cblkxstart, prc->xstart);
-				tmpystart = JAS_MAX(cblkystart, prc->ystart);
-				tmpxend = JAS_MIN(cblkxend, prc->xend);
-				tmpyend = JAS_MIN(cblkyend, prc->yend);
-				if (tmpxend > tmpxstart && tmpyend > tmpystart) {
-					cblk->firstpassno = -1;
-					cblk->mqdec = 0;
-					cblk->nulldec = 0;
-					cblk->flags = 0;
-					cblk->numpasses = 0;
-					cblk->segs.head = 0;
-					cblk->segs.tail = 0;
-					cblk->curseg = 0;
-					cblk->numimsbs = 0;
-					cblk->numlenbits = 3;
-					cblk->flags = 0;
-					if (!(cblk->data = jas_seq2d_create(0, 0, 0, 0))) {
-						return -1;
-					}
-					jas_seq2d_bindsub(cblk->data, band->data, tmpxstart, tmpystart, tmpxend, tmpyend);
-					++cblk;
-					--cblkcnt;
-				}
-				cblkxstart += 1 << rlvl->cblkwidthexpn;
-				if (cblkxstart >= cbgxend) {
-					cblkxstart = cbgxstart;
-					cblkystart += 1 << rlvl->cblkheightexpn;
-				}
-			}
-
-		} else {
-			prc->cblks = 0;
-			prc->incltagtree = 0;
-			prc->numimsbstagtree = 0;
-		}
-		cbgxstart += 1 << rlvl->cbgwidthexpn;
-		if (cbgxstart >= brcbgxend) {
-			cbgxstart = tlcbgxstart;
-			cbgystart += 1 << rlvl->cbgheightexpn;
-		}
-
-	}
+    cbgxstart = tlcbgxstart;
+    cbgystart = tlcbgystart;
+    for (prccnt = rlvl->numprcs, prc = band->prcs;
+      prccnt > 0; --prccnt, ++prc) {
+        cbgxend = cbgxstart + (1 << rlvl->cbgwidthexpn);
+        cbgyend = cbgystart + (1 << rlvl->cbgheightexpn);
+        prc->xstart = JAS_MAX(cbgxstart, jas_seq2d_xstart(band->data));
+        prc->ystart = JAS_MAX(cbgystart, jas_seq2d_ystart(band->data));
+        prc->xend = JAS_MIN(cbgxend, jas_seq2d_xend(band->data));
+        prc->yend = JAS_MIN(cbgyend, jas_seq2d_yend(band->data));
+        if (prc->xend > prc->xstart && prc->yend > prc->ystart) {
+            tlcblkxstart = JPC_FLOORDIVPOW2(prc->xstart,
+              rlvl->cblkwidthexpn) << rlvl->cblkwidthexpn;
+            tlcblkystart = JPC_FLOORDIVPOW2(prc->ystart,
+              rlvl->cblkheightexpn) << rlvl->cblkheightexpn;
+            brcblkxend = JPC_CEILDIVPOW2U(prc->xend,
+              rlvl->cblkwidthexpn) << rlvl->cblkwidthexpn;
+            brcblkyend = JPC_CEILDIVPOW2U(prc->yend,
+              rlvl->cblkheightexpn) << rlvl->cblkheightexpn;
+            prc->numhcblks = (brcblkxend - tlcblkxstart) >>
+              rlvl->cblkwidthexpn;
+            prc->numvcblks = (brcblkyend - tlcblkystart) >>
+              rlvl->cblkheightexpn;
+            prc->numcblks = prc->numhcblks * prc->numvcblks;
+            assert(prc->numcblks > 0);
+
+            if (!(prc->incltagtree = jpc_tagtree_create(prc->numhcblks, prc->numvcblks))) {
+                return -1;
+            }
+            if (!(prc->numimsbstagtree = jpc_tagtree_create(prc->numhcblks, prc->numvcblks))) {
+                return -1;
+            }
+            if (!(prc->cblks = jas_malloc(prc->numcblks * sizeof(jpc_dec_cblk_t)))) {
+                return -1;
+            }
+
+            cblkxstart = cbgxstart;
+            cblkystart = cbgystart;
+            for (cblkcnt = prc->numcblks, cblk = prc->cblks; cblkcnt > 0;) {
+                cblkxend = cblkxstart + (1 << rlvl->cblkwidthexpn);
+                cblkyend = cblkystart + (1 << rlvl->cblkheightexpn);
+                tmpxstart = JAS_MAX(cblkxstart, prc->xstart);
+                tmpystart = JAS_MAX(cblkystart, prc->ystart);
+                tmpxend = JAS_MIN(cblkxend, prc->xend);
+                tmpyend = JAS_MIN(cblkyend, prc->yend);
+                if (tmpxend > tmpxstart && tmpyend > tmpystart) {
+                    cblk->firstpassno = -1;
+                    cblk->mqdec = 0;
+                    cblk->nulldec = 0;
+                    cblk->flags = 0;
+                    cblk->numpasses = 0;
+                    cblk->segs.head = 0;
+                    cblk->segs.tail = 0;
+                    cblk->curseg = 0;
+                    cblk->numimsbs = 0;
+                    cblk->numlenbits = 3;
+                    cblk->flags = 0;
+                    if (!(cblk->data = jas_seq2d_create(0, 0, 0, 0))) {
+                        return -1;
+                    }
+                    jas_seq2d_bindsub(cblk->data, band->data, tmpxstart, tmpystart, tmpxend, tmpyend);
+                    ++cblk;
+                    --cblkcnt;
+                }
+                cblkxstart += 1 << rlvl->cblkwidthexpn;
+                if (cblkxstart >= cbgxend) {
+                    cblkxstart = cbgxstart;
+                    cblkystart += 1 << rlvl->cblkheightexpn;
+                }
+            }
+
+        } else {
+            prc->cblks = 0;
+            prc->incltagtree = 0;
+            prc->numimsbstagtree = 0;
+        }
+        cbgxstart += 1 << rlvl->cbgwidthexpn;
+        if (cbgxstart >= brcbgxend) {
+            cbgxstart = tlcbgxstart;
+            cbgystart += 1 << rlvl->cbgheightexpn;
+        }
+
+    }
 /********************************************/
-			}
-		}
-	}
+            }
+        }
+    }
 
 if (!(tile->pi = jpc_dec_pi_create(dec, tile)))
 {
-	return -1;
+    return -1;
 }
 
-	for (pchgno = 0; pchgno < jpc_pchglist_numpchgs(tile->cp->pchglist);
-	  ++pchgno) {
-		pchg = jpc_pchg_copy(jpc_pchglist_get(tile->cp->pchglist, pchgno));
-		assert(pchg);
-		jpc_pi_addpchg(tile->pi, pchg);
-	}
-	jpc_pi_init(tile->pi);
+    for (pchgno = 0; pchgno < jpc_pchglist_numpchgs(tile->cp->pchglist);
+      ++pchgno) {
+        pchg = jpc_pchg_copy(jpc_pchglist_get(tile->cp->pchglist, pchgno));
+        assert(pchg);
+        jpc_pi_addpchg(tile->pi, pchg);
+    }
+    jpc_pi_init(tile->pi);
 
-	return 0;
+    return 0;
 }
 
 static int jpc_dec_tilefini(jpc_dec_t *dec, jpc_dec_tile_t *tile)
 {
-	jpc_dec_tcomp_t *tcomp;
-	int compno;
-	int bandno;
-	int rlvlno;
-	jpc_dec_band_t *band;
-	jpc_dec_rlvl_t *rlvl;
-	int prcno;
-	jpc_dec_prc_t *prc;
-	jpc_dec_seg_t *seg;
-	jpc_dec_cblk_t *cblk;
-	int cblkno;
+    jpc_dec_tcomp_t *tcomp;
+    int compno;
+    int bandno;
+    int rlvlno;
+    jpc_dec_band_t *band;
+    jpc_dec_rlvl_t *rlvl;
+    int prcno;
+    jpc_dec_prc_t *prc;
+    jpc_dec_seg_t *seg;
+    jpc_dec_cblk_t *cblk;
+    int cblkno;
 
 if (tile->tcomps) {
 
-	for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
-	  ++compno, ++tcomp) {
-		for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno < tcomp->numrlvls;
-		  ++rlvlno, ++rlvl) {
+    for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
+      ++compno, ++tcomp) {
+        for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno < tcomp->numrlvls;
+          ++rlvlno, ++rlvl) {
 if (!rlvl->bands) {
-	continue;
+    continue;
 }
-			for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands; ++bandno, ++band) {
+            for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands; ++bandno, ++band) {
 if (band->prcs) {
-				for (prcno = 0, prc = band->prcs; prcno <
-				  rlvl->numprcs; ++prcno, ++prc) {
+                for (prcno = 0, prc = band->prcs; prcno <
+                  rlvl->numprcs; ++prcno, ++prc) {
 if (!prc->cblks) {
-	continue;
-}
-					for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks; ++cblkno, ++cblk) {
-
-	while (cblk->segs.head) {
-		seg = cblk->segs.head;
-		jpc_seglist_remove(&cblk->segs, seg);
-		jpc_seg_destroy(seg);
-	}
-	jas_matrix_destroy(cblk->data);
-	if (cblk->mqdec) {
-		jpc_mqdec_destroy(cblk->mqdec);
-	}
-	if (cblk->nulldec) {
-		jpc_bitstream_close(cblk->nulldec);
-	}
-	if (cblk->flags) {
-		jas_matrix_destroy(cblk->flags);
-	}
-					}
-					if (prc->incltagtree) {
-						jpc_tagtree_destroy(prc->incltagtree);
-					}
-					if (prc->numimsbstagtree) {
-						jpc_tagtree_destroy(prc->numimsbstagtree);
-					}
-					if (prc->cblks) {
-						jas_free(prc->cblks);
-					}
-				}
-}
-				if (band->data) {
-					jas_matrix_destroy(band->data);
-				}
-				if (band->prcs) {
-					jas_free(band->prcs);
-				}
-			}
-			if (rlvl->bands) {
-				jas_free(rlvl->bands);
-			}
-		}
-		if (tcomp->rlvls) {
-			jas_free(tcomp->rlvls);
-		}
-		if (tcomp->data) {
-			jas_matrix_destroy(tcomp->data);
-		}
-		if (tcomp->tsfb) {
-			jpc_tsfb_destroy(tcomp->tsfb);
-		}
-	}
-}
-	if (tile->cp) {
-		jpc_dec_cp_destroy(tile->cp);
-		tile->cp = 0;
-	}
-	if (tile->tcomps) {
-		jas_free(tile->tcomps);
-		tile->tcomps = 0;
-	}
-	if (tile->pi) {
-		jpc_pi_destroy(tile->pi);
-		tile->pi = 0;
-	}
-	if (tile->pkthdrstream) {
-		jas_stream_close(tile->pkthdrstream);
-		tile->pkthdrstream = 0;
-	}
-	if (tile->pptstab) {
-		jpc_ppxstab_destroy(tile->pptstab);
-		tile->pptstab = 0;
-	}
-
-	tile->state = JPC_TILE_DONE;
-
-	return 0;
+    continue;
+}
+                    for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks; ++cblkno, ++cblk) {
+
+    while (cblk->segs.head) {
+        seg = cblk->segs.head;
+        jpc_seglist_remove(&cblk->segs, seg);
+        jpc_seg_destroy(seg);
+    }
+    jas_matrix_destroy(cblk->data);
+    if (cblk->mqdec) {
+        jpc_mqdec_destroy(cblk->mqdec);
+    }
+    if (cblk->nulldec) {
+        jpc_bitstream_close(cblk->nulldec);
+    }
+    if (cblk->flags) {
+        jas_matrix_destroy(cblk->flags);
+    }
+                    }
+                    if (prc->incltagtree) {
+                        jpc_tagtree_destroy(prc->incltagtree);
+                    }
+                    if (prc->numimsbstagtree) {
+                        jpc_tagtree_destroy(prc->numimsbstagtree);
+                    }
+                    if (prc->cblks) {
+                        jas_free(prc->cblks);
+                    }
+                }
+}
+                if (band->data) {
+                    jas_matrix_destroy(band->data);
+                }
+                if (band->prcs) {
+                    jas_free(band->prcs);
+                }
+            }
+            if (rlvl->bands) {
+                jas_free(rlvl->bands);
+            }
+        }
+        if (tcomp->rlvls) {
+            jas_free(tcomp->rlvls);
+        }
+        if (tcomp->data) {
+            jas_matrix_destroy(tcomp->data);
+        }
+        if (tcomp->tsfb) {
+            jpc_tsfb_destroy(tcomp->tsfb);
+        }
+    }
+}
+    if (tile->cp) {
+        jpc_dec_cp_destroy(tile->cp);
+        tile->cp = 0;
+    }
+    if (tile->tcomps) {
+        jas_free(tile->tcomps);
+        tile->tcomps = 0;
+    }
+    if (tile->pi) {
+        jpc_pi_destroy(tile->pi);
+        tile->pi = 0;
+    }
+    if (tile->pkthdrstream) {
+        jas_stream_close(tile->pkthdrstream);
+        tile->pkthdrstream = 0;
+    }
+    if (tile->pptstab) {
+        jpc_ppxstab_destroy(tile->pptstab);
+        tile->pptstab = 0;
+    }
+
+    tile->state = JPC_TILE_DONE;
+
+    return 0;
 }
 
 static int jpc_dec_tiledecode(jpc_dec_t *dec, jpc_dec_tile_t *tile)
 {
-	int i;
-	int j;
-	jpc_dec_tcomp_t *tcomp;
-	jpc_dec_rlvl_t *rlvl;
-	jpc_dec_band_t *band;
-	int compno;
-	int rlvlno;
-	int bandno;
-	int adjust;
-	int v;
-	jpc_dec_ccp_t *ccp;
-	jpc_dec_cmpt_t *cmpt;
-
-	if (jpc_dec_decodecblks(dec, tile)) {
-		fprintf(stderr, "jpc_dec_decodecblks failed\n");
-		return -1;
-	}
-
-	/* Perform dequantization. */
-	for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
-	  ++compno, ++tcomp) {
-		ccp = &tile->cp->ccps[compno];
-		for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno < tcomp->numrlvls;
-		  ++rlvlno, ++rlvl) {
-			if (!rlvl->bands) {
-				continue;
-			}
-			for (bandno = 0, band = rlvl->bands;
-			  bandno < rlvl->numbands; ++bandno, ++band) {
-				if (!band->data) {
-					continue;
-				}
-				jpc_undo_roi(band->data, band->roishift, ccp->roishift -
-				  band->roishift, band->numbps);
-				if (tile->realmode) {
-					jas_matrix_asl(band->data, JPC_FIX_FRACBITS);
-					jpc_dequantize(band->data, band->absstepsize);
-				}
-
-			}
-		}
-	}
-
-	/* Apply an inverse wavelet transform if necessary. */
-	for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
-	  ++compno, ++tcomp) {
-		ccp = &tile->cp->ccps[compno];
-		jpc_tsfb_synthesize(tcomp->tsfb, ((ccp->qmfbid ==
-		  JPC_COX_RFT) ? JPC_TSFB_RITIMODE : 0), tcomp->data);
-	}
-
-
-	/* Apply an inverse intercomponent transform if necessary. */
-	switch (tile->cp->mctid) {
-	case JPC_MCT_RCT:
-		assert(dec->numcomps == 3);
-		jpc_irct(tile->tcomps[0].data, tile->tcomps[1].data,
-		  tile->tcomps[2].data);
-		break;
-	case JPC_MCT_ICT:
-		assert(dec->numcomps == 3);
-		jpc_iict(tile->tcomps[0].data, tile->tcomps[1].data,
-		  tile->tcomps[2].data);
-		break;
-	}
-
-	/* Perform rounding and convert to integer values. */
-	if (tile->realmode) {
-		for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
-		  ++compno, ++tcomp) {
-			for (i = 0; i < jas_matrix_numrows(tcomp->data); ++i) {
-				for (j = 0; j < jas_matrix_numcols(tcomp->data); ++j) {
-					v = jas_matrix_get(tcomp->data, i, j);
-					v = jpc_fix_round(v);
-					jas_matrix_set(tcomp->data, i, j, jpc_fixtoint(v));
-				}
-			}
-		}
-	}
-
-	/* Perform level shift. */
-	for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
-	  dec->numcomps; ++compno, ++tcomp, ++cmpt) {
-		adjust = cmpt->sgnd ? 0 : (1 << (cmpt->prec - 1));
-		for (i = 0; i < jas_matrix_numrows(tcomp->data); ++i) {
-			for (j = 0; j < jas_matrix_numcols(tcomp->data); ++j) {
-				*jas_matrix_getref(tcomp->data, i, j) += adjust;
-			}
-		}
-	}
-
-	/* Perform clipping. */
-	for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
-	  dec->numcomps; ++compno, ++tcomp, ++cmpt) {
-		jpc_fix_t mn;
-		jpc_fix_t mx;
-		mn = cmpt->sgnd ? (-(1 << (cmpt->prec - 1))) : (0);
-		mx = cmpt->sgnd ? ((1 << (cmpt->prec - 1)) - 1) : ((1 <<
-		  cmpt->prec) - 1);
-		jas_matrix_clip(tcomp->data, mn, mx);
-	}
-
-	/* XXX need to free tsfb struct */
-
-	/* Write the data for each component of the image. */
-	for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
-	  dec->numcomps; ++compno, ++tcomp, ++cmpt) {
-		if (jas_image_writecmpt(dec->image, compno, tcomp->xstart -
-		  JPC_CEILDIV(dec->xstart, cmpt->hstep), tcomp->ystart -
-		  JPC_CEILDIV(dec->ystart, cmpt->vstep), jas_matrix_numcols(
-		  tcomp->data), jas_matrix_numrows(tcomp->data), tcomp->data)) {
-			fprintf(stderr, "write component failed\n");
-			return -4;
-		}
-	}
-
-	return 0;
+    int i;
+    int j;
+    jpc_dec_tcomp_t *tcomp;
+    jpc_dec_rlvl_t *rlvl;
+    jpc_dec_band_t *band;
+    int compno;
+    int rlvlno;
+    int bandno;
+    int adjust;
+    int v;
+    jpc_dec_ccp_t *ccp;
+    jpc_dec_cmpt_t *cmpt;
+
+    if (jpc_dec_decodecblks(dec, tile)) {
+        fprintf(stderr, "jpc_dec_decodecblks failed\n");
+        return -1;
+    }
+
+    /* Perform dequantization. */
+    for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
+      ++compno, ++tcomp) {
+        ccp = &tile->cp->ccps[compno];
+        for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno < tcomp->numrlvls;
+          ++rlvlno, ++rlvl) {
+            if (!rlvl->bands) {
+                continue;
+            }
+            for (bandno = 0, band = rlvl->bands;
+              bandno < rlvl->numbands; ++bandno, ++band) {
+                if (!band->data) {
+                    continue;
+                }
+                jpc_undo_roi(band->data, band->roishift, ccp->roishift -
+                  band->roishift, band->numbps);
+                if (tile->realmode) {
+                    jas_matrix_asl(band->data, JPC_FIX_FRACBITS);
+                    jpc_dequantize(band->data, band->absstepsize);
+                }
+
+            }
+        }
+    }
+
+    /* Apply an inverse wavelet transform if necessary. */
+    for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
+      ++compno, ++tcomp) {
+        ccp = &tile->cp->ccps[compno];
+        jpc_tsfb_synthesize(tcomp->tsfb, ((ccp->qmfbid ==
+          JPC_COX_RFT) ? JPC_TSFB_RITIMODE : 0), tcomp->data);
+    }
+
+
+    /* Apply an inverse intercomponent transform if necessary. */
+    switch (tile->cp->mctid) {
+    case JPC_MCT_RCT:
+        assert(dec->numcomps == 3);
+        jpc_irct(tile->tcomps[0].data, tile->tcomps[1].data,
+          tile->tcomps[2].data);
+        break;
+    case JPC_MCT_ICT:
+        assert(dec->numcomps == 3);
+        jpc_iict(tile->tcomps[0].data, tile->tcomps[1].data,
+          tile->tcomps[2].data);
+        break;
+    }
+
+    /* Perform rounding and convert to integer values. */
+    if (tile->realmode) {
+        for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
+          ++compno, ++tcomp) {
+            for (i = 0; i < jas_matrix_numrows(tcomp->data); ++i) {
+                for (j = 0; j < jas_matrix_numcols(tcomp->data); ++j) {
+                    v = jas_matrix_get(tcomp->data, i, j);
+                    v = jpc_fix_round(v);
+                    jas_matrix_set(tcomp->data, i, j, jpc_fixtoint(v));
+                }
+            }
+        }
+    }
+
+    /* Perform level shift. */
+    for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
+      dec->numcomps; ++compno, ++tcomp, ++cmpt) {
+        adjust = cmpt->sgnd ? 0 : (1 << (cmpt->prec - 1));
+        for (i = 0; i < jas_matrix_numrows(tcomp->data); ++i) {
+            for (j = 0; j < jas_matrix_numcols(tcomp->data); ++j) {
+                *jas_matrix_getref(tcomp->data, i, j) += adjust;
+            }
+        }
+    }
+
+    /* Perform clipping. */
+    for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
+      dec->numcomps; ++compno, ++tcomp, ++cmpt) {
+        jpc_fix_t mn;
+        jpc_fix_t mx;
+        mn = cmpt->sgnd ? (-(1 << (cmpt->prec - 1))) : (0);
+        mx = cmpt->sgnd ? ((1 << (cmpt->prec - 1)) - 1) : ((1 <<
+          cmpt->prec) - 1);
+        jas_matrix_clip(tcomp->data, mn, mx);
+    }
+
+    /* XXX need to free tsfb struct */
+
+    /* Write the data for each component of the image. */
+    for (compno = 0, tcomp = tile->tcomps, cmpt = dec->cmpts; compno <
+      dec->numcomps; ++compno, ++tcomp, ++cmpt) {
+        if (jas_image_writecmpt(dec->image, compno, tcomp->xstart -
+          JPC_CEILDIV(dec->xstart, cmpt->hstep), tcomp->ystart -
+          JPC_CEILDIV(dec->ystart, cmpt->vstep), jas_matrix_numcols(
+          tcomp->data), jas_matrix_numrows(tcomp->data), tcomp->data)) {
+            fprintf(stderr, "write component failed\n");
+            return -4;
+        }
+    }
+
+    return 0;
 }
 
 static int jpc_dec_process_eoc(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	int tileno;
-	jpc_dec_tile_t *tile;
-	for (tileno = 0, tile = dec->tiles; tileno < dec->numtiles; ++tileno,
-	  ++tile) {
-		if (tile->state == JPC_TILE_ACTIVE) {
-			if (jpc_dec_tiledecode(dec, tile)) {
-				return -1;
-			}
-		}
-		jpc_dec_tilefini(dec, tile);
-	}
+    int tileno;
+    jpc_dec_tile_t *tile;
+    for (tileno = 0, tile = dec->tiles; tileno < dec->numtiles; ++tileno,
+      ++tile) {
+        if (tile->state == JPC_TILE_ACTIVE) {
+            if (jpc_dec_tiledecode(dec, tile)) {
+                return -1;
+            }
+        }
+        jpc_dec_tilefini(dec, tile);
+    }
 
-	/* We are done processing the code stream. */
-	dec->state = JPC_MT;
+    /* We are done processing the code stream. */
+    dec->state = JPC_MT;
 
-	return 1;
+    return 1;
 }
 
 static int jpc_dec_process_siz(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	jpc_siz_t *siz = &ms->parms.siz;
-	uint_fast16_t compno;
-	uint_fast32_t tileno;
-	jpc_dec_tile_t *tile;
-	jpc_dec_tcomp_t *tcomp;
-	uint_fast32_t htileno;
-	uint_fast32_t vtileno;
-	jpc_dec_cmpt_t *cmpt;
-
-	dec->xstart = siz->xoff;
-	dec->ystart = siz->yoff;
-	dec->xend = siz->width;
-	dec->yend = siz->height;
-	dec->tilewidth = siz->tilewidth;
-	dec->tileheight = siz->tileheight;
-	dec->tilexoff = siz->tilexoff;
-	dec->tileyoff = siz->tileyoff;
-	dec->numcomps = siz->numcomps;
-	if (!(dec->cp = jpc_dec_cp_create(dec->numcomps))) {
-		return -1;
-	}
-
-	if (!(dec->cmpts = jas_malloc(dec->numcomps * sizeof(jpc_dec_cmpt_t)))) {
-		return -1;
-	}
-
-	for (compno = 0, cmpt = dec->cmpts; compno < dec->numcomps; ++compno,
-	  ++cmpt) {
-		cmpt->prec = siz->comps[compno].prec;
-		cmpt->sgnd = siz->comps[compno].sgnd;
-		cmpt->hstep = siz->comps[compno].hsamp;
-		cmpt->vstep = siz->comps[compno].vsamp;
-		cmpt->width = JPC_CEILDIV(dec->xend, cmpt->hstep) -
-		  JPC_CEILDIV(dec->xstart, cmpt->hstep);
-		cmpt->height = JPC_CEILDIV(dec->yend, cmpt->vstep) -
-		  JPC_CEILDIV(dec->ystart, cmpt->vstep);
-		cmpt->hsubstep = 0;
-		cmpt->vsubstep = 0;
-	}
-
-	dec->image = 0;
-
-	dec->numhtiles = JPC_CEILDIV(dec->xend - dec->tilexoff, dec->tilewidth);
-	dec->numvtiles = JPC_CEILDIV(dec->yend - dec->tileyoff, dec->tileheight);
-	dec->numtiles = dec->numhtiles * dec->numvtiles;
-	if (!(dec->tiles = jas_malloc(dec->numtiles * sizeof(jpc_dec_tile_t)))) {
-		return -1;
-	}
-
-	for (tileno = 0, tile = dec->tiles; tileno < dec->numtiles; ++tileno,
-	  ++tile) {
-		htileno = tileno % dec->numhtiles;
-		vtileno = tileno / dec->numhtiles;
-		tile->realmode = 0;
-		tile->state = JPC_TILE_INIT;
-		tile->xstart = JAS_MAX(dec->tilexoff + htileno * dec->tilewidth,
-		  dec->xstart);
-		tile->ystart = JAS_MAX(dec->tileyoff + vtileno * dec->tileheight,
-		  dec->ystart);
-		tile->xend = JAS_MIN(dec->tilexoff + (htileno + 1) *
-		  dec->tilewidth, dec->xend);
-		tile->yend = JAS_MIN(dec->tileyoff + (vtileno + 1) *
-		  dec->tileheight, dec->yend);
-		tile->numparts = 0;
-		tile->partno = 0;
-		tile->pkthdrstream = 0;
-		tile->pkthdrstreampos = 0;
-		tile->pptstab = 0;
-		tile->cp = 0;
-		if (!(tile->tcomps = jas_malloc(dec->numcomps *
-		  sizeof(jpc_dec_tcomp_t)))) {
-			return -1;
-		}
-		for (compno = 0, cmpt = dec->cmpts, tcomp = tile->tcomps;
-		  compno < dec->numcomps; ++compno, ++cmpt, ++tcomp) {
-			tcomp->rlvls = 0;
-			tcomp->data = 0;
-			tcomp->xstart = JPC_CEILDIV(tile->xstart, cmpt->hstep);
-			tcomp->ystart = JPC_CEILDIV(tile->ystart, cmpt->vstep);
-			tcomp->xend = JPC_CEILDIV(tile->xend, cmpt->hstep);
-			tcomp->yend = JPC_CEILDIV(tile->yend, cmpt->vstep);
-			tcomp->tsfb = 0;
-		}
-	}
-
-	dec->pkthdrstreams = 0;
-
-	/* We should expect to encounter other main header marker segments
-	  or an SOT marker segment next. */
-	dec->state = JPC_MH;
-
-	return 0;
+    jpc_siz_t *siz = &ms->parms.siz;
+    uint_fast16_t compno;
+    uint_fast32_t tileno;
+    jpc_dec_tile_t *tile;
+    jpc_dec_tcomp_t *tcomp;
+    uint_fast32_t htileno;
+    uint_fast32_t vtileno;
+    jpc_dec_cmpt_t *cmpt;
+
+    dec->xstart = siz->xoff;
+    dec->ystart = siz->yoff;
+    dec->xend = siz->width;
+    dec->yend = siz->height;
+    dec->tilewidth = siz->tilewidth;
+    dec->tileheight = siz->tileheight;
+    dec->tilexoff = siz->tilexoff;
+    dec->tileyoff = siz->tileyoff;
+    dec->numcomps = siz->numcomps;
+    if (!(dec->cp = jpc_dec_cp_create(dec->numcomps))) {
+        return -1;
+    }
+
+    if (!(dec->cmpts = jas_malloc(dec->numcomps * sizeof(jpc_dec_cmpt_t)))) {
+        return -1;
+    }
+
+    for (compno = 0, cmpt = dec->cmpts; compno < dec->numcomps; ++compno,
+      ++cmpt) {
+        cmpt->prec = siz->comps[compno].prec;
+        cmpt->sgnd = siz->comps[compno].sgnd;
+        cmpt->hstep = siz->comps[compno].hsamp;
+        cmpt->vstep = siz->comps[compno].vsamp;
+        cmpt->width = JPC_CEILDIV(dec->xend, cmpt->hstep) -
+          JPC_CEILDIV(dec->xstart, cmpt->hstep);
+        cmpt->height = JPC_CEILDIV(dec->yend, cmpt->vstep) -
+          JPC_CEILDIV(dec->ystart, cmpt->vstep);
+        cmpt->hsubstep = 0;
+        cmpt->vsubstep = 0;
+    }
+
+    dec->image = 0;
+
+    dec->numhtiles = JPC_CEILDIV(dec->xend - dec->tilexoff, dec->tilewidth);
+    dec->numvtiles = JPC_CEILDIV(dec->yend - dec->tileyoff, dec->tileheight);
+    dec->numtiles = dec->numhtiles * dec->numvtiles;
+    if (!(dec->tiles = jas_malloc(dec->numtiles * sizeof(jpc_dec_tile_t)))) {
+        return -1;
+    }
+
+    for (tileno = 0, tile = dec->tiles; tileno < dec->numtiles; ++tileno,
+      ++tile) {
+        htileno = tileno % dec->numhtiles;
+        vtileno = tileno / dec->numhtiles;
+        tile->realmode = 0;
+        tile->state = JPC_TILE_INIT;
+        tile->xstart = JAS_MAX(dec->tilexoff + htileno * dec->tilewidth,
+          dec->xstart);
+        tile->ystart = JAS_MAX(dec->tileyoff + vtileno * dec->tileheight,
+          dec->ystart);
+        tile->xend = JAS_MIN(dec->tilexoff + (htileno + 1) *
+          dec->tilewidth, dec->xend);
+        tile->yend = JAS_MIN(dec->tileyoff + (vtileno + 1) *
+          dec->tileheight, dec->yend);
+        tile->numparts = 0;
+        tile->partno = 0;
+        tile->pkthdrstream = 0;
+        tile->pkthdrstreampos = 0;
+        tile->pptstab = 0;
+        tile->cp = 0;
+        if (!(tile->tcomps = jas_malloc(dec->numcomps *
+          sizeof(jpc_dec_tcomp_t)))) {
+            return -1;
+        }
+        for (compno = 0, cmpt = dec->cmpts, tcomp = tile->tcomps;
+          compno < dec->numcomps; ++compno, ++cmpt, ++tcomp) {
+            tcomp->rlvls = 0;
+            tcomp->data = 0;
+            tcomp->xstart = JPC_CEILDIV(tile->xstart, cmpt->hstep);
+            tcomp->ystart = JPC_CEILDIV(tile->ystart, cmpt->vstep);
+            tcomp->xend = JPC_CEILDIV(tile->xend, cmpt->hstep);
+            tcomp->yend = JPC_CEILDIV(tile->yend, cmpt->vstep);
+            tcomp->tsfb = 0;
+        }
+    }
+
+    dec->pkthdrstreams = 0;
+
+    /* We should expect to encounter other main header marker segments
+      or an SOT marker segment next. */
+    dec->state = JPC_MH;
+
+    return 0;
 }
 
 static int jpc_dec_process_cod(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	jpc_cod_t *cod = &ms->parms.cod;
-	jpc_dec_tile_t *tile;
-
-	switch (dec->state) {
-	case JPC_MH:
-		jpc_dec_cp_setfromcod(dec->cp, cod);
-		break;
-	case JPC_TPH:
-		if (!(tile = dec->curtile)) {
-			return -1;
-		}
-		if (tile->partno != 0) {
-			return -1;
-		}
-		jpc_dec_cp_setfromcod(tile->cp, cod);
-		break;
-	}
-	return 0;
+    jpc_cod_t *cod = &ms->parms.cod;
+    jpc_dec_tile_t *tile;
+
+    switch (dec->state) {
+    case JPC_MH:
+        jpc_dec_cp_setfromcod(dec->cp, cod);
+        break;
+    case JPC_TPH:
+        if (!(tile = dec->curtile)) {
+            return -1;
+        }
+        if (tile->partno != 0) {
+            return -1;
+        }
+        jpc_dec_cp_setfromcod(tile->cp, cod);
+        break;
+    }
+    return 0;
 }
 
 static int jpc_dec_process_coc(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	jpc_coc_t *coc = &ms->parms.coc;
-	jpc_dec_tile_t *tile;
-
-	if (coc->compno > dec->numcomps) {
-		fprintf(stderr,
-		  "invalid component number in COC marker segment\n");
-		return -1;
-	}
-	switch (dec->state) {
-	case JPC_MH:
-		jpc_dec_cp_setfromcoc(dec->cp, coc);
-		break;
-	case JPC_TPH:
-		if (!(tile = dec->curtile)) {
-			return -1;
-		}
-		if (tile->partno > 0) {
-			return -1;
-		}
-		jpc_dec_cp_setfromcoc(tile->cp, coc);
-		break;
-	}
-	return 0;
+    jpc_coc_t *coc = &ms->parms.coc;
+    jpc_dec_tile_t *tile;
+
+    if (coc->compno > dec->numcomps) {
+        fprintf(stderr,
+          "invalid component number in COC marker segment\n");
+        return -1;
+    }
+    switch (dec->state) {
+    case JPC_MH:
+        jpc_dec_cp_setfromcoc(dec->cp, coc);
+        break;
+    case JPC_TPH:
+        if (!(tile = dec->curtile)) {
+            return -1;
+        }
+        if (tile->partno > 0) {
+            return -1;
+        }
+        jpc_dec_cp_setfromcoc(tile->cp, coc);
+        break;
+    }
+    return 0;
 }
 
 static int jpc_dec_process_rgn(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	jpc_rgn_t *rgn = &ms->parms.rgn;
-	jpc_dec_tile_t *tile;
-
-	if (rgn->compno > dec->numcomps) {
-		fprintf(stderr,
-		  "invalid component number in RGN marker segment\n");
-		return -1;
-	}
-	switch (dec->state) {
-	case JPC_MH:
-		jpc_dec_cp_setfromrgn(dec->cp, rgn);
-		break;
-	case JPC_TPH:
-		if (!(tile = dec->curtile)) {
-			return -1;
-		}
-		if (tile->partno > 0) {
-			return -1;
-		}
-		jpc_dec_cp_setfromrgn(tile->cp, rgn);
-		break;
-	}
-
-	return 0;
+    jpc_rgn_t *rgn = &ms->parms.rgn;
+    jpc_dec_tile_t *tile;
+
+    if (rgn->compno > dec->numcomps) {
+        fprintf(stderr,
+          "invalid component number in RGN marker segment\n");
+        return -1;
+    }
+    switch (dec->state) {
+    case JPC_MH:
+        jpc_dec_cp_setfromrgn(dec->cp, rgn);
+        break;
+    case JPC_TPH:
+        if (!(tile = dec->curtile)) {
+            return -1;
+        }
+        if (tile->partno > 0) {
+            return -1;
+        }
+        jpc_dec_cp_setfromrgn(tile->cp, rgn);
+        break;
+    }
+
+    return 0;
 }
 
 static int jpc_dec_process_qcd(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	jpc_qcd_t *qcd = &ms->parms.qcd;
-	jpc_dec_tile_t *tile;
-
-	switch (dec->state) {
-	case JPC_MH:
-		jpc_dec_cp_setfromqcd(dec->cp, qcd);
-		break;
-	case JPC_TPH:
-		if (!(tile = dec->curtile)) {
-			return -1;
-		}
-		if (tile->partno > 0) {
-			return -1;
-		}
-		jpc_dec_cp_setfromqcd(tile->cp, qcd);
-		break;
-	}
-	return 0;
+    jpc_qcd_t *qcd = &ms->parms.qcd;
+    jpc_dec_tile_t *tile;
+
+    switch (dec->state) {
+    case JPC_MH:
+        jpc_dec_cp_setfromqcd(dec->cp, qcd);
+        break;
+    case JPC_TPH:
+        if (!(tile = dec->curtile)) {
+            return -1;
+        }
+        if (tile->partno > 0) {
+            return -1;
+        }
+        jpc_dec_cp_setfromqcd(tile->cp, qcd);
+        break;
+    }
+    return 0;
 }
 
 static int jpc_dec_process_qcc(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	jpc_qcc_t *qcc = &ms->parms.qcc;
-	jpc_dec_tile_t *tile;
-
-	if (qcc->compno > dec->numcomps) {
-		fprintf(stderr,
-		  "invalid component number in QCC marker segment\n");
-		return -1;
-	}
-	switch (dec->state) {
-	case JPC_MH:
-		jpc_dec_cp_setfromqcc(dec->cp, qcc);
-		break;
-	case JPC_TPH:
-		if (!(tile = dec->curtile)) {
-			return -1;
-		}
-		if (tile->partno > 0) {
-			return -1;
-		}
-		jpc_dec_cp_setfromqcc(tile->cp, qcc);
-		break;
-	}
-	return 0;
+    jpc_qcc_t *qcc = &ms->parms.qcc;
+    jpc_dec_tile_t *tile;
+
+    if (qcc->compno > dec->numcomps) {
+        fprintf(stderr,
+          "invalid component number in QCC marker segment\n");
+        return -1;
+    }
+    switch (dec->state) {
+    case JPC_MH:
+        jpc_dec_cp_setfromqcc(dec->cp, qcc);
+        break;
+    case JPC_TPH:
+        if (!(tile = dec->curtile)) {
+            return -1;
+        }
+        if (tile->partno > 0) {
+            return -1;
+        }
+        jpc_dec_cp_setfromqcc(tile->cp, qcc);
+        break;
+    }
+    return 0;
 }
 
 static int jpc_dec_process_poc(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	jpc_poc_t *poc = &ms->parms.poc;
-	jpc_dec_tile_t *tile;
-	switch (dec->state) {
-	case JPC_MH:
-		if (jpc_dec_cp_setfrompoc(dec->cp, poc, 1)) {
-			return -1;
-		}
-		break;
-	case JPC_TPH:
-		if (!(tile = dec->curtile)) {
-			return -1;
-		}
-		if (!tile->partno) {
-			if (jpc_dec_cp_setfrompoc(tile->cp, poc, (!tile->partno))) {
-				return -1;
-			}
-		} else {
-			jpc_pi_addpchgfrompoc(tile->pi, poc);
-		}
-		break;
-	}
-	return 0;
+    jpc_poc_t *poc = &ms->parms.poc;
+    jpc_dec_tile_t *tile;
+    switch (dec->state) {
+    case JPC_MH:
+        if (jpc_dec_cp_setfrompoc(dec->cp, poc, 1)) {
+            return -1;
+        }
+        break;
+    case JPC_TPH:
+        if (!(tile = dec->curtile)) {
+            return -1;
+        }
+        if (!tile->partno) {
+            if (jpc_dec_cp_setfrompoc(tile->cp, poc, (!tile->partno))) {
+                return -1;
+            }
+        } else {
+            jpc_pi_addpchgfrompoc(tile->pi, poc);
+        }
+        break;
+    }
+    return 0;
 }
 
 static int jpc_dec_process_ppm(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	jpc_ppm_t *ppm = &ms->parms.ppm;
-	jpc_ppxstabent_t *ppmstabent;
+    jpc_ppm_t *ppm = &ms->parms.ppm;
+    jpc_ppxstabent_t *ppmstabent;
 
-	if (!dec->ppmstab) {
-		if (!(dec->ppmstab = jpc_ppxstab_create())) {
-			return -1;
-		}
-	}
+    if (!dec->ppmstab) {
+        if (!(dec->ppmstab = jpc_ppxstab_create())) {
+            return -1;
+        }
+    }
 
-	if (!(ppmstabent = jpc_ppxstabent_create())) {
-		return -1;
-	}
-	ppmstabent->ind = ppm->ind;
-	ppmstabent->data = ppm->data;
-	ppm->data = 0;
-	ppmstabent->len = ppm->len;
-	if (jpc_ppxstab_insert(dec->ppmstab, ppmstabent)) {
-		return -1;
-	}
-	return 0;
+    if (!(ppmstabent = jpc_ppxstabent_create())) {
+        return -1;
+    }
+    ppmstabent->ind = ppm->ind;
+    ppmstabent->data = ppm->data;
+    ppm->data = 0;
+    ppmstabent->len = ppm->len;
+    if (jpc_ppxstab_insert(dec->ppmstab, ppmstabent)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_dec_process_ppt(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	jpc_ppt_t *ppt = &ms->parms.ppt;
-	jpc_dec_tile_t *tile;
-	jpc_ppxstabent_t *pptstabent;
-
-	tile = dec->curtile;
-	if (!tile->pptstab) {
-		if (!(tile->pptstab = jpc_ppxstab_create())) {
-			return -1;
-		}
-	}
-	if (!(pptstabent = jpc_ppxstabent_create())) {
-		return -1;
-	}
-	pptstabent->ind = ppt->ind;
-	pptstabent->data = ppt->data;
-	ppt->data = 0;
-	pptstabent->len = ppt->len;
-	if (jpc_ppxstab_insert(tile->pptstab, pptstabent)) {
-		return -1;
-	}
-	return 0;
+    jpc_ppt_t *ppt = &ms->parms.ppt;
+    jpc_dec_tile_t *tile;
+    jpc_ppxstabent_t *pptstabent;
+
+    tile = dec->curtile;
+    if (!tile->pptstab) {
+        if (!(tile->pptstab = jpc_ppxstab_create())) {
+            return -1;
+        }
+    }
+    if (!(pptstabent = jpc_ppxstabent_create())) {
+        return -1;
+    }
+    pptstabent->ind = ppt->ind;
+    pptstabent->data = ppt->data;
+    ppt->data = 0;
+    pptstabent->len = ppt->len;
+    if (jpc_ppxstab_insert(tile->pptstab, pptstabent)) {
+        return -1;
+    }
+    return 0;
 }
 
 static int jpc_dec_process_com(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	return 0;
+    return 0;
 }
 
 static int jpc_dec_process_unk(jpc_dec_t *dec, jpc_ms_t *ms)
 {
-	fprintf(stderr, "warning: ignoring unknown marker segment\n");
-	jpc_ms_dump(ms, stderr);
-	return 0;
+    fprintf(stderr, "warning: ignoring unknown marker segment\n");
+    jpc_ms_dump(ms, stderr);
+    return 0;
 }
 
 /******************************************************************************\
@@ -1512,426 +1514,426 @@ static int jpc_dec_process_unk(jpc_dec_t *dec, jpc_ms_t *ms)
 
 static jpc_dec_cp_t *jpc_dec_cp_create(uint_fast16_t numcomps)
 {
-	jpc_dec_cp_t *cp;
-	jpc_dec_ccp_t *ccp;
-	int compno;
-
-	if (!(cp = jas_malloc(sizeof(jpc_dec_cp_t)))) {
-		return 0;
-	}
-	cp->flags = 0;
-	cp->numcomps = numcomps;
-	cp->prgord = 0;
-	cp->numlyrs = 0;
-	cp->mctid = 0;
-	cp->csty = 0;
-	if (!(cp->ccps = jas_malloc(cp->numcomps * sizeof(jpc_dec_ccp_t)))) {
-		return 0;
-	}
-	if (!(cp->pchglist = jpc_pchglist_create())) {
-		jas_free(cp->ccps);
-		return 0;
-	}
-	for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
-	  ++compno, ++ccp) {
-		ccp->flags = 0;
-		ccp->numrlvls = 0;
-		ccp->cblkwidthexpn = 0;
-		ccp->cblkheightexpn = 0;
-		ccp->qmfbid = 0;
-		ccp->numstepsizes = 0;
-		ccp->numguardbits = 0;
-		ccp->roishift = 0;
-		ccp->cblkctx = 0;
-	}
-	return cp;
+    jpc_dec_cp_t *cp;
+    jpc_dec_ccp_t *ccp;
+    int compno;
+
+    if (!(cp = jas_malloc(sizeof(jpc_dec_cp_t)))) {
+        return 0;
+    }
+    cp->flags = 0;
+    cp->numcomps = numcomps;
+    cp->prgord = 0;
+    cp->numlyrs = 0;
+    cp->mctid = 0;
+    cp->csty = 0;
+    if (!(cp->ccps = jas_malloc(cp->numcomps * sizeof(jpc_dec_ccp_t)))) {
+        return 0;
+    }
+    if (!(cp->pchglist = jpc_pchglist_create())) {
+        jas_free(cp->ccps);
+        return 0;
+    }
+    for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
+      ++compno, ++ccp) {
+        ccp->flags = 0;
+        ccp->numrlvls = 0;
+        ccp->cblkwidthexpn = 0;
+        ccp->cblkheightexpn = 0;
+        ccp->qmfbid = 0;
+        ccp->numstepsizes = 0;
+        ccp->numguardbits = 0;
+        ccp->roishift = 0;
+        ccp->cblkctx = 0;
+    }
+    return cp;
 }
 
 static jpc_dec_cp_t *jpc_dec_cp_copy(jpc_dec_cp_t *cp)
 {
-	jpc_dec_cp_t *newcp;
-	jpc_dec_ccp_t *newccp;
-	jpc_dec_ccp_t *ccp;
-	int compno;
-
-	if (!(newcp = jpc_dec_cp_create(cp->numcomps))) {
-		return 0;
-	}
-	newcp->flags = cp->flags;
-	newcp->prgord = cp->prgord;
-	newcp->numlyrs = cp->numlyrs;
-	newcp->mctid = cp->mctid;
-	newcp->csty = cp->csty;
-	jpc_pchglist_destroy(newcp->pchglist);
-	newcp->pchglist = 0;
-	if (!(newcp->pchglist = jpc_pchglist_copy(cp->pchglist))) {
-		jas_free(newcp);
-		return 0;
-	}
-	for (compno = 0, newccp = newcp->ccps, ccp = cp->ccps;
-	  compno < cp->numcomps;
-	  ++compno, ++newccp, ++ccp) {
-		*newccp = *ccp;
-	}
-	return newcp;
+    jpc_dec_cp_t *newcp;
+    jpc_dec_ccp_t *newccp;
+    jpc_dec_ccp_t *ccp;
+    int compno;
+
+    if (!(newcp = jpc_dec_cp_create(cp->numcomps))) {
+        return 0;
+    }
+    newcp->flags = cp->flags;
+    newcp->prgord = cp->prgord;
+    newcp->numlyrs = cp->numlyrs;
+    newcp->mctid = cp->mctid;
+    newcp->csty = cp->csty;
+    jpc_pchglist_destroy(newcp->pchglist);
+    newcp->pchglist = 0;
+    if (!(newcp->pchglist = jpc_pchglist_copy(cp->pchglist))) {
+        jas_free(newcp);
+        return 0;
+    }
+    for (compno = 0, newccp = newcp->ccps, ccp = cp->ccps;
+      compno < cp->numcomps;
+      ++compno, ++newccp, ++ccp) {
+        *newccp = *ccp;
+    }
+    return newcp;
 }
 
 static void jpc_dec_cp_resetflags(jpc_dec_cp_t *cp)
 {
-	int compno;
-	jpc_dec_ccp_t *ccp;
-	cp->flags &= (JPC_CSET | JPC_QSET);
-	for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
-	  ++compno, ++ccp) {
-		ccp->flags = 0;
-	}
+    int compno;
+    jpc_dec_ccp_t *ccp;
+    cp->flags &= (JPC_CSET | JPC_QSET);
+    for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
+      ++compno, ++ccp) {
+        ccp->flags = 0;
+    }
 }
 
 static void jpc_dec_cp_destroy(jpc_dec_cp_t *cp)
 {
-	if (cp->ccps) {
-		jas_free(cp->ccps);
-	}
-	if (cp->pchglist) {
-		jpc_pchglist_destroy(cp->pchglist);
-	}
-	jas_free(cp);
+    if (cp->ccps) {
+        jas_free(cp->ccps);
+    }
+    if (cp->pchglist) {
+        jpc_pchglist_destroy(cp->pchglist);
+    }
+    jas_free(cp);
 }
 
 static int jpc_dec_cp_isvalid(jpc_dec_cp_t *cp)
 {
-	uint_fast16_t compcnt;
-	jpc_dec_ccp_t *ccp;
+    uint_fast16_t compcnt;
+    jpc_dec_ccp_t *ccp;
 
-	if (!(cp->flags & JPC_CSET) || !(cp->flags & JPC_QSET)) {
-		return 0;
-	}
-	for (compcnt = cp->numcomps, ccp = cp->ccps; compcnt > 0; --compcnt,
-	  ++ccp) {
-		/* Is there enough step sizes for the number of bands? */
-		if ((ccp->qsty != JPC_QCX_SIQNT && ccp->numstepsizes < 3 *
-		  ccp->numrlvls - 2) || (ccp->qsty == JPC_QCX_SIQNT &&
-		  ccp->numstepsizes != 1)) {
-			return 0;
-		}
-	}
-	return 1;
+    if (!(cp->flags & JPC_CSET) || !(cp->flags & JPC_QSET)) {
+        return 0;
+    }
+    for (compcnt = cp->numcomps, ccp = cp->ccps; compcnt > 0; --compcnt,
+      ++ccp) {
+        /* Is there enough step sizes for the number of bands? */
+        if ((ccp->qsty != JPC_QCX_SIQNT && ccp->numstepsizes < 3 *
+          ccp->numrlvls - 2) || (ccp->qsty == JPC_QCX_SIQNT &&
+          ccp->numstepsizes != 1)) {
+            return 0;
+        }
+    }
+    return 1;
 }
 
 static void calcstepsizes(uint_fast16_t refstepsize, int numrlvls,
   uint_fast16_t *stepsizes)
 {
-	int bandno;
-	int numbands;
-	uint_fast16_t expn;
-	uint_fast16_t mant;
-	expn = JPC_QCX_GETEXPN(refstepsize);
-	mant = JPC_QCX_GETMANT(refstepsize);
-	numbands = 3 * numrlvls - 2;
-	for (bandno = 0; bandno < numbands; ++bandno) {
-		stepsizes[bandno] = JPC_QCX_MANT(mant) | JPC_QCX_EXPN(expn +
-		  (numrlvls - 1) - (numrlvls - 1 - ((bandno > 0) ? ((bandno + 2) / 3) : (0))));
-	}
+    int bandno;
+    int numbands;
+    uint_fast16_t expn;
+    uint_fast16_t mant;
+    expn = JPC_QCX_GETEXPN(refstepsize);
+    mant = JPC_QCX_GETMANT(refstepsize);
+    numbands = 3 * numrlvls - 2;
+    for (bandno = 0; bandno < numbands; ++bandno) {
+        stepsizes[bandno] = JPC_QCX_MANT(mant) | JPC_QCX_EXPN(expn +
+          (numrlvls - 1) - (numrlvls - 1 - ((bandno > 0) ? ((bandno + 2) / 3) : (0))));
+    }
 }
 
 static int jpc_dec_cp_prepare(jpc_dec_cp_t *cp)
 {
-	jpc_dec_ccp_t *ccp;
-	int compno;
-	int i;
-	for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
-	  ++compno, ++ccp) {
-		if (!(ccp->csty & JPC_COX_PRT)) {
-			for (i = 0; i < JPC_MAXRLVLS; ++i) {
-				ccp->prcwidthexpns[i] = 15;
-				ccp->prcheightexpns[i] = 15;
-			}
-		}
-		if (ccp->qsty == JPC_QCX_SIQNT) {
-			calcstepsizes(ccp->stepsizes[0], ccp->numrlvls, ccp->stepsizes);
-		}
-	}
-	return 0;
+    jpc_dec_ccp_t *ccp;
+    int compno;
+    int i;
+    for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
+      ++compno, ++ccp) {
+        if (!(ccp->csty & JPC_COX_PRT)) {
+            for (i = 0; i < JPC_MAXRLVLS; ++i) {
+                ccp->prcwidthexpns[i] = 15;
+                ccp->prcheightexpns[i] = 15;
+            }
+        }
+        if (ccp->qsty == JPC_QCX_SIQNT) {
+            calcstepsizes(ccp->stepsizes[0], ccp->numrlvls, ccp->stepsizes);
+        }
+    }
+    return 0;
 }
 
 static int jpc_dec_cp_setfromcod(jpc_dec_cp_t *cp, jpc_cod_t *cod)
 {
-	jpc_dec_ccp_t *ccp;
-	int compno;
-	cp->flags |= JPC_CSET;
-	cp->prgord = cod->prg;
-	if (cod->mctrans) {
-		cp->mctid = (cod->compparms.qmfbid == JPC_COX_INS) ? (JPC_MCT_ICT) : (JPC_MCT_RCT);
-	} else {
-		cp->mctid = JPC_MCT_NONE;
-	}
-	cp->numlyrs = cod->numlyrs;
-	cp->csty = cod->csty & (JPC_COD_SOP | JPC_COD_EPH);
-	for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
-	  ++compno, ++ccp) {
-		jpc_dec_cp_setfromcox(cp, ccp, &cod->compparms, 0);
-	}
-	cp->flags |= JPC_CSET;
-	return 0;
+    jpc_dec_ccp_t *ccp;
+    int compno;
+    cp->flags |= JPC_CSET;
+    cp->prgord = cod->prg;
+    if (cod->mctrans) {
+        cp->mctid = (cod->compparms.qmfbid == JPC_COX_INS) ? (JPC_MCT_ICT) : (JPC_MCT_RCT);
+    } else {
+        cp->mctid = JPC_MCT_NONE;
+    }
+    cp->numlyrs = cod->numlyrs;
+    cp->csty = cod->csty & (JPC_COD_SOP | JPC_COD_EPH);
+    for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
+      ++compno, ++ccp) {
+        jpc_dec_cp_setfromcox(cp, ccp, &cod->compparms, 0);
+    }
+    cp->flags |= JPC_CSET;
+    return 0;
 }
 
 static int jpc_dec_cp_setfromcoc(jpc_dec_cp_t *cp, jpc_coc_t *coc)
 {
-	jpc_dec_cp_setfromcox(cp, &cp->ccps[coc->compno], &coc->compparms, JPC_COC);
-	return 0;
+    jpc_dec_cp_setfromcox(cp, &cp->ccps[coc->compno], &coc->compparms, JPC_COC);
+    return 0;
 }
 
 static int jpc_dec_cp_setfromcox(jpc_dec_cp_t *cp, jpc_dec_ccp_t *ccp,
   jpc_coxcp_t *compparms, int flags)
 {
-	int rlvlno;
-	if ((flags & JPC_COC) || !(ccp->flags & JPC_COC)) {
-		ccp->numrlvls = compparms->numdlvls + 1;
-		ccp->cblkwidthexpn = JPC_COX_GETCBLKSIZEEXPN(
-		  compparms->cblkwidthval);
-		ccp->cblkheightexpn = JPC_COX_GETCBLKSIZEEXPN(
-		  compparms->cblkheightval);
-		ccp->qmfbid = compparms->qmfbid;
-		ccp->cblkctx = compparms->cblksty;
-		ccp->csty = compparms->csty & JPC_COX_PRT;
-		for (rlvlno = 0; rlvlno < compparms->numrlvls; ++rlvlno) {
-			ccp->prcwidthexpns[rlvlno] =
-			  compparms->rlvls[rlvlno].parwidthval;
-			ccp->prcheightexpns[rlvlno] =
-			  compparms->rlvls[rlvlno].parheightval;
-		}
-		ccp->flags |= flags | JPC_CSET;
-	}
-	return 0;
+    int rlvlno;
+    if ((flags & JPC_COC) || !(ccp->flags & JPC_COC)) {
+        ccp->numrlvls = compparms->numdlvls + 1;
+        ccp->cblkwidthexpn = JPC_COX_GETCBLKSIZEEXPN(
+          compparms->cblkwidthval);
+        ccp->cblkheightexpn = JPC_COX_GETCBLKSIZEEXPN(
+          compparms->cblkheightval);
+        ccp->qmfbid = compparms->qmfbid;
+        ccp->cblkctx = compparms->cblksty;
+        ccp->csty = compparms->csty & JPC_COX_PRT;
+        for (rlvlno = 0; rlvlno < compparms->numrlvls; ++rlvlno) {
+            ccp->prcwidthexpns[rlvlno] =
+              compparms->rlvls[rlvlno].parwidthval;
+            ccp->prcheightexpns[rlvlno] =
+              compparms->rlvls[rlvlno].parheightval;
+        }
+        ccp->flags |= flags | JPC_CSET;
+    }
+    return 0;
 }
 
 static int jpc_dec_cp_setfromqcd(jpc_dec_cp_t *cp, jpc_qcd_t *qcd)
 {
-	int compno;
-	jpc_dec_ccp_t *ccp;
-	for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
-	  ++compno, ++ccp) {
-		jpc_dec_cp_setfromqcx(cp, ccp, &qcd->compparms, 0);
-	}
-	cp->flags |= JPC_QSET;
-	return 0;
+    int compno;
+    jpc_dec_ccp_t *ccp;
+    for (compno = 0, ccp = cp->ccps; compno < cp->numcomps;
+      ++compno, ++ccp) {
+        jpc_dec_cp_setfromqcx(cp, ccp, &qcd->compparms, 0);
+    }
+    cp->flags |= JPC_QSET;
+    return 0;
 }
 
 static int jpc_dec_cp_setfromqcc(jpc_dec_cp_t *cp, jpc_qcc_t *qcc)
 {
-	return jpc_dec_cp_setfromqcx(cp, &cp->ccps[qcc->compno], &qcc->compparms, JPC_QCC);
+    return jpc_dec_cp_setfromqcx(cp, &cp->ccps[qcc->compno], &qcc->compparms, JPC_QCC);
 }
 
 static int jpc_dec_cp_setfromqcx(jpc_dec_cp_t *cp, jpc_dec_ccp_t *ccp,
   jpc_qcxcp_t *compparms, int flags)
 {
-	int bandno;
-	if ((flags & JPC_QCC) || !(ccp->flags & JPC_QCC)) {
-		ccp->flags |= flags | JPC_QSET;
-		for (bandno = 0; bandno < compparms->numstepsizes; ++bandno) {
-			ccp->stepsizes[bandno] = compparms->stepsizes[bandno];
-		}
-		ccp->numstepsizes = compparms->numstepsizes;
-		ccp->numguardbits = compparms->numguard;
-		ccp->qsty = compparms->qntsty;
-	}
-	return 0;
+    int bandno;
+    if ((flags & JPC_QCC) || !(ccp->flags & JPC_QCC)) {
+        ccp->flags |= flags | JPC_QSET;
+        for (bandno = 0; bandno < compparms->numstepsizes; ++bandno) {
+            ccp->stepsizes[bandno] = compparms->stepsizes[bandno];
+        }
+        ccp->numstepsizes = compparms->numstepsizes;
+        ccp->numguardbits = compparms->numguard;
+        ccp->qsty = compparms->qntsty;
+    }
+    return 0;
 }
 
 static int jpc_dec_cp_setfromrgn(jpc_dec_cp_t *cp, jpc_rgn_t *rgn)
 {
-	jpc_dec_ccp_t *ccp;
-	ccp = &cp->ccps[rgn->compno];
-	ccp->roishift = rgn->roishift;
-	return 0;
+    jpc_dec_ccp_t *ccp;
+    ccp = &cp->ccps[rgn->compno];
+    ccp->roishift = rgn->roishift;
+    return 0;
 }
 
 static int jpc_pi_addpchgfrompoc(jpc_pi_t *pi, jpc_poc_t *poc)
 {
-	int pchgno;
-	jpc_pchg_t *pchg;
-	for (pchgno = 0; pchgno < poc->numpchgs; ++pchgno) {
-		if (!(pchg = jpc_pchg_copy(&poc->pchgs[pchgno]))) {
-			return -1;
-		}
-		if (jpc_pchglist_insert(pi->pchglist, -1, pchg)) {
-			return -1;
-		}
-	}
-	return 0;
+    int pchgno;
+    jpc_pchg_t *pchg;
+    for (pchgno = 0; pchgno < poc->numpchgs; ++pchgno) {
+        if (!(pchg = jpc_pchg_copy(&poc->pchgs[pchgno]))) {
+            return -1;
+        }
+        if (jpc_pchglist_insert(pi->pchglist, -1, pchg)) {
+            return -1;
+        }
+    }
+    return 0;
 }
 
 static int jpc_dec_cp_setfrompoc(jpc_dec_cp_t *cp, jpc_poc_t *poc, int reset)
 {
-	int pchgno;
-	jpc_pchg_t *pchg;
-	if (reset) {
-		while (jpc_pchglist_numpchgs(cp->pchglist) > 0) {
-			pchg = jpc_pchglist_remove(cp->pchglist, 0);
-			jpc_pchg_destroy(pchg);
-		}
-	}
-	for (pchgno = 0; pchgno < poc->numpchgs; ++pchgno) {
-		if (!(pchg = jpc_pchg_copy(&poc->pchgs[pchgno]))) {
-			return -1;
-		}
-		if (jpc_pchglist_insert(cp->pchglist, -1, pchg)) {
-			return -1;
-		}
-	}
-	return 0;
+    int pchgno;
+    jpc_pchg_t *pchg;
+    if (reset) {
+        while (jpc_pchglist_numpchgs(cp->pchglist) > 0) {
+            pchg = jpc_pchglist_remove(cp->pchglist, 0);
+            jpc_pchg_destroy(pchg);
+        }
+    }
+    for (pchgno = 0; pchgno < poc->numpchgs; ++pchgno) {
+        if (!(pchg = jpc_pchg_copy(&poc->pchgs[pchgno]))) {
+            return -1;
+        }
+        if (jpc_pchglist_insert(cp->pchglist, -1, pchg)) {
+            return -1;
+        }
+    }
+    return 0;
 }
 
 static jpc_fix_t jpc_calcabsstepsize(int stepsize, int numbits)
 {
-	jpc_fix_t absstepsize;
-	int n;
+    jpc_fix_t absstepsize;
+    int n;
 
-	absstepsize = jpc_inttofix(1);
-	n = JPC_FIX_FRACBITS - 11;
-	absstepsize |= (n >= 0) ? (JPC_QCX_GETMANT(stepsize) << n) :
-	  (JPC_QCX_GETMANT(stepsize) >> (-n));
-	n = numbits - JPC_QCX_GETEXPN(stepsize);
-	absstepsize = (n >= 0) ? (absstepsize << n) : (absstepsize >> (-n));
-	return absstepsize;
+    absstepsize = jpc_inttofix(1);
+    n = JPC_FIX_FRACBITS - 11;
+    absstepsize |= (n >= 0) ? (JPC_QCX_GETMANT(stepsize) << n) :
+      (JPC_QCX_GETMANT(stepsize) >> (-n));
+    n = numbits - JPC_QCX_GETEXPN(stepsize);
+    absstepsize = (n >= 0) ? (absstepsize << n) : (absstepsize >> (-n));
+    return absstepsize;
 }
 
 static void jpc_dequantize(jas_matrix_t *x, jpc_fix_t absstepsize)
 {
-	int i;
-	int j;
-	int t;
+    int i;
+    int j;
+    int t;
 
-	assert(absstepsize >= 0);
-	if (absstepsize == jpc_inttofix(1)) {
-		return;
-	}
+    assert(absstepsize >= 0);
+    if (absstepsize == jpc_inttofix(1)) {
+        return;
+    }
 
-	for (i = 0; i < jas_matrix_numrows(x); ++i) {
-		for (j = 0; j < jas_matrix_numcols(x); ++j) {
-			t = jas_matrix_get(x, i, j);
-			if (t) {
-				t = jpc_fix_mul(t, absstepsize);
-			} else {
-				t = 0;
-			}
-			jas_matrix_set(x, i, j, t);
-		}
-	}
+    for (i = 0; i < jas_matrix_numrows(x); ++i) {
+        for (j = 0; j < jas_matrix_numcols(x); ++j) {
+            t = jas_matrix_get(x, i, j);
+            if (t) {
+                t = jpc_fix_mul(t, absstepsize);
+            } else {
+                t = 0;
+            }
+            jas_matrix_set(x, i, j, t);
+        }
+    }
 
 }
 
 static void jpc_undo_roi(jas_matrix_t *x, int roishift, int bgshift, int numbps)
 {
-	int i;
-	int j;
-	int thresh;
-	jpc_fix_t val;
-	jpc_fix_t mag;
-	bool warn;
-	uint_fast32_t mask;
-
-	if (roishift == 0 && bgshift == 0) {
-		return;
-	}
-	thresh = 1 << roishift;
-
-	warn = false;
-	for (i = 0; i < jas_matrix_numrows(x); ++i) {
-		for (j = 0; j < jas_matrix_numcols(x); ++j) {
-			val = jas_matrix_get(x, i, j);
-			mag = JAS_ABS(val);
-			if (mag >= thresh) {
-				/* We are dealing with ROI data. */
-				mag >>= roishift;
-				val = (val < 0) ? (-mag) : mag;
-				jas_matrix_set(x, i, j, val);
-			} else {
-				/* We are dealing with non-ROI (i.e., background) data. */
-				mag <<= bgshift;
-				mask = (1 << numbps) - 1;
-				/* Perform a basic sanity check on the sample value. */
-				/* Some implementations write garbage in the unused
-				  most-significant bit planes introduced by ROI shifting.
-				  Here we ensure that any such bits are masked off. */
-				if (mag & (~mask)) {
-					if (!warn) {
-						fprintf(stderr,
-						  "warning: possibly corrupt code stream\n");
-						warn = true;
-					}
-					mag &= mask;
-				}
-				val = (val < 0) ? (-mag) : mag;
-				jas_matrix_set(x, i, j, val);
-			}
-		}
-	}
+    int i;
+    int j;
+    int thresh;
+    jpc_fix_t val;
+    jpc_fix_t mag;
+    bool warn;
+    uint_fast32_t mask;
+
+    if (roishift == 0 && bgshift == 0) {
+        return;
+    }
+    thresh = 1 << roishift;
+
+    warn = false;
+    for (i = 0; i < jas_matrix_numrows(x); ++i) {
+        for (j = 0; j < jas_matrix_numcols(x); ++j) {
+            val = jas_matrix_get(x, i, j);
+            mag = JAS_ABS(val);
+            if (mag >= thresh) {
+                /* We are dealing with ROI data. */
+                mag >>= roishift;
+                val = (val < 0) ? (-mag) : mag;
+                jas_matrix_set(x, i, j, val);
+            } else {
+                /* We are dealing with non-ROI (i.e., background) data. */
+                mag <<= bgshift;
+                mask = (1 << numbps) - 1;
+                /* Perform a basic sanity check on the sample value. */
+                /* Some implementations write garbage in the unused
+                  most-significant bit planes introduced by ROI shifting.
+                  Here we ensure that any such bits are masked off. */
+                if (mag & (~mask)) {
+                    if (!warn) {
+                        fprintf(stderr,
+                          "warning: possibly corrupt code stream\n");
+                        warn = true;
+                    }
+                    mag &= mask;
+                }
+                val = (val < 0) ? (-mag) : mag;
+                jas_matrix_set(x, i, j, val);
+            }
+        }
+    }
 }
 
 static jpc_dec_t *jpc_dec_create(jpc_dec_importopts_t *impopts, jas_stream_t *in)
 {
-	jpc_dec_t *dec;
-
-	if (!(dec = jas_malloc(sizeof(jpc_dec_t)))) {
-		return 0;
-	}
-
-	dec->image = 0;
-	dec->xstart = 0;
-	dec->ystart = 0;
-	dec->xend = 0;
-	dec->yend = 0;
-	dec->tilewidth = 0;
-	dec->tileheight = 0;
-	dec->tilexoff = 0;
-	dec->tileyoff = 0;
-	dec->numhtiles = 0;
-	dec->numvtiles = 0;
-	dec->numtiles = 0;
-	dec->tiles = 0;
-	dec->curtile = 0;
-	dec->numcomps = 0;
-	dec->in = in;
-	dec->cp = 0;
-	dec->maxlyrs = impopts->maxlyrs;
-	dec->maxpkts = impopts->maxpkts;
+    jpc_dec_t *dec;
+
+    if (!(dec = jas_malloc(sizeof(jpc_dec_t)))) {
+        return 0;
+    }
+
+    dec->image = 0;
+    dec->xstart = 0;
+    dec->ystart = 0;
+    dec->xend = 0;
+    dec->yend = 0;
+    dec->tilewidth = 0;
+    dec->tileheight = 0;
+    dec->tilexoff = 0;
+    dec->tileyoff = 0;
+    dec->numhtiles = 0;
+    dec->numvtiles = 0;
+    dec->numtiles = 0;
+    dec->tiles = 0;
+    dec->curtile = 0;
+    dec->numcomps = 0;
+    dec->in = in;
+    dec->cp = 0;
+    dec->maxlyrs = impopts->maxlyrs;
+    dec->maxpkts = impopts->maxpkts;
 dec->numpkts = 0;
-	dec->ppmseqno = 0;
-	dec->state = 0;
-	dec->cmpts = 0;
-	dec->pkthdrstreams = 0;
-	dec->ppmstab = 0;
-	dec->curtileendoff = 0;
+    dec->ppmseqno = 0;
+    dec->state = 0;
+    dec->cmpts = 0;
+    dec->pkthdrstreams = 0;
+    dec->ppmstab = 0;
+    dec->curtileendoff = 0;
 
-	return dec;
+    return dec;
 }
 
 static void jpc_dec_destroy(jpc_dec_t *dec)
 {
-	if (dec->cstate) {
-		jpc_cstate_destroy(dec->cstate);
-	}
-	if (dec->pkthdrstreams) {
-		jpc_streamlist_destroy(dec->pkthdrstreams);
-	}
-	if (dec->image) {
-		jas_image_destroy(dec->image);
-	}
+    if (dec->cstate) {
+        jpc_cstate_destroy(dec->cstate);
+    }
+    if (dec->pkthdrstreams) {
+        jpc_streamlist_destroy(dec->pkthdrstreams);
+    }
+    if (dec->image) {
+        jas_image_destroy(dec->image);
+    }
 
-	if (dec->cp) {
-		jpc_dec_cp_destroy(dec->cp);
-	}
+    if (dec->cp) {
+        jpc_dec_cp_destroy(dec->cp);
+    }
 
-	if (dec->cmpts) {
-		jas_free(dec->cmpts);
-	}
+    if (dec->cmpts) {
+        jas_free(dec->cmpts);
+    }
 
-	if (dec->tiles) {
-		jas_free(dec->tiles);
-	}
+    if (dec->tiles) {
+        jas_free(dec->tiles);
+    }
 
-	jas_free(dec);
+    jas_free(dec);
 }
 
 /******************************************************************************\
@@ -1940,395 +1942,411 @@ static void jpc_dec_destroy(jpc_dec_t *dec)
 
 void jpc_seglist_insert(jpc_dec_seglist_t *list, jpc_dec_seg_t *ins, jpc_dec_seg_t *node)
 {
-	jpc_dec_seg_t *prev;
-	jpc_dec_seg_t *next;
-
-	prev = ins;
-	node->prev = prev;
-	next = prev ? (prev->next) : 0;
-	node->prev = prev;
-	node->next = next;
-	if (prev) {
-		prev->next = node;
-	} else {
-		list->head = node;
-	}
-	if (next) {
-		next->prev = node;
-	} else {
-		list->tail = node;
-	}
+    jpc_dec_seg_t *prev;
+    jpc_dec_seg_t *next;
+
+    prev = ins;
+    node->prev = prev;
+    next = prev ? (prev->next) : 0;
+    node->prev = prev;
+    node->next = next;
+    if (prev) {
+        prev->next = node;
+    } else {
+        list->head = node;
+    }
+    if (next) {
+        next->prev = node;
+    } else {
+        list->tail = node;
+    }
 }
 
 void jpc_seglist_remove(jpc_dec_seglist_t *list, jpc_dec_seg_t *seg)
 {
-	jpc_dec_seg_t *prev;
-	jpc_dec_seg_t *next;
-
-	prev = seg->prev;
-	next = seg->next;
-	if (prev) {
-		prev->next = next;
-	} else {
-		list->head = next;
-	}
-	if (next) {
-		next->prev = prev;
-	} else {
-		list->tail = prev;
-	}
-	seg->prev = 0;
-	seg->next = 0;
+    jpc_dec_seg_t *prev;
+    jpc_dec_seg_t *next;
+
+    prev = seg->prev;
+    next = seg->next;
+    if (prev) {
+        prev->next = next;
+    } else {
+        list->head = next;
+    }
+    if (next) {
+        next->prev = prev;
+    } else {
+        list->tail = prev;
+    }
+    seg->prev = 0;
+    seg->next = 0;
 }
 
 jpc_dec_seg_t *jpc_seg_alloc(void)
 {
-	jpc_dec_seg_t *seg;
+    jpc_dec_seg_t *seg;
 
-	if (!(seg = jas_malloc(sizeof(jpc_dec_seg_t)))) {
-		return 0;
-	}
-	seg->prev = 0;
-	seg->next = 0;
-	seg->passno = -1;
-	seg->numpasses = 0;
-	seg->maxpasses = 0;
-	seg->type = JPC_SEG_INVALID;
-	seg->stream = 0;
-	seg->cnt = 0;
-	seg->complete = 0;
-	seg->lyrno = -1;
-	return seg;
+    if (!(seg = jas_malloc(sizeof(jpc_dec_seg_t)))) {
+        return 0;
+    }
+    seg->prev = 0;
+    seg->next = 0;
+    seg->passno = -1;
+    seg->numpasses = 0;
+    seg->maxpasses = 0;
+    seg->type = JPC_SEG_INVALID;
+    seg->stream = 0;
+    seg->cnt = 0;
+    seg->complete = 0;
+    seg->lyrno = -1;
+    return seg;
 }
 
 void jpc_seg_destroy(jpc_dec_seg_t *seg)
 {
-	if (seg->stream) {
-		jas_stream_close(seg->stream);
-	}
-	jas_free(seg);
+    if (seg->stream) {
+        jas_stream_close(seg->stream);
+    }
+    jas_free(seg);
 }
 
 static int jpc_dec_dump(jpc_dec_t *dec, FILE *out)
 {
-	jpc_dec_tile_t *tile;
-	int tileno;
-	jpc_dec_tcomp_t *tcomp;
-	uint_fast16_t compno;
-	jpc_dec_rlvl_t *rlvl;
-	int rlvlno;
-	jpc_dec_band_t *band;
-	int bandno;
-	jpc_dec_prc_t *prc;
-	int prcno;
-	jpc_dec_cblk_t *cblk;
-	int cblkno;
-
-	for (tileno = 0, tile = dec->tiles; tileno < dec->numtiles;
-	  ++tileno, ++tile) {
-		for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
-		  ++compno, ++tcomp) {
-			for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno <
-			  tcomp->numrlvls; ++rlvlno, ++rlvl) {
-fprintf(out, "RESOLUTION LEVEL %d\n", rlvlno);
-fprintf(out, "xs =%d, ys = %d, xe = %d, ye = %d, w = %d, h = %d\n",
-  rlvl->xstart, rlvl->ystart, rlvl->xend, rlvl->yend, rlvl->xend -
-  rlvl->xstart, rlvl->yend - rlvl->ystart);
-				for (bandno = 0, band = rlvl->bands;
-				  bandno < rlvl->numbands; ++bandno, ++band) {
-fprintf(out, "BAND %d\n", bandno);
-fprintf(out, "xs =%d, ys = %d, xe = %d, ye = %d, w = %d, h = %d\n",
-  jas_seq2d_xstart(band->data), jas_seq2d_ystart(band->data), jas_seq2d_xend(band->data),
-  jas_seq2d_yend(band->data), jas_seq2d_xend(band->data) - jas_seq2d_xstart(band->data),
-  jas_seq2d_yend(band->data) - jas_seq2d_ystart(band->data));
-					for (prcno = 0, prc = band->prcs;
-					  prcno < rlvl->numprcs; ++prcno,
-					  ++prc) {
-fprintf(out, "CODE BLOCK GROUP %d\n", prcno);
-fprintf(out, "xs =%d, ys = %d, xe = %d, ye = %d, w = %d, h = %d\n",
-  prc->xstart, prc->ystart, prc->xend, prc->yend, prc->xend -
-  prc->xstart, prc->yend - prc->ystart);
-						for (cblkno = 0, cblk =
-						  prc->cblks; cblkno <
-						  prc->numcblks; ++cblkno,
-						  ++cblk) {
-fprintf(out, "CODE BLOCK %d\n", cblkno);
-fprintf(out, "xs =%d, ys = %d, xe = %d, ye = %d, w = %d, h = %d\n",
-  jas_seq2d_xstart(cblk->data), jas_seq2d_ystart(cblk->data), jas_seq2d_xend(cblk->data),
-  jas_seq2d_yend(cblk->data), jas_seq2d_xend(cblk->data) - jas_seq2d_xstart(cblk->data),
-  jas_seq2d_yend(cblk->data) - jas_seq2d_ystart(cblk->data));
-						}
-					}
-				}
-			}
-		}
-	}
-
-	return 0;
+    jpc_dec_tile_t *tile;
+    int tileno;
+    jpc_dec_tcomp_t *tcomp;
+    uint_fast16_t compno;
+    jpc_dec_rlvl_t *rlvl;
+    int rlvlno;
+    jpc_dec_band_t *band;
+    int bandno;
+    jpc_dec_prc_t *prc;
+    int prcno;
+    jpc_dec_cblk_t *cblk;
+    int cblkno;
+
+    for (tileno = 0, tile = dec->tiles; tileno < dec->numtiles;
+      ++tileno, ++tile) {
+        for (compno = 0, tcomp = tile->tcomps; compno < dec->numcomps;
+          ++compno, ++tcomp) {
+            for (rlvlno = 0, rlvl = tcomp->rlvls; rlvlno <
+              tcomp->numrlvls; ++rlvlno, ++rlvl) {
+                fprintf(out, "RESOLUTION LEVEL %d\n", rlvlno);
+                fprintf(out, "xs =%d, ys = %d, xe = %d, ye = %d, "
+                        "w = %d, h = %d\n",
+                        (int)rlvl->xstart, (int)rlvl->ystart,
+                        (int)rlvl->xend, (int)rlvl->yend,
+                        (int)(rlvl->xend - rlvl->xstart),
+                        (int)(rlvl->yend - rlvl->ystart));
+                for (bandno = 0, band = rlvl->bands;
+                     bandno < rlvl->numbands; ++bandno, ++band) {
+                    fprintf(out, "BAND %d\n", bandno);
+                    fprintf(out, "xs =%d, ys = %d, xe = %d, ye = %d, "
+                            "w = %d, h = %d\n",
+                            (int)jas_seq2d_xstart(band->data),
+                            (int)jas_seq2d_ystart(band->data),
+                            (int)jas_seq2d_xend(band->data),
+                            (int)jas_seq2d_yend(band->data),
+                            (int)(jas_seq2d_xend(band->data) -
+                                  jas_seq2d_xstart(band->data)),
+                            (int)(jas_seq2d_yend(band->data) -
+                                  jas_seq2d_ystart(band->data)));
+                    for (prcno = 0, prc = band->prcs;
+                         prcno < rlvl->numprcs;
+                         ++prcno, ++prc) {
+                        fprintf(out, "CODE BLOCK GROUP %d\n", prcno);
+                        fprintf(out, "xs =%d, ys = %d, xe = %d, ye = %d, "
+                                "w = %d, h = %d\n",
+                                (int)prc->xstart, (int)prc->ystart,
+                                (int)prc->xend, (int)prc->yend,
+                                (int)(prc->xend - prc->xstart),
+                                (int)(prc->yend - prc->ystart));
+                        for (cblkno = 0, cblk = prc->cblks;
+                             cblkno < prc->numcblks;
+                             ++cblkno, ++cblk) {
+                            fprintf(out, "CODE BLOCK %d\n", cblkno);
+                            fprintf(out, "xs =%d, ys = %d, xe = %d, ye = %d, "
+                                    "w = %d, h = %d\n",
+                                    (int)jas_seq2d_xstart(cblk->data),
+                                    (int)jas_seq2d_ystart(cblk->data),
+                                    (int)jas_seq2d_xend(cblk->data),
+                                    (int)jas_seq2d_yend(cblk->data),
+                                    (int)(jas_seq2d_xend(cblk->data) -
+                                          jas_seq2d_xstart(cblk->data)),
+                                    (int)(jas_seq2d_yend(cblk->data) -
+                                          jas_seq2d_ystart(cblk->data)));
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return 0;
 }
 
 jpc_streamlist_t *jpc_streamlist_create()
 {
-	jpc_streamlist_t *streamlist;
-	int i;
-
-	if (!(streamlist = jas_malloc(sizeof(jpc_streamlist_t)))) {
-		return 0;
-	}
-	streamlist->numstreams = 0;
-	streamlist->maxstreams = 100;
-	if (!(streamlist->streams = jas_malloc(streamlist->maxstreams *
-	  sizeof(jas_stream_t *)))) {
-		jas_free(streamlist);
-		return 0;
-	}
-	for (i = 0; i < streamlist->maxstreams; ++i) {
-		streamlist->streams[i] = 0;
-	}
-	return streamlist;
+    jpc_streamlist_t *streamlist;
+    int i;
+
+    if (!(streamlist = jas_malloc(sizeof(jpc_streamlist_t)))) {
+        return 0;
+    }
+    streamlist->numstreams = 0;
+    streamlist->maxstreams = 100;
+    if (!(streamlist->streams = jas_malloc(streamlist->maxstreams *
+      sizeof(jas_stream_t *)))) {
+        jas_free(streamlist);
+        return 0;
+    }
+    for (i = 0; i < streamlist->maxstreams; ++i) {
+        streamlist->streams[i] = 0;
+    }
+    return streamlist;
 }
 
 int jpc_streamlist_insert(jpc_streamlist_t *streamlist, int streamno,
   jas_stream_t *stream)
 {
-	jas_stream_t **newstreams;
-	int newmaxstreams;
-	int i;
-	/* Grow the array of streams if necessary. */
-	if (streamlist->numstreams >= streamlist->maxstreams) {
-		newmaxstreams = streamlist->maxstreams + 1024;
-		if (!(newstreams = jas_realloc(streamlist->streams,
-		  (newmaxstreams + 1024) * sizeof(jas_stream_t *)))) {
-			return -1;
-		}
-		for (i = streamlist->numstreams; i < streamlist->maxstreams; ++i) {
-			streamlist->streams[i] = 0;
-		}
-		streamlist->maxstreams = newmaxstreams;
-		streamlist->streams = newstreams;
-	}
-	if (streamno != streamlist->numstreams) {
-		/* Can only handle insertion at start of list. */
-		return -1;
-	}
-	streamlist->streams[streamno] = stream;
-	++streamlist->numstreams;
-	return 0;
+    jas_stream_t **newstreams;
+    int newmaxstreams;
+    int i;
+    /* Grow the array of streams if necessary. */
+    if (streamlist->numstreams >= streamlist->maxstreams) {
+        newmaxstreams = streamlist->maxstreams + 1024;
+        if (!(newstreams = jas_realloc(streamlist->streams,
+          (newmaxstreams + 1024) * sizeof(jas_stream_t *)))) {
+            return -1;
+        }
+        for (i = streamlist->numstreams; i < streamlist->maxstreams; ++i) {
+            streamlist->streams[i] = 0;
+        }
+        streamlist->maxstreams = newmaxstreams;
+        streamlist->streams = newstreams;
+    }
+    if (streamno != streamlist->numstreams) {
+        /* Can only handle insertion at start of list. */
+        return -1;
+    }
+    streamlist->streams[streamno] = stream;
+    ++streamlist->numstreams;
+    return 0;
 }
 
 jas_stream_t *jpc_streamlist_remove(jpc_streamlist_t *streamlist, int streamno)
 {
-	jas_stream_t *stream;
-	int i;
-	if (streamno >= streamlist->numstreams) {
-		abort();
-	}
-	stream = streamlist->streams[streamno];
-	for (i = streamno + 1; i < streamlist->numstreams; ++i) {
-		streamlist->streams[i - 1] = streamlist->streams[i];
-	}
-	--streamlist->numstreams;
-	return stream;
+    jas_stream_t *stream;
+    int i;
+    if (streamno >= streamlist->numstreams) {
+        abort();
+    }
+    stream = streamlist->streams[streamno];
+    for (i = streamno + 1; i < streamlist->numstreams; ++i) {
+        streamlist->streams[i - 1] = streamlist->streams[i];
+    }
+    --streamlist->numstreams;
+    return stream;
 }
 
 void jpc_streamlist_destroy(jpc_streamlist_t *streamlist)
 {
-	int streamno;
-	if (streamlist->streams) {
-		for (streamno = 0; streamno < streamlist->numstreams;
-		  ++streamno) {
-			jas_stream_close(streamlist->streams[streamno]);
-		}
-		jas_free(streamlist->streams);
-	}
-	jas_free(streamlist);
+    int streamno;
+    if (streamlist->streams) {
+        for (streamno = 0; streamno < streamlist->numstreams;
+          ++streamno) {
+            jas_stream_close(streamlist->streams[streamno]);
+        }
+        jas_free(streamlist->streams);
+    }
+    jas_free(streamlist);
 }
 
 jas_stream_t *jpc_streamlist_get(jpc_streamlist_t *streamlist, int streamno)
 {
-	assert(streamno < streamlist->numstreams);
-	return streamlist->streams[streamno];
+    assert(streamno < streamlist->numstreams);
+    return streamlist->streams[streamno];
 }
 
 int jpc_streamlist_numstreams(jpc_streamlist_t *streamlist)
 {
-	return streamlist->numstreams;
+    return streamlist->numstreams;
 }
 
 jpc_ppxstab_t *jpc_ppxstab_create()
 {
-	jpc_ppxstab_t *tab;
+    jpc_ppxstab_t *tab;
 
-	if (!(tab = jas_malloc(sizeof(jpc_ppxstab_t)))) {
-		return 0;
-	}
-	tab->numents = 0;
-	tab->maxents = 0;
-	tab->ents = 0;
-	return tab;
+    if (!(tab = jas_malloc(sizeof(jpc_ppxstab_t)))) {
+        return 0;
+    }
+    tab->numents = 0;
+    tab->maxents = 0;
+    tab->ents = 0;
+    return tab;
 }
 
 void jpc_ppxstab_destroy(jpc_ppxstab_t *tab)
 {
-	int i;
-	for (i = 0; i < tab->numents; ++i) {
-		jpc_ppxstabent_destroy(tab->ents[i]);
-	}
-	if (tab->ents) {
-		jas_free(tab->ents);
-	}
-	jas_free(tab);
+    int i;
+    for (i = 0; i < tab->numents; ++i) {
+        jpc_ppxstabent_destroy(tab->ents[i]);
+    }
+    if (tab->ents) {
+        jas_free(tab->ents);
+    }
+    jas_free(tab);
 }
 
 int jpc_ppxstab_grow(jpc_ppxstab_t *tab, int maxents)
 {
-	jpc_ppxstabent_t **newents;
-	if (tab->maxents < maxents) {
-		newents = (tab->ents) ? jas_realloc(tab->ents, maxents *
-		  sizeof(jpc_ppxstabent_t *)) : jas_malloc(maxents * sizeof(jpc_ppxstabent_t *));
-		if (!newents) {
-			return -1;
-		}
-		tab->ents = newents;
-		tab->maxents = maxents;
-	}
-	return 0;
+    jpc_ppxstabent_t **newents;
+    if (tab->maxents < maxents) {
+        newents = (tab->ents) ? jas_realloc(tab->ents, maxents *
+          sizeof(jpc_ppxstabent_t *)) : jas_malloc(maxents * sizeof(jpc_ppxstabent_t *));
+        if (!newents) {
+            return -1;
+        }
+        tab->ents = newents;
+        tab->maxents = maxents;
+    }
+    return 0;
 }
 
 int jpc_ppxstab_insert(jpc_ppxstab_t *tab, jpc_ppxstabent_t *ent)
 {
-	int inspt;
-	int i;
+    int inspt;
+    int i;
 
-	for (i = 0; i < tab->numents; ++i) {
-		if (tab->ents[i]->ind > ent->ind) {
-			break;
-		}
-	}
-	inspt = i;
+    for (i = 0; i < tab->numents; ++i) {
+        if (tab->ents[i]->ind > ent->ind) {
+            break;
+        }
+    }
+    inspt = i;
 
-	if (tab->numents >= tab->maxents) {
-		if (jpc_ppxstab_grow(tab, tab->maxents + 128)) {
-			return -1;
-		}
-	}
+    if (tab->numents >= tab->maxents) {
+        if (jpc_ppxstab_grow(tab, tab->maxents + 128)) {
+            return -1;
+        }
+    }
 
-	for (i = tab->numents; i > inspt; --i) {
-		tab->ents[i] = tab->ents[i - 1];
-	}
-	tab->ents[i] = ent;
-	++tab->numents;
+    for (i = tab->numents; i > inspt; --i) {
+        tab->ents[i] = tab->ents[i - 1];
+    }
+    tab->ents[i] = ent;
+    ++tab->numents;
 
-	return 0;
+    return 0;
 }
 
 jpc_streamlist_t *jpc_ppmstabtostreams(jpc_ppxstab_t *tab)
 {
-	jpc_streamlist_t *streams;
-	unsigned char *dataptr;
-	uint_fast32_t datacnt;
-	uint_fast32_t tpcnt;
-	jpc_ppxstabent_t *ent;
-	int entno;
-	jas_stream_t *stream;
-	int n;
-
-	if (!(streams = jpc_streamlist_create())) {
-		goto error;
-	}
-
-	if (!tab->numents) {
-		return streams;
-	}
-
-	entno = 0;
-	ent = tab->ents[entno];
-	dataptr = ent->data;
-	datacnt = ent->len;
-	for (;;) {
-
-		/* Get the length of the packet header data for the current
-		  tile-part. */
-		if (datacnt < 4) {
-			goto error;
-		}
-		if (!(stream = jas_stream_memopen(0, 0))) {
-			goto error;
-		}
-		if (jpc_streamlist_insert(streams, jpc_streamlist_numstreams(streams),
-		  stream)) {
-			goto error;
-		}
-		tpcnt = (dataptr[0] << 24) | (dataptr[1] << 16) | (dataptr[2] << 8)
-		  | dataptr[3];
-		datacnt -= 4;
-		dataptr += 4;
-
-		/* Get the packet header data for the current tile-part. */
-		while (tpcnt) {
-			if (!datacnt) {
-				if (++entno >= tab->numents) {
-					goto error;
-				}
-				ent = tab->ents[entno];
-				dataptr = ent->data;
-				datacnt = ent->len;
-			}
-			n = JAS_MIN(tpcnt, datacnt);
-			if (jas_stream_write(stream, dataptr, n) != n) {
-				goto error;
-			}
-			tpcnt -= n;
-			dataptr += n;
-			datacnt -= n;
-		}
-		jas_stream_rewind(stream);
-		if (!datacnt) {
-			if (++entno >= tab->numents) {
-				break;
-			}
-			ent = tab->ents[entno];
-			dataptr = ent->data;
-			datacnt = ent->len;
-		}
-	}
-
-	return streams;
+    jpc_streamlist_t *streams;
+    unsigned char *dataptr;
+    uint_fast32_t datacnt;
+    uint_fast32_t tpcnt;
+    jpc_ppxstabent_t *ent;
+    int entno;
+    jas_stream_t *stream;
+    int n;
+
+    if (!(streams = jpc_streamlist_create())) {
+        goto error;
+    }
+
+    if (!tab->numents) {
+        return streams;
+    }
+
+    entno = 0;
+    ent = tab->ents[entno];
+    dataptr = ent->data;
+    datacnt = ent->len;
+    for (;;) {
+
+        /* Get the length of the packet header data for the current
+          tile-part. */
+        if (datacnt < 4) {
+            goto error;
+        }
+        if (!(stream = jas_stream_memopen(0, 0))) {
+            goto error;
+        }
+        if (jpc_streamlist_insert(streams, jpc_streamlist_numstreams(streams),
+          stream)) {
+            goto error;
+        }
+        tpcnt = (dataptr[0] << 24) | (dataptr[1] << 16) | (dataptr[2] << 8)
+          | dataptr[3];
+        datacnt -= 4;
+        dataptr += 4;
+
+        /* Get the packet header data for the current tile-part. */
+        while (tpcnt) {
+            if (!datacnt) {
+                if (++entno >= tab->numents) {
+                    goto error;
+                }
+                ent = tab->ents[entno];
+                dataptr = ent->data;
+                datacnt = ent->len;
+            }
+            n = JAS_MIN(tpcnt, datacnt);
+            if (jas_stream_write(stream, dataptr, n) != n) {
+                goto error;
+            }
+            tpcnt -= n;
+            dataptr += n;
+            datacnt -= n;
+        }
+        jas_stream_rewind(stream);
+        if (!datacnt) {
+            if (++entno >= tab->numents) {
+                break;
+            }
+            ent = tab->ents[entno];
+            dataptr = ent->data;
+            datacnt = ent->len;
+        }
+    }
+
+    return streams;
 
 error:
-	jpc_streamlist_destroy(streams);
-	return 0;
+    jpc_streamlist_destroy(streams);
+    return 0;
 }
 
 int jpc_pptstabwrite(jas_stream_t *out, jpc_ppxstab_t *tab)
 {
-	int i;
-	jpc_ppxstabent_t *ent;
-	for (i = 0; i < tab->numents; ++i) {
-		ent = tab->ents[i];
-		if (jas_stream_write(out, ent->data, ent->len) != ent->len) {
-			return -1;
-		}
-	}
-	return 0;
+    int i;
+    jpc_ppxstabent_t *ent;
+    for (i = 0; i < tab->numents; ++i) {
+        ent = tab->ents[i];
+        if (jas_stream_write(out, ent->data, ent->len) != ent->len) {
+            return -1;
+        }
+    }
+    return 0;
 }
 
 jpc_ppxstabent_t *jpc_ppxstabent_create()
 {
-	jpc_ppxstabent_t *ent;
-	if (!(ent = jas_malloc(sizeof(jpc_ppxstabent_t)))) {
-		return 0;
-	}
-	ent->data = 0;
-	ent->len = 0;
-	ent->ind = 0;
-	return ent;
+    jpc_ppxstabent_t *ent;
+    if (!(ent = jas_malloc(sizeof(jpc_ppxstabent_t)))) {
+        return 0;
+    }
+    ent->data = 0;
+    ent->len = 0;
+    ent->ind = 0;
+    return ent;
 }
 
 void jpc_ppxstabent_destroy(jpc_ppxstabent_t *ent)
 {
-	if (ent->data) {
-		jas_free(ent->data);
-	}
-	jas_free(ent);
+    if (ent->data) {
+        jas_free(ent->data);
+    }
+    jas_free(ent);
 }
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_dec.h b/converter/other/jpeg2000/libjasper/jpc/jpc_dec.h
index 5231048d..02c5553d 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_dec.h
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_dec.h
@@ -175,7 +175,7 @@ typedef struct {
 	/* The number of streams in this list. */
 	int numstreams;
 
-	/* The maximum number of streams that can be accomodated without
+	/* The maximum number of streams that can be accommodated without
 	  growing the streams array. */
 	int maxstreams;
 
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c b/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c
index 3284dfeb..d17e9aa3 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c
@@ -1,129 +1,13 @@
-/*
- * 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__
- */
-
-/*
- * $Id$
- */
-
-/******************************************************************************\
-* Includes.
-\******************************************************************************/
-
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
 #include <math.h>
 #include <float.h>
 
+#include "netpbm/pm.h"
+#include "netpbm/nstring.h"
+
 #include "jasper/jas_string.h"
 #include "jasper/jas_malloc.h"
 #include "jasper/jas_image.h"
@@ -147,24 +31,24 @@
 #include "jpc_math.h"
 #include "jpc_util.h"
 
-/******************************************************************************\
+/*****************************************************************************\
 *
-\******************************************************************************/
+\*****************************************************************************/
 
 #define JPC_POW2(n) \
-	(1 << (n))
+    (1 << (n))
 
 #define JPC_FLOORTOMULTPOW2(x, n) \
   (((n) > 0) ? ((x) & (~((1 << n) - 1))) : (x))
 /* Round to the nearest multiple of the specified power of two in the
   direction of negative infinity. */
 
-#define	JPC_CEILTOMULTPOW2(x, n) \
+#define JPC_CEILTOMULTPOW2(x, n) \
   (((n) > 0) ? JPC_FLOORTOMULTPOW2(((x) + (1 << (n)) - 1), n) : (x))
 /* Round to the nearest multiple of the specified power of two in the
   direction of positive infinity. */
 
-#define	JPC_POW2(n)	\
+#define JPC_POW2(n) \
   (1 << (n))
 
 jpc_enc_tile_t *jpc_enc_tile_create(jpc_enc_cp_t *cp, jas_image_t *image, int tileno);
@@ -202,180 +86,205 @@ void jpc_enc_destroy(jpc_enc_t *enc);
 static int jpc_enc_encodemainhdr(jpc_enc_t *enc);
 static int jpc_enc_encodemainbody(jpc_enc_t *enc);
 int jpc_enc_encodetiledata(jpc_enc_t *enc);
-int rateallocate(jpc_enc_t *enc, int numlyrs, uint_fast32_t *cumlens);
 int setins(int numvalues, jpc_flt_t *values, jpc_flt_t value);
 static jpc_enc_cp_t *cp_create(char *optstr, jas_image_t *image);
 void jpc_enc_cp_destroy(jpc_enc_cp_t *cp);
 
 static uint_fast32_t jpc_abstorelstepsize(jpc_fix_t absdelta, int scaleexpn)
 {
-	int p;
-	uint_fast32_t mant;
-	uint_fast32_t expn;
-	int n;
-
-	if (absdelta < 0) {
-		abort();
-	}
-
-	p = jpc_firstone(absdelta) - JPC_FIX_FRACBITS;
-	n = 11 - jpc_firstone(absdelta);
-	mant = ((n < 0) ? (absdelta >> (-n)) : (absdelta << n)) & 0x7ff;
-	expn = scaleexpn - p;
-	if (scaleexpn < p) {
-		abort();
-	}
-	return JPC_QCX_EXPN(expn) | JPC_QCX_MANT(mant);
+    int p;
+    uint_fast32_t mant;
+    uint_fast32_t expn;
+    int n;
+
+    if (absdelta < 0) {
+        abort();
+    }
+
+    p = jpc_firstone(absdelta) - JPC_FIX_FRACBITS;
+    n = 11 - jpc_firstone(absdelta);
+    mant = ((n < 0) ? (absdelta >> (-n)) : (absdelta << n)) & 0x7ff;
+    expn = scaleexpn - p;
+    if (scaleexpn < p) {
+        abort();
+    }
+    return JPC_QCX_EXPN(expn) | JPC_QCX_MANT(mant);
 }
 
 typedef enum {
-	OPT_DEBUG,
-	OPT_IMGAREAOFFX,
-	OPT_IMGAREAOFFY,
-	OPT_TILEGRDOFFX,
-	OPT_TILEGRDOFFY,
-	OPT_TILEWIDTH,
-	OPT_TILEHEIGHT,
-	OPT_PRCWIDTH,
-	OPT_PRCHEIGHT,
-	OPT_CBLKWIDTH,
-	OPT_CBLKHEIGHT,
-	OPT_MODE,
-	OPT_PRG,
-	OPT_NOMCT,
-	OPT_MAXRLVLS,
-	OPT_SOP,
-	OPT_EPH,
-	OPT_LAZY,
-	OPT_TERMALL,
-	OPT_SEGSYM,
-	OPT_VCAUSAL,
-	OPT_RESET,
-	OPT_PTERM,
-	OPT_NUMGBITS,
-	OPT_RATE,
-	OPT_ILYRRATES,
-	OPT_JP2OVERHEAD
+    OPT_DEBUG,
+    OPT_IMGAREAOFFX,
+    OPT_IMGAREAOFFY,
+    OPT_TILEGRDOFFX,
+    OPT_TILEGRDOFFY,
+    OPT_TILEWIDTH,
+    OPT_TILEHEIGHT,
+    OPT_PRCWIDTH,
+    OPT_PRCHEIGHT,
+    OPT_CBLKWIDTH,
+    OPT_CBLKHEIGHT,
+    OPT_MODE,
+    OPT_PRG,
+    OPT_NOMCT,
+    OPT_MAXRLVLS,
+    OPT_SOP,
+    OPT_EPH,
+    OPT_LAZY,
+    OPT_TERMALL,
+    OPT_SEGSYM,
+    OPT_VCAUSAL,
+    OPT_RESET,
+    OPT_PTERM,
+    OPT_NUMGBITS,
+    OPT_RATE,
+    OPT_ILYRRATES,
+    OPT_JP2OVERHEAD
 } optid_t;
 
 jas_taginfo_t encopts[] = {
-	{OPT_DEBUG, "debug"},
-	{OPT_IMGAREAOFFX, "imgareatlx"},
-	{OPT_IMGAREAOFFY, "imgareatly"},
-	{OPT_TILEGRDOFFX, "tilegrdtlx"},
-	{OPT_TILEGRDOFFY, "tilegrdtly"},
-	{OPT_TILEWIDTH, "tilewidth"},
-	{OPT_TILEHEIGHT, "tileheight"},
-	{OPT_PRCWIDTH, "prcwidth"},
-	{OPT_PRCHEIGHT, "prcheight"},
-	{OPT_CBLKWIDTH, "cblkwidth"},
-	{OPT_CBLKHEIGHT, "cblkheight"},
-	{OPT_MODE, "mode"},
-	{OPT_PRG, "prg"},
-	{OPT_NOMCT, "nomct"},
-	{OPT_MAXRLVLS, "numrlvls"},
-	{OPT_SOP, "sop"},
-	{OPT_EPH, "eph"},
-	{OPT_LAZY, "lazy"},
-	{OPT_TERMALL, "termall"},
-	{OPT_SEGSYM, "segsym"},
-	{OPT_VCAUSAL, "vcausal"},
-	{OPT_PTERM, "pterm"},
-	{OPT_RESET, "resetprob"},
-	{OPT_NUMGBITS, "numgbits"},
-	{OPT_RATE, "rate"},
-	{OPT_ILYRRATES, "ilyrrates"},
-	{OPT_JP2OVERHEAD, "_jp2overhead"},
-	{-1, 0}
+    {OPT_DEBUG, "debug"},
+    {OPT_IMGAREAOFFX, "imgareatlx"},
+    {OPT_IMGAREAOFFY, "imgareatly"},
+    {OPT_TILEGRDOFFX, "tilegrdtlx"},
+    {OPT_TILEGRDOFFY, "tilegrdtly"},
+    {OPT_TILEWIDTH, "tilewidth"},
+    {OPT_TILEHEIGHT, "tileheight"},
+    {OPT_PRCWIDTH, "prcwidth"},
+    {OPT_PRCHEIGHT, "prcheight"},
+    {OPT_CBLKWIDTH, "cblkwidth"},
+    {OPT_CBLKHEIGHT, "cblkheight"},
+    {OPT_MODE, "mode"},
+    {OPT_PRG, "prg"},
+    {OPT_NOMCT, "nomct"},
+    {OPT_MAXRLVLS, "numrlvls"},
+    {OPT_SOP, "sop"},
+    {OPT_EPH, "eph"},
+    {OPT_LAZY, "lazy"},
+    {OPT_TERMALL, "termall"},
+    {OPT_SEGSYM, "segsym"},
+    {OPT_VCAUSAL, "vcausal"},
+    {OPT_PTERM, "pterm"},
+    {OPT_RESET, "resetprob"},
+    {OPT_NUMGBITS, "numgbits"},
+    {OPT_RATE, "rate"},
+    {OPT_ILYRRATES, "ilyrrates"},
+    {OPT_JP2OVERHEAD, "_jp2overhead"},
+    {-1, 0}
 };
 
 typedef enum {
-	PO_L = 0,
-	PO_R
+    PO_L = 0,
+    PO_R
 } poid_t;
 
 
 jas_taginfo_t prgordtab[] = {
-	{JPC_COD_LRCPPRG, "lrcp"},
-	{JPC_COD_RLCPPRG, "rlcp"},
-	{JPC_COD_RPCLPRG, "rpcl"},
-	{JPC_COD_PCRLPRG, "pcrl"},
-	{JPC_COD_CPRLPRG, "cprl"},
-	{-1, 0}
+    {JPC_COD_LRCPPRG, "lrcp"},
+    {JPC_COD_RLCPPRG, "rlcp"},
+    {JPC_COD_RPCLPRG, "rpcl"},
+    {JPC_COD_PCRLPRG, "pcrl"},
+    {JPC_COD_CPRLPRG, "cprl"},
+    {-1, 0}
 };
 
 typedef enum {
-	MODE_INT,
-	MODE_REAL
+    MODE_INT,
+    MODE_REAL
 } modeid_t;
 
 jas_taginfo_t modetab[] = {
-	{MODE_INT, "int"},
-	{MODE_REAL, "real"},
-	{-1, 0}
+    {MODE_INT, "int"},
+    {MODE_REAL, "real"},
+    {-1, 0}
 };
 
+
+static void
+tracev(const char * const fmt,
+       va_list            args) {
+
+    vfprintf(stderr, fmt, args);
+
+    fprintf(stderr, "\n");
+}
+
+
+
+static void
+trace(const char * const fmt, ...) {
+
+    if (jas_getdbglevel() > 0) {
+        va_list args;
+
+        va_start(args, fmt);
+        tracev(fmt, args);
+        va_end(args);
+    }
+}
+
+
+
 /******************************************************************************\
 * The main encoder entry point.
 \******************************************************************************/
 
 int jpc_encode(jas_image_t *image, jas_stream_t *out, char *optstr)
 {
-	jpc_enc_t *enc;
-	jpc_enc_cp_t *cp;
+    jpc_enc_t *enc;
+    jpc_enc_cp_t *cp;
 
-	enc = 0;
-	cp = 0;
+    enc = 0;
+    cp = 0;
 
-	jpc_initluts();
+    jpc_initluts();
 
-	if (!(cp = cp_create(optstr, image))) {
-		fprintf(stderr, "invalid JP encoder options\n");
-		goto error;
-	}
+    if (!(cp = cp_create(optstr, image))) {
+        fprintf(stderr, "invalid JP encoder options\n");
+        goto error;
+    }
 
-	if (!(enc = jpc_enc_create(cp, out, image))) {
-		goto error;
-	}
-	cp = 0;
+    if (!(enc = jpc_enc_create(cp, out, image))) {
+        goto error;
+    }
+    cp = 0;
 
-	/* Encode the main header. */
-	if (jpc_enc_encodemainhdr(enc)) {
-		goto error;
-	}
+    /* Encode the main header. */
+    if (jpc_enc_encodemainhdr(enc)) {
+        goto error;
+    }
 
-	/* Encode the main body.  This constitutes most of the encoding work. */
-	if (jpc_enc_encodemainbody(enc)) {
-		goto error;
-	}
+    /* Encode the main body.  This constitutes most of the encoding work. */
+    if (jpc_enc_encodemainbody(enc)) {
+        goto error;
+    }
 
-	/* Write EOC marker segment. */
-	if (!(enc->mrk = jpc_ms_create(JPC_MS_EOC))) {
-		goto error;
-	}
-	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
-		fprintf(stderr, "cannot write EOI marker\n");
-		goto error;
-	}
-	jpc_ms_destroy(enc->mrk);
-	enc->mrk = 0;
+    /* Write EOC marker segment. */
+    if (!(enc->mrk = jpc_ms_create(JPC_MS_EOC))) {
+        goto error;
+    }
+    if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+        fprintf(stderr, "cannot write EOI marker\n");
+        goto error;
+    }
+    jpc_ms_destroy(enc->mrk);
+    enc->mrk = 0;
 
-	if (jas_stream_flush(enc->out)) {
-		goto error;
-	}
+    if (jas_stream_flush(enc->out)) {
+        goto error;
+    }
 
-	jpc_enc_destroy(enc);
+    jpc_enc_destroy(enc);
 
-	return 0;
+    return 0;
 
 error:
-	if (cp) {
-		jpc_enc_cp_destroy(cp);
-	}
-	if (enc) {
-		jpc_enc_destroy(enc);
-	}
-	return -1;
+    if (cp) {
+        jpc_enc_cp_destroy(cp);
+    }
+    if (enc) {
+        jpc_enc_destroy(enc);
+    }
+    return -1;
 }
 
 /******************************************************************************\
@@ -384,466 +293,461 @@ error:
 
 static jpc_enc_cp_t *cp_create(char *optstr, jas_image_t *image)
 {
-	jpc_enc_cp_t *cp;
-	jas_tvparser_t *tvp;
-	int ret;
-	int numilyrrates;
-	double *ilyrrates;
-	int i;
-	int tagid;
-	jpc_enc_tcp_t *tcp;
-	jpc_enc_tccp_t *tccp;
-	jpc_enc_ccp_t *ccp;
-	uint_fast16_t cmptno;
-	uint_fast16_t rlvlno;
-	uint_fast16_t prcwidthexpn;
-	uint_fast16_t prcheightexpn;
-	bool enablemct;
-	uint_fast32_t jp2overhead;
-	uint_fast16_t lyrno;
-	uint_fast32_t hsteplcm;
-	uint_fast32_t vsteplcm;
-	bool mctvalid;
-
-	tvp = 0;
-	cp = 0;
-	ilyrrates = 0;
-	numilyrrates = 0;
-
-	if (!(cp = jas_malloc(sizeof(jpc_enc_cp_t)))) {
-		goto error;
-	}
-
-	prcwidthexpn = 15;
-	prcheightexpn = 15;
-	enablemct = true;
-	jp2overhead = 0;
-
-	cp->ccps = 0;
-	cp->debug = 0;
-	cp->imgareatlx = UINT_FAST32_MAX;
-	cp->imgareatly = UINT_FAST32_MAX;
-	cp->refgrdwidth = 0;
-	cp->refgrdheight = 0;
-	cp->tilegrdoffx = UINT_FAST32_MAX;
-	cp->tilegrdoffy = UINT_FAST32_MAX;
-	cp->tilewidth = 0;
-	cp->tileheight = 0;
-	cp->numcmpts = jas_image_numcmpts(image);
-
-	hsteplcm = 1;
-	vsteplcm = 1;
-	for (cmptno = 0; cmptno < jas_image_numcmpts(image); ++cmptno) {
-		if (jas_image_cmptbrx(image, cmptno) + jas_image_cmpthstep(image, cmptno) <=
-		  jas_image_brx(image) || jas_image_cmptbry(image, cmptno) +
-		  jas_image_cmptvstep(image, cmptno) <= jas_image_bry(image)) {
-			fprintf(stderr, "unsupported image type\n");
-			goto error;
-		}
-		/* Note: We ought to be calculating the LCMs here.  Fix some day. */
-		hsteplcm *= jas_image_cmpthstep(image, cmptno);
-		vsteplcm *= jas_image_cmptvstep(image, cmptno);
-	}
-
-	if (!(cp->ccps = jas_malloc(cp->numcmpts * sizeof(jpc_enc_ccp_t)))) {
-		goto error;
-	}
-	for (cmptno = 0, ccp = cp->ccps; cmptno < cp->numcmpts; ++cmptno,
-	  ++ccp) {
-		ccp->sampgrdstepx = jas_image_cmpthstep(image, cmptno);
-		ccp->sampgrdstepy = jas_image_cmptvstep(image, cmptno);
-		/* XXX - this isn't quite correct for more general image */
-		ccp->sampgrdsubstepx = 0;
-		ccp->sampgrdsubstepx = 0;
-		ccp->prec = jas_image_cmptprec(image, cmptno);
-		ccp->sgnd = jas_image_cmptsgnd(image, cmptno);
-		ccp->numstepsizes = 0;
-		memset(ccp->stepsizes, 0, sizeof(ccp->stepsizes));
-	}
-
-	cp->rawsize = jas_image_rawsize(image);
-	cp->totalsize = UINT_FAST32_MAX;
-
-	tcp = &cp->tcp;
-	tcp->csty = 0;
-	tcp->intmode = true;
-	tcp->prg = JPC_COD_LRCPPRG;
-	tcp->numlyrs = 1;
-	tcp->ilyrrates = 0;
-
-	tccp = &cp->tccp;
-	tccp->csty = 0;
-	tccp->maxrlvls = 6;
-	tccp->cblkwidthexpn = 6;
-	tccp->cblkheightexpn = 6;
-	tccp->cblksty = 0;
-	tccp->numgbits = 2;
-
-	if (!(tvp = jas_tvparser_create(optstr ? optstr : ""))) {
-		goto error;
-	}
-
-	while (!(ret = jas_tvparser_next(tvp))) {
-		switch (jas_taginfo_nonull(jas_taginfos_lookup(encopts,
-		  jas_tvparser_gettag(tvp)))->id) {
-		case OPT_DEBUG:
-			cp->debug = atoi(jas_tvparser_getval(tvp));
-			break;
-		case OPT_IMGAREAOFFX:
-			cp->imgareatlx = atoi(jas_tvparser_getval(tvp));
-			break;
-		case OPT_IMGAREAOFFY:
-			cp->imgareatly = atoi(jas_tvparser_getval(tvp));
-			break;
-		case OPT_TILEGRDOFFX:
-			cp->tilegrdoffx = atoi(jas_tvparser_getval(tvp));
-			break;
-		case OPT_TILEGRDOFFY:
-			cp->tilegrdoffy = atoi(jas_tvparser_getval(tvp));
-			break;
-		case OPT_TILEWIDTH:
-			cp->tilewidth = atoi(jas_tvparser_getval(tvp));
-			break;
-		case OPT_TILEHEIGHT:
-			cp->tileheight = atoi(jas_tvparser_getval(tvp));
-			break;
-		case OPT_PRCWIDTH:
-			prcwidthexpn = jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
-			break;
-		case OPT_PRCHEIGHT:
-			prcheightexpn = jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
-			break;
-		case OPT_CBLKWIDTH:
-			tccp->cblkwidthexpn =
-			  jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
-			break;
-		case OPT_CBLKHEIGHT:
-			tccp->cblkheightexpn =
-			  jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
-			break;
-		case OPT_MODE:
-			if ((tagid = jas_taginfo_nonull(jas_taginfos_lookup(modetab,
-			  jas_tvparser_getval(tvp)))->id) < 0) {
-				fprintf(stderr,
-				  "ignoring invalid mode %s\n",
-				  jas_tvparser_getval(tvp));
-			} else {
-				tcp->intmode = (tagid == MODE_INT);
-			}
-			break;
-		case OPT_PRG:
-			if ((tagid = jas_taginfo_nonull(jas_taginfos_lookup(prgordtab,
-			  jas_tvparser_getval(tvp)))->id) < 0) {
-				fprintf(stderr,
-				  "ignoring invalid progression order %s\n",
-				  jas_tvparser_getval(tvp));
-			} else {
-				tcp->prg = tagid;
-			}
-			break;
-		case OPT_NOMCT:
-			enablemct = false;
-			break;
-		case OPT_MAXRLVLS:
-			tccp->maxrlvls = atoi(jas_tvparser_getval(tvp));
-			break;
-		case OPT_SOP:
-			cp->tcp.csty |= JPC_COD_SOP;
-			break;
-		case OPT_EPH:
-			cp->tcp.csty |= JPC_COD_EPH;
-			break;
-		case OPT_LAZY:
-			tccp->cblksty |= JPC_COX_LAZY;
-			break;
-		case OPT_TERMALL:
-			tccp->cblksty |= JPC_COX_TERMALL;
-			break;
-		case OPT_SEGSYM:
-			tccp->cblksty |= JPC_COX_SEGSYM;
-			break;
-		case OPT_VCAUSAL:
-			tccp->cblksty |= JPC_COX_VSC;
-			break;
-		case OPT_RESET:
-			tccp->cblksty |= JPC_COX_RESET;
-			break;
-		case OPT_PTERM:
-			tccp->cblksty |= JPC_COX_PTERM;
-			break;
-		case OPT_NUMGBITS:
-			cp->tccp.numgbits = atoi(jas_tvparser_getval(tvp));
-			break;
-		case OPT_RATE:
-			if (ratestrtosize(jas_tvparser_getval(tvp), cp->rawsize,
-			  &cp->totalsize)) {
-				fprintf(stderr,
-				  "ignoring bad rate specifier %s\n",
-				  jas_tvparser_getval(tvp));
-			}
-			break;
-		case OPT_ILYRRATES:
-			if (jpc_atoaf(jas_tvparser_getval(tvp), &numilyrrates,
-			  &ilyrrates)) {
-				fprintf(stderr,
-				  "warning: invalid intermediate layer rates specifier ignored (%s)\n",
-				  jas_tvparser_getval(tvp));
-			}
-			break;
-
-		case OPT_JP2OVERHEAD:
-			jp2overhead = atoi(jas_tvparser_getval(tvp));
-			break;
-		default:
-			fprintf(stderr, "warning: ignoring invalid option %s\n",
-			 jas_tvparser_gettag(tvp));
-			break;
-		}
-	}
-
-	jas_tvparser_destroy(tvp);
-	tvp = 0;
-
-	if (cp->totalsize != UINT_FAST32_MAX) {
-		cp->totalsize = (cp->totalsize > jp2overhead) ?
-		  (cp->totalsize - jp2overhead) : 0;
-	}
-
-	if (cp->imgareatlx == UINT_FAST32_MAX) {
-		cp->imgareatlx = 0;
-	} else {
-		if (hsteplcm != 1) {
-			fprintf(stderr, "warning: overriding imgareatlx value\n");
-		}
-		cp->imgareatlx *= hsteplcm;
-	}
-	if (cp->imgareatly == UINT_FAST32_MAX) {
-		cp->imgareatly = 0;
-	} else {
-		if (vsteplcm != 1) {
-			fprintf(stderr, "warning: overriding imgareatly value\n");
-		}
-		cp->imgareatly *= vsteplcm;
-	}
-	cp->refgrdwidth = cp->imgareatlx + jas_image_width(image);
-	cp->refgrdheight = cp->imgareatly + jas_image_height(image);
-	if (cp->tilegrdoffx == UINT_FAST32_MAX) {
-		cp->tilegrdoffx = cp->imgareatlx;
-	}
-	if (cp->tilegrdoffy == UINT_FAST32_MAX) {
-		cp->tilegrdoffy = cp->imgareatly;
-	}
-	if (!cp->tilewidth) {
-		cp->tilewidth = cp->refgrdwidth - cp->tilegrdoffx;
-	}
-	if (!cp->tileheight) {
-		cp->tileheight = cp->refgrdheight - cp->tilegrdoffy;
-	}
-
-	if (cp->numcmpts == 3) {
-		mctvalid = true;
-		for (cmptno = 0; cmptno < jas_image_numcmpts(image); ++cmptno) {
-			if (jas_image_cmptprec(image, cmptno) != jas_image_cmptprec(image, 0) ||
-			  jas_image_cmptsgnd(image, cmptno) != jas_image_cmptsgnd(image, 0) ||
-			  jas_image_cmptwidth(image, cmptno) != jas_image_cmptwidth(image, 0) ||
-			  jas_image_cmptheight(image, cmptno) != jas_image_cmptheight(image, 0)) {
-				mctvalid = false;
-			}
-		}
-	} else {
-		mctvalid = false;
-	}
-	if (mctvalid && enablemct && jas_image_colorspace(image) != JAS_IMAGE_CS_RGB) {
-		fprintf(stderr, "warning: color model apparently not RGB\n");
-	}
-	if (mctvalid && enablemct && jas_image_colorspace(image) == JAS_IMAGE_CS_RGB) {
-		tcp->mctid = (tcp->intmode) ? (JPC_MCT_RCT) : (JPC_MCT_ICT);
-	} else {
-		tcp->mctid = JPC_MCT_NONE;
-	}
-	tccp->qmfbid = (tcp->intmode) ? (JPC_COX_RFT) : (JPC_COX_INS);
-
-	for (rlvlno = 0; rlvlno < tccp->maxrlvls; ++rlvlno) {
-		tccp->prcwidthexpns[rlvlno] = prcwidthexpn;
-		tccp->prcheightexpns[rlvlno] = prcheightexpn;
-	}
-	if (prcwidthexpn != 15 || prcheightexpn != 15) {
-		tccp->csty |= JPC_COX_PRT;
-	}
-
-	/* Ensure that the tile width and height is valid. */
-	if (!cp->tilewidth) {
-		fprintf(stderr, "invalid tile width %lu\n", (unsigned long)
-		  cp->tilewidth);
-		goto error;
-	}
-	if (!cp->tileheight) {
-		fprintf(stderr, "invalid tile height %lu\n", (unsigned long)
-		  cp->tileheight);
-		goto error;
-	}
-
-	/* Ensure that the tile grid offset is valid. */
-	if (cp->tilegrdoffx > cp->imgareatlx ||
-	  cp->tilegrdoffy > cp->imgareatly ||
-	  cp->tilegrdoffx + cp->tilewidth < cp->imgareatlx ||
-	  cp->tilegrdoffy + cp->tileheight < cp->imgareatly) {
-		fprintf(stderr, "invalid tile grid offset (%lu, %lu)\n",
-		  (unsigned long) cp->tilegrdoffx, (unsigned long)
-		  cp->tilegrdoffy);
-		goto error;
-	}
-
-	cp->numhtiles = JPC_CEILDIV(cp->refgrdwidth - cp->tilegrdoffx,
-	  cp->tilewidth);
-	cp->numvtiles = JPC_CEILDIV(cp->refgrdheight - cp->tilegrdoffy,
-	  cp->tileheight);
-	cp->numtiles = cp->numhtiles * cp->numvtiles;
-
-	if (ilyrrates && numilyrrates > 0) {
-		tcp->numlyrs = numilyrrates + 1;
-		if (!(tcp->ilyrrates = jas_malloc((tcp->numlyrs - 1) *
-		  sizeof(jpc_fix_t)))) {
-			goto error;
-		}
-		for (i = 0; i < tcp->numlyrs - 1; ++i) {
-			tcp->ilyrrates[i] = jpc_dbltofix(ilyrrates[i]);
-		}
-	}
-
-	/* Ensure that the integer mode is used in the case of lossless
-	  coding. */
-	if (cp->totalsize == UINT_FAST32_MAX && (!cp->tcp.intmode)) {
-		fprintf(stderr, "cannot use real mode for lossless coding\n");
-		goto error;
-	}
-
-	/* Ensure that the precinct width is valid. */
-	if (prcwidthexpn > 15) {
-		fprintf(stderr, "invalid precinct width\n");
-		goto error;
-	}
-
-	/* Ensure that the precinct height is valid. */
-	if (prcheightexpn > 15) {
-		fprintf(stderr, "invalid precinct height\n");
-		goto error;
-	}
-
-	/* Ensure that the code block width is valid. */
-	if (cp->tccp.cblkwidthexpn < 2 || cp->tccp.cblkwidthexpn > 12) {
-		fprintf(stderr, "invalid code block width %d\n",
-		  JPC_POW2(cp->tccp.cblkwidthexpn));
-		goto error;
-	}
-
-	/* Ensure that the code block height is valid. */
-	if (cp->tccp.cblkheightexpn < 2 || cp->tccp.cblkheightexpn > 12) {
-		fprintf(stderr, "invalid code block height %d\n",
-		  JPC_POW2(cp->tccp.cblkheightexpn));
-		goto error;
-	}
-
-	/* Ensure that the code block size is not too large. */
-	if (cp->tccp.cblkwidthexpn + cp->tccp.cblkheightexpn > 12) {
-		fprintf(stderr, "code block size too large\n");
-		goto error;
-	}
-
-	/* Ensure that the number of layers is valid. */
-	if (cp->tcp.numlyrs > 16384) {
-		fprintf(stderr, "too many layers\n");
-		goto error;
-	}
-
-	/* There must be at least one resolution level. */
-	if (cp->tccp.maxrlvls < 1) {
-		fprintf(stderr, "must be at least one resolution level\n");
-		goto error;
-	}
-
-	/* Ensure that the number of guard bits is valid. */
-	if (cp->tccp.numgbits > 8) {
-		fprintf(stderr, "invalid number of guard bits\n");
-		goto error;
-	}
-
-	/* Ensure that the rate is within the legal range. */
-	if (cp->totalsize != UINT_FAST32_MAX && cp->totalsize > cp->rawsize) {
-		fprintf(stderr, "warning: specified rate is unreasonably large (%lu > %lu)\n", (unsigned long) cp->totalsize, (unsigned long) cp->rawsize);
-	}
-
-	/* Ensure that the intermediate layer rates are valid. */
-	if (tcp->numlyrs > 1) {
-		/* The intermediate layers rates must increase monotonically. */
-		for (lyrno = 0; lyrno + 2 < tcp->numlyrs; ++lyrno) {
-			if (tcp->ilyrrates[lyrno] >= tcp->ilyrrates[lyrno + 1]) {
-				fprintf(stderr, "intermediate layer rates must increase monotonically\n");
-				goto error;
-			}
-		}
-		/* The intermediate layer rates must be less than the overall rate. */
-		if (cp->totalsize != UINT_FAST32_MAX) {
-			for (lyrno = 0; lyrno < tcp->numlyrs - 1; ++lyrno) {
-				if (jpc_fixtodbl(tcp->ilyrrates[lyrno]) > ((double) cp->totalsize)
-				  / cp->rawsize) {
-					fprintf(stderr, "warning: intermediate layer rates must be less than overall rate\n");
-					goto error;
-				}
-			}
-		}
-	}
-
-	if (ilyrrates) {
-		jas_free(ilyrrates);
-	}
-
-	return cp;
+    jpc_enc_cp_t *cp;
+    jas_tvparser_t *tvp;
+    int ret;
+    int numilyrrates;
+    double *ilyrrates;
+    int i;
+    int tagid;
+    jpc_enc_tcp_t *tcp;
+    jpc_enc_tccp_t *tccp;
+    jpc_enc_ccp_t *ccp;
+    uint_fast16_t cmptno;
+    uint_fast16_t rlvlno;
+    uint_fast16_t prcwidthexpn;
+    uint_fast16_t prcheightexpn;
+    bool enablemct;
+    uint_fast32_t jp2overhead;
+    uint_fast16_t lyrno;
+    uint_fast32_t hsteplcm;
+    uint_fast32_t vsteplcm;
+    bool mctvalid;
+
+    tvp = 0;
+    cp = 0;
+    ilyrrates = 0;
+    numilyrrates = 0;
+
+    if (!(cp = jas_malloc(sizeof(jpc_enc_cp_t)))) {
+        goto error;
+    }
+
+    prcwidthexpn = 15;
+    prcheightexpn = 15;
+    enablemct = true;
+    jp2overhead = 0;
+
+    cp->ccps = 0;
+    cp->debug = 0;
+    cp->imgareatlx = UINT_FAST32_MAX;
+    cp->imgareatly = UINT_FAST32_MAX;
+    cp->refgrdwidth = 0;
+    cp->refgrdheight = 0;
+    cp->tilegrdoffx = UINT_FAST32_MAX;
+    cp->tilegrdoffy = UINT_FAST32_MAX;
+    cp->tilewidth = 0;
+    cp->tileheight = 0;
+    cp->numcmpts = jas_image_numcmpts(image);
+
+    hsteplcm = 1;
+    vsteplcm = 1;
+    for (cmptno = 0; cmptno < jas_image_numcmpts(image); ++cmptno) {
+        if (jas_image_cmptbrx(image, cmptno) + jas_image_cmpthstep(image, cmptno) <=
+          jas_image_brx(image) || jas_image_cmptbry(image, cmptno) +
+          jas_image_cmptvstep(image, cmptno) <= jas_image_bry(image)) {
+            fprintf(stderr, "We don't know how to interpret this image type\n");
+            goto error;
+        }
+        /* Note: We ought to be calculating the LCMs here.  Fix some day. */
+        hsteplcm *= jas_image_cmpthstep(image, cmptno);
+        vsteplcm *= jas_image_cmptvstep(image, cmptno);
+    }
+
+    if (!(cp->ccps = jas_malloc(cp->numcmpts * sizeof(jpc_enc_ccp_t)))) {
+        goto error;
+    }
+    for (cmptno = 0, ccp = cp->ccps; cmptno < cp->numcmpts; ++cmptno,
+      ++ccp) {
+        ccp->sampgrdstepx = jas_image_cmpthstep(image, cmptno);
+        ccp->sampgrdstepy = jas_image_cmptvstep(image, cmptno);
+        /* XXX - this isn't quite correct for more general image */
+        ccp->sampgrdsubstepx = 0;
+        ccp->sampgrdsubstepx = 0;
+        ccp->prec = jas_image_cmptprec(image, cmptno);
+        ccp->sgnd = jas_image_cmptsgnd(image, cmptno);
+        ccp->numstepsizes = 0;
+        memset(ccp->stepsizes, 0, sizeof(ccp->stepsizes));
+    }
+
+    cp->rawsize = jas_image_rawsize(image);
+    cp->totalsize = UINT_FAST32_MAX;
+
+    tcp = &cp->tcp;
+    tcp->csty = 0;
+    tcp->intmode = true;
+    tcp->prg = JPC_COD_LRCPPRG;
+    tcp->numlyrs = 1;
+    tcp->ilyrrates = 0;
+
+    tccp = &cp->tccp;
+    tccp->csty = 0;
+    tccp->maxrlvls = 6;
+    tccp->cblkwidthexpn = 6;
+    tccp->cblkheightexpn = 6;
+    tccp->cblksty = 0;
+    tccp->numgbits = 2;
+
+    if (!(tvp = jas_tvparser_create(optstr ? optstr : ""))) {
+        goto error;
+    }
+
+    while (!(ret = jas_tvparser_next(tvp))) {
+        switch (jas_taginfo_nonull(jas_taginfos_lookup(encopts,
+          jas_tvparser_gettag(tvp)))->id) {
+        case OPT_DEBUG:
+            cp->debug = atoi(jas_tvparser_getval(tvp));
+            break;
+        case OPT_IMGAREAOFFX:
+            cp->imgareatlx = atoi(jas_tvparser_getval(tvp));
+            break;
+        case OPT_IMGAREAOFFY:
+            cp->imgareatly = atoi(jas_tvparser_getval(tvp));
+            break;
+        case OPT_TILEGRDOFFX:
+            cp->tilegrdoffx = atoi(jas_tvparser_getval(tvp));
+            break;
+        case OPT_TILEGRDOFFY:
+            cp->tilegrdoffy = atoi(jas_tvparser_getval(tvp));
+            break;
+        case OPT_TILEWIDTH:
+            cp->tilewidth = atoi(jas_tvparser_getval(tvp));
+            break;
+        case OPT_TILEHEIGHT:
+            cp->tileheight = atoi(jas_tvparser_getval(tvp));
+            break;
+        case OPT_PRCWIDTH:
+            prcwidthexpn = jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
+            break;
+        case OPT_PRCHEIGHT:
+            prcheightexpn = jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
+            break;
+        case OPT_CBLKWIDTH:
+            tccp->cblkwidthexpn =
+              jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
+            break;
+        case OPT_CBLKHEIGHT:
+            tccp->cblkheightexpn =
+              jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
+            break;
+        case OPT_MODE:
+            if ((tagid = jas_taginfo_nonull(jas_taginfos_lookup(modetab,
+              jas_tvparser_getval(tvp)))->id) < 0) {
+                fprintf(stderr,
+                  "ignoring invalid mode %s\n",
+                  jas_tvparser_getval(tvp));
+            } else {
+                tcp->intmode = (tagid == MODE_INT);
+            }
+            break;
+        case OPT_PRG:
+            if ((tagid = jas_taginfo_nonull(jas_taginfos_lookup(prgordtab,
+              jas_tvparser_getval(tvp)))->id) < 0) {
+                fprintf(stderr,
+                  "ignoring invalid progression order %s\n",
+                  jas_tvparser_getval(tvp));
+            } else {
+                tcp->prg = tagid;
+            }
+            break;
+        case OPT_NOMCT:
+            enablemct = false;
+            break;
+        case OPT_MAXRLVLS:
+            tccp->maxrlvls = atoi(jas_tvparser_getval(tvp));
+            break;
+        case OPT_SOP:
+            cp->tcp.csty |= JPC_COD_SOP;
+            break;
+        case OPT_EPH:
+            cp->tcp.csty |= JPC_COD_EPH;
+            break;
+        case OPT_LAZY:
+            tccp->cblksty |= JPC_COX_LAZY;
+            break;
+        case OPT_TERMALL:
+            tccp->cblksty |= JPC_COX_TERMALL;
+            break;
+        case OPT_SEGSYM:
+            tccp->cblksty |= JPC_COX_SEGSYM;
+            break;
+        case OPT_VCAUSAL:
+            tccp->cblksty |= JPC_COX_VSC;
+            break;
+        case OPT_RESET:
+            tccp->cblksty |= JPC_COX_RESET;
+            break;
+        case OPT_PTERM:
+            tccp->cblksty |= JPC_COX_PTERM;
+            break;
+        case OPT_NUMGBITS:
+            cp->tccp.numgbits = atoi(jas_tvparser_getval(tvp));
+            break;
+        case OPT_RATE:
+            if (ratestrtosize(jas_tvparser_getval(tvp), cp->rawsize,
+              &cp->totalsize)) {
+                fprintf(stderr,
+                  "ignoring bad rate specifier %s\n",
+                  jas_tvparser_getval(tvp));
+            }
+            break;
+        case OPT_ILYRRATES:
+            if (jpc_atoaf(jas_tvparser_getval(tvp), &numilyrrates,
+              &ilyrrates)) {
+                fprintf(stderr,
+                  "warning: invalid intermediate layer rates specifier ignored (%s)\n",
+                  jas_tvparser_getval(tvp));
+            }
+            break;
+
+        case OPT_JP2OVERHEAD:
+            jp2overhead = atoi(jas_tvparser_getval(tvp));
+            break;
+        default:
+            fprintf(stderr, "warning: ignoring invalid option %s\n",
+             jas_tvparser_gettag(tvp));
+            break;
+        }
+    }
+
+    jas_tvparser_destroy(tvp);
+    tvp = 0;
+
+    if (cp->totalsize != UINT_FAST32_MAX) {
+        cp->totalsize = (cp->totalsize > jp2overhead) ?
+          (cp->totalsize - jp2overhead) : 0;
+    }
+
+    if (cp->imgareatlx == UINT_FAST32_MAX) {
+        cp->imgareatlx = 0;
+    } else {
+        if (hsteplcm != 1) {
+            fprintf(stderr, "warning: overriding imgareatlx value\n");
+        }
+        cp->imgareatlx *= hsteplcm;
+    }
+    if (cp->imgareatly == UINT_FAST32_MAX) {
+        cp->imgareatly = 0;
+    } else {
+        if (vsteplcm != 1) {
+            fprintf(stderr, "warning: overriding imgareatly value\n");
+        }
+        cp->imgareatly *= vsteplcm;
+    }
+    cp->refgrdwidth = cp->imgareatlx + jas_image_width(image);
+    cp->refgrdheight = cp->imgareatly + jas_image_height(image);
+    if (cp->tilegrdoffx == UINT_FAST32_MAX) {
+        cp->tilegrdoffx = cp->imgareatlx;
+    }
+    if (cp->tilegrdoffy == UINT_FAST32_MAX) {
+        cp->tilegrdoffy = cp->imgareatly;
+    }
+    if (!cp->tilewidth) {
+        cp->tilewidth = cp->refgrdwidth - cp->tilegrdoffx;
+    }
+    if (!cp->tileheight) {
+        cp->tileheight = cp->refgrdheight - cp->tilegrdoffy;
+    }
+
+    if (cp->numcmpts == 3) {
+        mctvalid = true;
+        for (cmptno = 0; cmptno < jas_image_numcmpts(image); ++cmptno) {
+            if (jas_image_cmptprec(image, cmptno) != jas_image_cmptprec(image, 0) ||
+              jas_image_cmptsgnd(image, cmptno) != jas_image_cmptsgnd(image, 0) ||
+              jas_image_cmptwidth(image, cmptno) != jas_image_cmptwidth(image, 0) ||
+              jas_image_cmptheight(image, cmptno) != jas_image_cmptheight(image, 0)) {
+                mctvalid = false;
+            }
+        }
+    } else {
+        mctvalid = false;
+    }
+    if (mctvalid && enablemct && jas_image_colorspace(image) != JAS_IMAGE_CS_RGB) {
+        fprintf(stderr, "warning: color model apparently not RGB\n");
+    }
+    if (mctvalid && enablemct && jas_image_colorspace(image) == JAS_IMAGE_CS_RGB) {
+        tcp->mctid = (tcp->intmode) ? (JPC_MCT_RCT) : (JPC_MCT_ICT);
+    } else {
+        tcp->mctid = JPC_MCT_NONE;
+    }
+    tccp->qmfbid = (tcp->intmode) ? (JPC_COX_RFT) : (JPC_COX_INS);
+
+    for (rlvlno = 0; rlvlno < tccp->maxrlvls; ++rlvlno) {
+        tccp->prcwidthexpns[rlvlno] = prcwidthexpn;
+        tccp->prcheightexpns[rlvlno] = prcheightexpn;
+    }
+    if (prcwidthexpn != 15 || prcheightexpn != 15) {
+        tccp->csty |= JPC_COX_PRT;
+    }
+
+    /* Ensure that the tile width and height is valid. */
+    if (!cp->tilewidth) {
+        fprintf(stderr, "invalid tile width %lu\n", (unsigned long)
+          cp->tilewidth);
+        goto error;
+    }
+    if (!cp->tileheight) {
+        fprintf(stderr, "invalid tile height %lu\n", (unsigned long)
+          cp->tileheight);
+        goto error;
+    }
+
+    /* Ensure that the tile grid offset is valid. */
+    if (cp->tilegrdoffx > cp->imgareatlx ||
+      cp->tilegrdoffy > cp->imgareatly ||
+      cp->tilegrdoffx + cp->tilewidth < cp->imgareatlx ||
+      cp->tilegrdoffy + cp->tileheight < cp->imgareatly) {
+        fprintf(stderr, "invalid tile grid offset (%lu, %lu)\n",
+          (unsigned long) cp->tilegrdoffx, (unsigned long)
+          cp->tilegrdoffy);
+        goto error;
+    }
+
+    cp->numhtiles = JPC_CEILDIV(cp->refgrdwidth - cp->tilegrdoffx,
+      cp->tilewidth);
+    cp->numvtiles = JPC_CEILDIV(cp->refgrdheight - cp->tilegrdoffy,
+      cp->tileheight);
+    cp->numtiles = cp->numhtiles * cp->numvtiles;
+
+    if (ilyrrates && numilyrrates > 0) {
+        tcp->numlyrs = numilyrrates + 1;
+        if (!(tcp->ilyrrates = jas_malloc((tcp->numlyrs - 1) *
+          sizeof(jpc_fix_t)))) {
+            goto error;
+        }
+        for (i = 0; i < tcp->numlyrs - 1; ++i) {
+            tcp->ilyrrates[i] = jpc_dbltofix(ilyrrates[i]);
+        }
+    }
+
+    /* Ensure that the integer mode is used in the case of lossless
+      coding. */
+    if (cp->totalsize == UINT_FAST32_MAX && (!cp->tcp.intmode)) {
+        fprintf(stderr, "cannot use real mode for lossless coding\n");
+        goto error;
+    }
+
+    /* Ensure that the precinct width is valid. */
+    if (prcwidthexpn > 15) {
+        fprintf(stderr, "invalid precinct width\n");
+        goto error;
+    }
+
+    /* Ensure that the precinct height is valid. */
+    if (prcheightexpn > 15) {
+        fprintf(stderr, "invalid precinct height\n");
+        goto error;
+    }
+
+    /* Ensure that the code block width is valid. */
+    if (cp->tccp.cblkwidthexpn < 2 || cp->tccp.cblkwidthexpn > 12) {
+        fprintf(stderr, "invalid code block width %d\n",
+          JPC_POW2(cp->tccp.cblkwidthexpn));
+        goto error;
+    }
+
+    /* Ensure that the code block height is valid. */
+    if (cp->tccp.cblkheightexpn < 2 || cp->tccp.cblkheightexpn > 12) {
+        fprintf(stderr, "invalid code block height %d\n",
+          JPC_POW2(cp->tccp.cblkheightexpn));
+        goto error;
+    }
+
+    /* Ensure that the code block size is not too large. */
+    if (cp->tccp.cblkwidthexpn + cp->tccp.cblkheightexpn > 12) {
+        fprintf(stderr, "code block size too large\n");
+        goto error;
+    }
+
+    /* Ensure that the number of layers is valid. */
+    if (cp->tcp.numlyrs > 16384) {
+        fprintf(stderr, "too many layers\n");
+        goto error;
+    }
+
+    /* There must be at least one resolution level. */
+    if (cp->tccp.maxrlvls < 1) {
+        fprintf(stderr, "must be at least one resolution level\n");
+        goto error;
+    }
+
+    /* Ensure that the number of guard bits is valid. */
+    if (cp->tccp.numgbits > 8) {
+        fprintf(stderr, "invalid number of guard bits\n");
+        goto error;
+    }
+
+    /* Ensure that the intermediate layer rates are valid. */
+    if (tcp->numlyrs > 1) {
+        /* The intermediate layers rates must increase monotonically. */
+        for (lyrno = 0; lyrno + 2 < tcp->numlyrs; ++lyrno) {
+            if (tcp->ilyrrates[lyrno] >= tcp->ilyrrates[lyrno + 1]) {
+                fprintf(stderr, "intermediate layer rates must increase monotonically\n");
+                goto error;
+            }
+        }
+        /* The intermediate layer rates must be less than the overall rate. */
+        if (cp->totalsize != UINT_FAST32_MAX) {
+            for (lyrno = 0; lyrno < tcp->numlyrs - 1; ++lyrno) {
+                if (jpc_fixtodbl(tcp->ilyrrates[lyrno]) > ((double) cp->totalsize)
+                  / cp->rawsize) {
+                    fprintf(stderr, "warning: intermediate layer rates must be less than overall rate\n");
+                    goto error;
+                }
+            }
+        }
+    }
+
+    if (ilyrrates) {
+        jas_free(ilyrrates);
+    }
+
+    return cp;
 
 error:
 
-	if (ilyrrates) {
-		jas_free(ilyrrates);
-	}
-	if (tvp) {
-		jas_tvparser_destroy(tvp);
-	}
-	if (cp) {
-		jpc_enc_cp_destroy(cp);
-	}
-	return 0;
+    if (ilyrrates) {
+        jas_free(ilyrrates);
+    }
+    if (tvp) {
+        jas_tvparser_destroy(tvp);
+    }
+    if (cp) {
+        jpc_enc_cp_destroy(cp);
+    }
+    return 0;
 }
 
 void jpc_enc_cp_destroy(jpc_enc_cp_t *cp)
 {
-	if (cp->ccps) {
-		if (cp->tcp.ilyrrates) {
-			jas_free(cp->tcp.ilyrrates);
-		}
-		jas_free(cp->ccps);
-	}
-	jas_free(cp);
+    if (cp->ccps) {
+        if (cp->tcp.ilyrrates) {
+            jas_free(cp->tcp.ilyrrates);
+        }
+        jas_free(cp->ccps);
+    }
+    jas_free(cp);
 }
 
 int ratestrtosize(const char *s, uint_fast32_t rawsize, uint_fast32_t *size)
 {
-	char *cp;
-	jpc_flt_t f;
-
-	/* Note: This function must not modify output size on failure. */
-	if ((cp = strchr(s, 'B'))) {
-		*size = atoi(s);
-	} else {
-		f = atof(s);
-		if (f < 0) {
-			*size = 0;
-		} else if (f > 1.0) {
-			*size = rawsize + 1;
-		} else {
-			*size = f * rawsize;
-		}
-	}
-	return 0;
+    char *cp;
+    jpc_flt_t f;
+
+    /* Note: This function must not modify output size on failure. */
+    if ((cp = strchr(s, 'B'))) {
+        *size = atoi(s);
+    } else {
+        f = atof(s);
+        if (f < 0) {
+            *size = 0;
+        } else if (f > 1.0) {
+            *size = rawsize + 1;
+        } else {
+            *size = f * rawsize;
+        }
+    }
+    return 0;
 }
 
 /******************************************************************************\
@@ -852,58 +756,58 @@ int ratestrtosize(const char *s, uint_fast32_t rawsize, uint_fast32_t *size)
 
 jpc_enc_t *jpc_enc_create(jpc_enc_cp_t *cp, jas_stream_t *out, jas_image_t *image)
 {
-	jpc_enc_t *enc;
+    jpc_enc_t *enc;
 
-	enc = 0;
+    enc = 0;
 
-	if (!(enc = jas_malloc(sizeof(jpc_enc_t)))) {
-		goto error;
-	}
+    if (!(enc = jas_malloc(sizeof(jpc_enc_t)))) {
+        goto error;
+    }
 
-	enc->image = image;
-	enc->out = out;
-	enc->cp = cp;
-	enc->cstate = 0;
-	enc->tmpstream = 0;
-	enc->mrk = 0;
-	enc->curtile = 0;
+    enc->image = image;
+    enc->out = out;
+    enc->cp = cp;
+    enc->cstate = 0;
+    enc->tmpstream = 0;
+    enc->mrk = 0;
+    enc->curtile = 0;
 
-	if (!(enc->cstate = jpc_cstate_create())) {
-		goto error;
-	}
-	enc->len = 0;
-	enc->mainbodysize = 0;
+    if (!(enc->cstate = jpc_cstate_create())) {
+        goto error;
+    }
+    enc->len = 0;
+    enc->mainbodysize = 0;
 
-	return enc;
+    return enc;
 
 error:
 
-	if (enc) {
-		jpc_enc_destroy(enc);
-	}
-	return 0;
+    if (enc) {
+        jpc_enc_destroy(enc);
+    }
+    return 0;
 }
 
 void jpc_enc_destroy(jpc_enc_t *enc)
 {
-	/* The image object (i.e., enc->image) and output stream object
-	(i.e., enc->out) are created outside of the encoder.
-	Therefore, they must not be destroyed here. */
-
-	if (enc->curtile) {
-		jpc_enc_tile_destroy(enc->curtile);
-	}
-	if (enc->cp) {
-		jpc_enc_cp_destroy(enc->cp);
-	}
-	if (enc->cstate) {
-		jpc_cstate_destroy(enc->cstate);
-	}
-	if (enc->tmpstream) {
-		jas_stream_close(enc->tmpstream);
-	}
-
-	jas_free(enc);
+    /* The image object (i.e., enc->image) and output stream object
+    (i.e., enc->out) are created outside of the encoder.
+    Therefore, they must not be destroyed here. */
+
+    if (enc->curtile) {
+        jpc_enc_tile_destroy(enc->curtile);
+    }
+    if (enc->cp) {
+        jpc_enc_cp_destroy(enc->cp);
+    }
+    if (enc->cstate) {
+        jpc_cstate_destroy(enc->cstate);
+    }
+    if (enc->tmpstream) {
+        jas_stream_close(enc->tmpstream);
+    }
+
+    jas_free(enc);
 }
 
 /******************************************************************************\
@@ -912,1713 +816,2011 @@ void jpc_enc_destroy(jpc_enc_t *enc)
 
 static int jpc_enc_encodemainhdr(jpc_enc_t *enc)
 {
-	jpc_siz_t *siz;
-	jpc_cod_t *cod;
-	jpc_qcd_t *qcd;
-	int i;
+    jpc_siz_t *siz;
+    jpc_cod_t *cod;
+    jpc_qcd_t *qcd;
+    int i;
 long startoff;
 long mainhdrlen;
-	jpc_enc_cp_t *cp;
-	jpc_qcc_t *qcc;
-	jpc_enc_tccp_t *tccp;
-	uint_fast16_t cmptno;
-	jpc_tsfb_band_t bandinfos[JPC_MAXBANDS];
-	jpc_fix_t mctsynweight;
-	jpc_enc_tcp_t *tcp;
-	jpc_tsfb_t *tsfb;
-	jpc_tsfb_band_t *bandinfo;
-	uint_fast16_t numbands;
-	uint_fast16_t bandno;
-	uint_fast16_t rlvlno;
-	uint_fast16_t analgain;
-	jpc_fix_t absstepsize;
-	char buf[1024];
-	jpc_com_t *com;
-
-	cp = enc->cp;
+    jpc_enc_cp_t *cp;
+    jpc_qcc_t *qcc;
+    jpc_enc_tccp_t *tccp;
+    uint_fast16_t cmptno;
+    jpc_tsfb_band_t bandinfos[JPC_MAXBANDS];
+    jpc_fix_t mctsynweight;
+    jpc_enc_tcp_t *tcp;
+    jpc_tsfb_t *tsfb;
+    jpc_tsfb_band_t *bandinfo;
+    uint_fast16_t numbands;
+    uint_fast16_t bandno;
+    uint_fast16_t rlvlno;
+    uint_fast16_t analgain;
+    jpc_fix_t absstepsize;
+    char buf[1024];
+    jpc_com_t *com;
+
+    cp = enc->cp;
 
 startoff = jas_stream_getrwcount(enc->out);
 
-	/* Write SOC marker segment. */
-	if (!(enc->mrk = jpc_ms_create(JPC_MS_SOC))) {
-		return -1;
-	}
-	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
-		fprintf(stderr, "cannot write SOC marker\n");
-		return -1;
-	}
-	jpc_ms_destroy(enc->mrk);
-	enc->mrk = 0;
-
-	/* Write SIZ marker segment. */
-	if (!(enc->mrk = jpc_ms_create(JPC_MS_SIZ))) {
-		return -1;
-	}
-	siz = &enc->mrk->parms.siz;
-	siz->caps = 0;
-	siz->xoff = cp->imgareatlx;
-	siz->yoff = cp->imgareatly;
-	siz->width = cp->refgrdwidth;
-	siz->height = cp->refgrdheight;
-	siz->tilexoff = cp->tilegrdoffx;
-	siz->tileyoff = cp->tilegrdoffy;
-	siz->tilewidth = cp->tilewidth;
-	siz->tileheight = cp->tileheight;
-	siz->numcomps = cp->numcmpts;
-	siz->comps = jas_malloc(siz->numcomps * sizeof(jpc_sizcomp_t));
-	assert(siz->comps);
-	for (i = 0; i < cp->numcmpts; ++i) {
-		siz->comps[i].prec = cp->ccps[i].prec;
-		siz->comps[i].sgnd = cp->ccps[i].sgnd;
-		siz->comps[i].hsamp = cp->ccps[i].sampgrdstepx;
-		siz->comps[i].vsamp = cp->ccps[i].sampgrdstepy;
-	}
-	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
-		fprintf(stderr, "cannot write SIZ marker\n");
-		return -1;
-	}
-	jpc_ms_destroy(enc->mrk);
-	enc->mrk = 0;
-
-	if (!(enc->mrk = jpc_ms_create(JPC_MS_COM))) {
-		return -1;
-	}
-	sprintf(buf, "Creator: JasPer Version %s", jas_getversion());
-	com = &enc->mrk->parms.com;
-	com->len = strlen(buf);
-	com->regid = JPC_COM_LATIN;
-	if (!(com->data = JAS_CAST(unsigned char *, jas_strdup(buf)))) {
-		abort();
-	}
-	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
-		fprintf(stderr, "cannot write COM marker\n");
-		return -1;
-	}
-	jpc_ms_destroy(enc->mrk);
-	enc->mrk = 0;
+    /* Write SOC marker segment. */
+    if (!(enc->mrk = jpc_ms_create(JPC_MS_SOC))) {
+        return -1;
+    }
+    if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+        fprintf(stderr, "cannot write SOC marker\n");
+        return -1;
+    }
+    jpc_ms_destroy(enc->mrk);
+    enc->mrk = 0;
+
+    /* Write SIZ marker segment. */
+    if (!(enc->mrk = jpc_ms_create(JPC_MS_SIZ))) {
+        return -1;
+    }
+    siz = &enc->mrk->parms.siz;
+    siz->caps = 0;
+    siz->xoff = cp->imgareatlx;
+    siz->yoff = cp->imgareatly;
+    siz->width = cp->refgrdwidth;
+    siz->height = cp->refgrdheight;
+    siz->tilexoff = cp->tilegrdoffx;
+    siz->tileyoff = cp->tilegrdoffy;
+    siz->tilewidth = cp->tilewidth;
+    siz->tileheight = cp->tileheight;
+    siz->numcomps = cp->numcmpts;
+    siz->comps = jas_malloc(siz->numcomps * sizeof(jpc_sizcomp_t));
+    assert(siz->comps);
+    for (i = 0; i < cp->numcmpts; ++i) {
+        siz->comps[i].prec = cp->ccps[i].prec;
+        siz->comps[i].sgnd = cp->ccps[i].sgnd;
+        siz->comps[i].hsamp = cp->ccps[i].sampgrdstepx;
+        siz->comps[i].vsamp = cp->ccps[i].sampgrdstepy;
+    }
+    if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+        fprintf(stderr, "cannot write SIZ marker\n");
+        return -1;
+    }
+    jpc_ms_destroy(enc->mrk);
+    enc->mrk = 0;
+
+    if (!(enc->mrk = jpc_ms_create(JPC_MS_COM))) {
+        return -1;
+    }
+    sprintf(buf, "Creator: JasPer Version %s", jas_getversion());
+    com = &enc->mrk->parms.com;
+    com->len = strlen(buf);
+    com->regid = JPC_COM_LATIN;
+    if (!(com->data = JAS_CAST(unsigned char *, jas_strdup(buf)))) {
+        abort();
+    }
+    if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+        fprintf(stderr, "cannot write COM marker\n");
+        return -1;
+    }
+    jpc_ms_destroy(enc->mrk);
+    enc->mrk = 0;
 
 #if 0
-	if (!(enc->mrk = jpc_ms_create(JPC_MS_CRG))) {
-		return -1;
-	}
-	crg = &enc->mrk->parms.crg;
-	crg->comps = jas_malloc(crg->numcomps * sizeof(jpc_crgcomp_t));
-	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
-		fprintf(stderr, "cannot write CRG marker\n");
-		return -1;
-	}
-	jpc_ms_destroy(enc->mrk);
-	enc->mrk = 0;
+    if (!(enc->mrk = jpc_ms_create(JPC_MS_CRG))) {
+        return -1;
+    }
+    crg = &enc->mrk->parms.crg;
+    crg->comps = jas_malloc(crg->numcomps * sizeof(jpc_crgcomp_t));
+    if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+        fprintf(stderr, "cannot write CRG marker\n");
+        return -1;
+    }
+    jpc_ms_destroy(enc->mrk);
+    enc->mrk = 0;
 #endif
 
-	tcp = &cp->tcp;
-	tccp = &cp->tccp;
-	for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) {
-		tsfb = jpc_cod_gettsfb(tccp->qmfbid, tccp->maxrlvls - 1);
-		jpc_tsfb_getbands(tsfb, 0, 0, 1 << tccp->maxrlvls, 1 << tccp->maxrlvls,
-		  bandinfos);
-		jpc_tsfb_destroy(tsfb);
-		mctsynweight = jpc_mct_getsynweight(tcp->mctid, cmptno);
-		numbands = 3 * tccp->maxrlvls - 2;
-		for (bandno = 0, bandinfo = bandinfos; bandno < numbands;
-		  ++bandno, ++bandinfo) {
-			rlvlno = (bandno) ? ((bandno - 1) / 3 + 1) : 0;
-			analgain = JPC_NOMINALGAIN(tccp->qmfbid, tccp->maxrlvls,
-			  rlvlno, bandinfo->orient);
-			if (!tcp->intmode) {
-				absstepsize = jpc_fix_div(jpc_inttofix(1 <<
-				  (analgain + 1)), bandinfo->synenergywt);
-			} else {
-				absstepsize = jpc_inttofix(1);
-			}	
-			cp->ccps[cmptno].stepsizes[bandno] =
-			  jpc_abstorelstepsize(absstepsize,
-			  cp->ccps[cmptno].prec + analgain);
-		}
-		cp->ccps[cmptno].numstepsizes = numbands;
-	}
-
-	if (!(enc->mrk = jpc_ms_create(JPC_MS_COD))) {
-		return -1;
-	}
-	cod = &enc->mrk->parms.cod;
-	cod->csty = cp->tccp.csty | cp->tcp.csty;
-	cod->compparms.csty = cp->tccp.csty | cp->tcp.csty;
-	cod->compparms.numdlvls = cp->tccp.maxrlvls - 1;
-	cod->compparms.numrlvls = cp->tccp.maxrlvls;
-	cod->prg = cp->tcp.prg;
-	cod->numlyrs = cp->tcp.numlyrs;
-	cod->compparms.cblkwidthval = JPC_COX_CBLKSIZEEXPN(cp->tccp.cblkwidthexpn);
-	cod->compparms.cblkheightval = JPC_COX_CBLKSIZEEXPN(cp->tccp.cblkheightexpn);
-	cod->compparms.cblksty = cp->tccp.cblksty;
-	cod->compparms.qmfbid = cp->tccp.qmfbid;
-	cod->mctrans = (cp->tcp.mctid != JPC_MCT_NONE);
-	if (tccp->csty & JPC_COX_PRT) {
-		for (rlvlno = 0; rlvlno < tccp->maxrlvls; ++rlvlno) {
-			cod->compparms.rlvls[rlvlno].parwidthval = tccp->prcwidthexpns[rlvlno];
-			cod->compparms.rlvls[rlvlno].parheightval = tccp->prcheightexpns[rlvlno];
-		}
-	}
-	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
-		fprintf(stderr, "cannot write COD marker\n");
-		return -1;
-	}
-	jpc_ms_destroy(enc->mrk);
-	enc->mrk = 0;
-
-	if (!(enc->mrk = jpc_ms_create(JPC_MS_QCD))) {
-		return -1;
-	}
-	qcd = &enc->mrk->parms.qcd;
-	qcd->compparms.qntsty = (tccp->qmfbid == JPC_COX_INS) ?
-	  JPC_QCX_SEQNT : JPC_QCX_NOQNT;
-	qcd->compparms.numstepsizes = cp->ccps[0].numstepsizes;
-	qcd->compparms.numguard = cp->tccp.numgbits;
-	qcd->compparms.stepsizes = cp->ccps[0].stepsizes;
-	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
-		return -1;
-	}
-	/* We do not want the step size array to be freed! */
-	qcd->compparms.stepsizes = 0;
-	jpc_ms_destroy(enc->mrk);
-	enc->mrk = 0;
-
-	tccp = &cp->tccp;
-	for (cmptno = 1; cmptno < cp->numcmpts; ++cmptno) {
-		if (!(enc->mrk = jpc_ms_create(JPC_MS_QCC))) {
-			return -1;
-		}
-		qcc = &enc->mrk->parms.qcc;
-		qcc->compno = cmptno;
-		qcc->compparms.qntsty = (tccp->qmfbid == JPC_COX_INS) ?
-		  JPC_QCX_SEQNT : JPC_QCX_NOQNT;
-		qcc->compparms.numstepsizes = cp->ccps[cmptno].numstepsizes;
-		qcc->compparms.numguard = cp->tccp.numgbits;
-		qcc->compparms.stepsizes = cp->ccps[cmptno].stepsizes;
-		if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
-			return -1;
-		}
-		/* We do not want the step size array to be freed! */
-		qcc->compparms.stepsizes = 0;
-		jpc_ms_destroy(enc->mrk);
-		enc->mrk = 0;
-	}
-
-#define MAINTLRLEN	2
-	mainhdrlen = jas_stream_getrwcount(enc->out) - startoff;
-	enc->len += mainhdrlen;
-	if (enc->cp->totalsize != UINT_FAST32_MAX) {
-		uint_fast32_t overhead;
-		overhead = mainhdrlen + MAINTLRLEN;
-		enc->mainbodysize = (enc->cp->totalsize >= overhead) ?
-		  (enc->cp->totalsize - overhead) : 0;
-	} else {
-		enc->mainbodysize = UINT_FAST32_MAX;
-	}
-
-	return 0;
+    tcp = &cp->tcp;
+    tccp = &cp->tccp;
+    for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) {
+        tsfb = jpc_cod_gettsfb(tccp->qmfbid, tccp->maxrlvls - 1);
+        jpc_tsfb_getbands(tsfb, 0, 0, 1 << tccp->maxrlvls, 1 << tccp->maxrlvls,
+          bandinfos);
+        jpc_tsfb_destroy(tsfb);
+        mctsynweight = jpc_mct_getsynweight(tcp->mctid, cmptno);
+        numbands = 3 * tccp->maxrlvls - 2;
+        for (bandno = 0, bandinfo = bandinfos; bandno < numbands;
+          ++bandno, ++bandinfo) {
+            rlvlno = (bandno) ? ((bandno - 1) / 3 + 1) : 0;
+            analgain = JPC_NOMINALGAIN(tccp->qmfbid, tccp->maxrlvls,
+              rlvlno, bandinfo->orient);
+            if (!tcp->intmode) {
+                absstepsize = jpc_fix_div(jpc_inttofix(1 <<
+                  (analgain + 1)), bandinfo->synenergywt);
+            } else {
+                absstepsize = jpc_inttofix(1);
+            }   
+            cp->ccps[cmptno].stepsizes[bandno] =
+              jpc_abstorelstepsize(absstepsize,
+              cp->ccps[cmptno].prec + analgain);
+        }
+        cp->ccps[cmptno].numstepsizes = numbands;
+    }
+
+    if (!(enc->mrk = jpc_ms_create(JPC_MS_COD))) {
+        return -1;
+    }
+    cod = &enc->mrk->parms.cod;
+    cod->csty = cp->tccp.csty | cp->tcp.csty;
+    cod->compparms.csty = cp->tccp.csty | cp->tcp.csty;
+    cod->compparms.numdlvls = cp->tccp.maxrlvls - 1;
+    cod->compparms.numrlvls = cp->tccp.maxrlvls;
+    cod->prg = cp->tcp.prg;
+    cod->numlyrs = cp->tcp.numlyrs;
+    cod->compparms.cblkwidthval = JPC_COX_CBLKSIZEEXPN(cp->tccp.cblkwidthexpn);
+    cod->compparms.cblkheightval = JPC_COX_CBLKSIZEEXPN(cp->tccp.cblkheightexpn);
+    cod->compparms.cblksty = cp->tccp.cblksty;
+    cod->compparms.qmfbid = cp->tccp.qmfbid;
+    cod->mctrans = (cp->tcp.mctid != JPC_MCT_NONE);
+    if (tccp->csty & JPC_COX_PRT) {
+        for (rlvlno = 0; rlvlno < tccp->maxrlvls; ++rlvlno) {
+            cod->compparms.rlvls[rlvlno].parwidthval = tccp->prcwidthexpns[rlvlno];
+            cod->compparms.rlvls[rlvlno].parheightval = tccp->prcheightexpns[rlvlno];
+        }
+    }
+    if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+        fprintf(stderr, "cannot write COD marker\n");
+        return -1;
+    }
+    jpc_ms_destroy(enc->mrk);
+    enc->mrk = 0;
+
+    if (!(enc->mrk = jpc_ms_create(JPC_MS_QCD))) {
+        return -1;
+    }
+    qcd = &enc->mrk->parms.qcd;
+    qcd->compparms.qntsty = (tccp->qmfbid == JPC_COX_INS) ?
+      JPC_QCX_SEQNT : JPC_QCX_NOQNT;
+    qcd->compparms.numstepsizes = cp->ccps[0].numstepsizes;
+    qcd->compparms.numguard = cp->tccp.numgbits;
+    qcd->compparms.stepsizes = cp->ccps[0].stepsizes;
+    if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+        return -1;
+    }
+    /* We do not want the step size array to be freed! */
+    qcd->compparms.stepsizes = 0;
+    jpc_ms_destroy(enc->mrk);
+    enc->mrk = 0;
+
+    tccp = &cp->tccp;
+    for (cmptno = 1; cmptno < cp->numcmpts; ++cmptno) {
+        if (!(enc->mrk = jpc_ms_create(JPC_MS_QCC))) {
+            return -1;
+        }
+        qcc = &enc->mrk->parms.qcc;
+        qcc->compno = cmptno;
+        qcc->compparms.qntsty = (tccp->qmfbid == JPC_COX_INS) ?
+          JPC_QCX_SEQNT : JPC_QCX_NOQNT;
+        qcc->compparms.numstepsizes = cp->ccps[cmptno].numstepsizes;
+        qcc->compparms.numguard = cp->tccp.numgbits;
+        qcc->compparms.stepsizes = cp->ccps[cmptno].stepsizes;
+        if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
+            return -1;
+        }
+        /* We do not want the step size array to be freed! */
+        qcc->compparms.stepsizes = 0;
+        jpc_ms_destroy(enc->mrk);
+        enc->mrk = 0;
+    }
+
+#define MAINTLRLEN  2
+    mainhdrlen = jas_stream_getrwcount(enc->out) - startoff;
+    enc->len += mainhdrlen;
+    if (enc->cp->totalsize != UINT_FAST32_MAX) {
+        uint_fast32_t overhead;
+        overhead = mainhdrlen + MAINTLRLEN;
+        enc->mainbodysize = (enc->cp->totalsize >= overhead) ?
+          (enc->cp->totalsize - overhead) : 0;
+    } else {
+        enc->mainbodysize = UINT_FAST32_MAX;
+    }
+
+    return 0;
 }
 
-static int jpc_enc_encodemainbody(jpc_enc_t *enc)
+int jpc_enc_encodetiledata(jpc_enc_t *enc)
 {
-	int tileno;
-	int tilex;
-	int tiley;
-	int i;
-	jpc_sot_t *sot;
-	jpc_enc_tcmpt_t *comp;
-	jpc_enc_tcmpt_t *endcomps;
-	jpc_enc_band_t *band;
-	jpc_enc_band_t *endbands;
-	jpc_enc_rlvl_t *lvl;
-	int rlvlno;
-	jpc_qcc_t *qcc;
-	jpc_cod_t *cod;
-	int adjust;
-	int j;
-	int absbandno;
-	long numbytes;
-	long tilehdrlen;
-	long tilelen;
-	jpc_enc_tile_t *tile;
-	jpc_enc_cp_t *cp;
-	double rho;
-	uint_fast16_t lyrno;
-	uint_fast16_t cmptno;
-	int samestepsizes;
-	jpc_enc_ccp_t *ccps;
-	jpc_enc_tccp_t *tccp;
-int bandno;
-uint_fast32_t x;
-uint_fast32_t y;
-int mingbits;
-int actualnumbps;
-jpc_fix_t mxmag;
-jpc_fix_t mag;
-int numgbits;
-
-	cp = enc->cp;
-
-	/* Avoid compile warnings. */
-	numbytes = 0;
-
-	for (tileno = 0; tileno < cp->numtiles; ++tileno) {
-		tilex = tileno % cp->numhtiles;
-		tiley = tileno / cp->numhtiles;
-
-		if (!(enc->curtile = jpc_enc_tile_create(enc->cp, enc->image, tileno))) {
-			abort();
-		}
-
-		tile = enc->curtile;
-
-		if (jas_getdbglevel() >= 10) {
-			jpc_enc_dump(enc);
-		}
-
-		endcomps = &tile->tcmpts[tile->numtcmpts];
-		for (cmptno = 0, comp = tile->tcmpts; cmptno < tile->numtcmpts; ++cmptno, ++comp) {
-			if (!cp->ccps[cmptno].sgnd) {
-				adjust = 1 << (cp->ccps[cmptno].prec - 1);
-				for (i = 0; i < jas_matrix_numrows(comp->data); ++i) {
-					for (j = 0; j < jas_matrix_numcols(comp->data); ++j) {
-						*jas_matrix_getref(comp->data, i, j) -= adjust;
-					}
-				}
-			}
-		}
-
-		if (!tile->intmode) {
-				endcomps = &tile->tcmpts[tile->numtcmpts];
-				for (comp = tile->tcmpts; comp != endcomps; ++comp) {
-					jas_matrix_asl(comp->data, JPC_FIX_FRACBITS);
-				}
-		}
-
-		switch (tile->mctid) {
-		case JPC_MCT_RCT:
-assert(jas_image_numcmpts(enc->image) == 3);
-			jpc_rct(tile->tcmpts[0].data, tile->tcmpts[1].data,
-			  tile->tcmpts[2].data);
-			break;
-		case JPC_MCT_ICT:
-assert(jas_image_numcmpts(enc->image) == 3);
-			jpc_ict(tile->tcmpts[0].data, tile->tcmpts[1].data,
-			  tile->tcmpts[2].data);
-			break;
-		default:
-			break;
-		}
-
-		for (i = 0; i < jas_image_numcmpts(enc->image); ++i) {
-			comp = &tile->tcmpts[i];
-			jpc_tsfb_analyze(comp->tsfb, ((comp->qmfbid == JPC_COX_RFT) ? JPC_TSFB_RITIMODE : 0), comp->data);
-
-		}
-
-
-		endcomps = &tile->tcmpts[tile->numtcmpts];
-		for (cmptno = 0, comp = tile->tcmpts; comp != endcomps; ++cmptno, ++comp) {
-			mingbits = 0;
-			absbandno = 0;
-			/* All bands must have a corresponding quantizer step size,
-			  even if they contain no samples and are never coded. */
-			/* Some bands may not be hit by the loop below, so we must
-			  initialize all of the step sizes to a sane value. */
-			memset(comp->stepsizes, 0, sizeof(comp->stepsizes));
-			for (rlvlno = 0, lvl = comp->rlvls; rlvlno < comp->numrlvls; ++rlvlno, ++lvl) {
-				if (!lvl->bands) {
-					absbandno += rlvlno ? 3 : 1;
-					continue;
-				}
-				endbands = &lvl->bands[lvl->numbands];
-				for (band = lvl->bands; band != endbands; ++band) {
-					if (!band->data) {
-						++absbandno;
-						continue;
-					}
-					actualnumbps = 0;
-					mxmag = 0;
-					for (y = 0; y < jas_matrix_numrows(band->data); ++y) {
-						for (x = 0; x < jas_matrix_numcols(band->data); ++x) {
-							mag = abs(jas_matrix_get(band->data, y, x));
-							if (mag > mxmag) {
-								mxmag = mag;
-							}
-						}
-					}
-					if (tile->intmode) {
-						actualnumbps = jpc_firstone(mxmag) + 1;
-					} else {
-						actualnumbps = jpc_firstone(mxmag) + 1 - JPC_FIX_FRACBITS;
-					}
-					numgbits = actualnumbps - (cp->ccps[cmptno].prec - 1 +
-					  band->analgain);
-#if 0
-fprintf(stderr, "%d %d mag=%d actual=%d numgbits=%d\n", cp->ccps[cmptno].prec, band->analgain, mxmag, actualnumbps, numgbits);
-#endif
-					if (numgbits > mingbits) {
-						mingbits = numgbits;
-					}
-					if (!tile->intmode) {
-						band->absstepsize = jpc_fix_div(jpc_inttofix(1
-						  << (band->analgain + 1)),
-						  band->synweight);
-					} else {
-						band->absstepsize = jpc_inttofix(1);
-					}
-					band->stepsize = jpc_abstorelstepsize(
-					  band->absstepsize, cp->ccps[cmptno].prec +
-					  band->analgain);
-					band->numbps = cp->tccp.numgbits +
-					  JPC_QCX_GETEXPN(band->stepsize) - 1;
-
-					if ((!tile->intmode) && band->data) {
-						quantize(band->data, band->absstepsize);
-					}
-
-					comp->stepsizes[absbandno] = band->stepsize;
-					++absbandno;
-				}
-			}
-
-			assert(JPC_FIX_FRACBITS >= JPC_NUMEXTRABITS);
-			if (!tile->intmode) {
-				jas_matrix_divpow2(comp->data, JPC_FIX_FRACBITS - JPC_NUMEXTRABITS);
-			} else {
-				jas_matrix_asl(comp->data, JPC_NUMEXTRABITS);
-			}
-		}
-#if 0
-fprintf(stderr, "mingbits %d\n", mingbits);
-#endif
+assert(enc->tmpstream);
+    if (jpc_enc_encpkts(enc, enc->tmpstream)) {
+        return -1;
+    }
+    return 0;
+}
 
-		if (mingbits > cp->tccp.numgbits) {
-			fprintf(stderr, "error: too few guard bits (need at least %d)\n",
-			  mingbits);
-			return -1;
-		}
-
-		if (!(enc->tmpstream = jas_stream_memopen(0, 0))) {
-			fprintf(stderr, "cannot open tmp file\n");
-			return -1;
-		}
-
-		/* Write the tile header. */
-		if (!(enc->mrk = jpc_ms_create(JPC_MS_SOT))) {
-			return -1;
-		}
-		sot = &enc->mrk->parms.sot;
-		sot->len = 0;
-		sot->tileno = tileno;
-		sot->partno = 0;
-		sot->numparts = 1;
-		if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
-			fprintf(stderr, "cannot write SOT marker\n");
-			return -1;
-		}
-		jpc_ms_destroy(enc->mrk);
-		enc->mrk = 0;
+void quantize(jas_matrix_t *data, jpc_fix_t stepsize)
+{
+    int i;
+    int j;
+    jpc_fix_t t;
 
-/************************************************************************/
-/************************************************************************/
-/************************************************************************/
+    if (stepsize == jpc_inttofix(1)) {
+        return;
+    }
 
-		tccp = &cp->tccp;
-		for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) {
-			comp = &tile->tcmpts[cmptno];
-			if (comp->numrlvls != tccp->maxrlvls) {
-				if (!(enc->mrk = jpc_ms_create(JPC_MS_COD))) {
-					return -1;
-				}
-/* XXX = this is not really correct. we are using comp #0's precint sizes
-and other characteristics */
-				comp = &tile->tcmpts[0];
-				cod = &enc->mrk->parms.cod;
-				cod->compparms.csty = 0;
-				cod->compparms.numdlvls = comp->numrlvls - 1;
-				cod->prg = tile->prg;
-				cod->numlyrs = tile->numlyrs;
-				cod->compparms.cblkwidthval = JPC_COX_CBLKSIZEEXPN(comp->cblkwidthexpn);
-				cod->compparms.cblkheightval = JPC_COX_CBLKSIZEEXPN(comp->cblkheightexpn);
-				cod->compparms.cblksty = comp->cblksty;
-				cod->compparms.qmfbid = comp->qmfbid;
-				cod->mctrans = (tile->mctid != JPC_MCT_NONE);
-				for (i = 0; i < comp->numrlvls; ++i) {
-					cod->compparms.rlvls[i].parwidthval = comp->rlvls[i].prcwidthexpn;
-					cod->compparms.rlvls[i].parheightval = comp->rlvls[i].prcheightexpn;
-				}
-				if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
-					return -1;
-				}
-				jpc_ms_destroy(enc->mrk);
-				enc->mrk = 0;
-			}
-		}
-
-		for (cmptno = 0, comp = tile->tcmpts; cmptno < cp->numcmpts; ++cmptno, ++comp) {
-			ccps = &cp->ccps[cmptno];
-			if (ccps->numstepsizes == comp->numstepsizes) {
-				samestepsizes = 1;
-				for (bandno = 0; bandno < ccps->numstepsizes; ++bandno) {
-					if (ccps->stepsizes[bandno] != comp->stepsizes[bandno]) {
-						samestepsizes = 0;
-						break;
-					}
-				}
-			} else {
-				samestepsizes = 0;
-			}
-			if (!samestepsizes) {
-				if (!(enc->mrk = jpc_ms_create(JPC_MS_QCC))) {
-					return -1;
-				}
-				qcc = &enc->mrk->parms.qcc;
-				qcc->compno = cmptno;
-				qcc->compparms.numguard = cp->tccp.numgbits;
-				qcc->compparms.qntsty = (comp->qmfbid == JPC_COX_INS) ?
-				  JPC_QCX_SEQNT : JPC_QCX_NOQNT;
-				qcc->compparms.numstepsizes = comp->numstepsizes;
-				qcc->compparms.stepsizes = comp->stepsizes;
-				if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
-					return -1;
-				}
-				qcc->compparms.stepsizes = 0;
-				jpc_ms_destroy(enc->mrk);
-				enc->mrk = 0;
-			}
-		}
-
-		/* Write a SOD marker to indicate the end of the tile header. */
-		if (!(enc->mrk = jpc_ms_create(JPC_MS_SOD))) {
-			return -1;
-		}
-		if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
-			fprintf(stderr, "cannot write SOD marker\n");
-			return -1;
-		}
-		jpc_ms_destroy(enc->mrk);
-		enc->mrk = 0;
-tilehdrlen = jas_stream_getrwcount(enc->tmpstream);
+    for (i = 0; i < jas_matrix_numrows(data); ++i) {
+        for (j = 0; j < jas_matrix_numcols(data); ++j) {
+            t = jas_matrix_get(data, i, j);
 
-/************************************************************************/
-/************************************************************************/
-/************************************************************************/
+{
+    if (t < 0) {
+        t = jpc_fix_neg(jpc_fix_div(jpc_fix_neg(t), stepsize));
+    } else {
+        t = jpc_fix_div(t, stepsize);
+    }
+}
 
-if (jpc_enc_enccblks(enc)) {
-	abort();
-	return -1;
+            jas_matrix_set(data, i, j, t);
+        }
+    }
 }
 
-		cp = enc->cp;
-		rho = (double) (tile->brx - tile->tlx) * (tile->bry - tile->tly) /
-		  ((cp->refgrdwidth - cp->imgareatlx) * (cp->refgrdheight -
-		  cp->imgareatly));
-		tile->rawsize = cp->rawsize * rho;
-
-		for (lyrno = 0; lyrno < tile->numlyrs - 1; ++lyrno) {
-			tile->lyrsizes[lyrno] = tile->rawsize * jpc_fixtodbl(
-			  cp->tcp.ilyrrates[lyrno]);
-		}
-		tile->lyrsizes[tile->numlyrs - 1] = (cp->totalsize != UINT_FAST32_MAX) ?
-		  (rho * enc->mainbodysize) : UINT_FAST32_MAX;
-		for (lyrno = 0; lyrno < tile->numlyrs; ++lyrno) {
-			if (tile->lyrsizes[lyrno] != UINT_FAST32_MAX) {
-				if (tilehdrlen <= tile->lyrsizes[lyrno]) {
-					tile->lyrsizes[lyrno] -= tilehdrlen;
-				} else {
-					tile->lyrsizes[lyrno] = 0;
-				}
-			}
-		}
-
-		if (rateallocate(enc, tile->numlyrs, tile->lyrsizes)) {
-			return -1;
-		}
+static void calcrdslopes(jpc_enc_cblk_t *cblk)
+{
+    jpc_enc_pass_t *endpasses;
+    jpc_enc_pass_t *pass0;
+    jpc_enc_pass_t *pass1;
+    jpc_enc_pass_t *pass2;
+    jpc_flt_t slope0;
+    jpc_flt_t slope;
+    jpc_flt_t dd;
+    long dr;
+
+    endpasses = &cblk->passes[cblk->numpasses];
+    pass2 = cblk->passes;
+    slope0 = 0;
+    while (pass2 != endpasses) {
+        pass0 = 0;
+        for (pass1 = cblk->passes; pass1 != endpasses; ++pass1) {
+            dd = pass1->cumwmsedec;
+            dr = pass1->end;
+            if (pass0) {
+                dd -= pass0->cumwmsedec;
+                dr -= pass0->end;
+            }
+            if (dd <= 0) {
+                pass1->rdslope = JPC_BADRDSLOPE;
+                if (pass1 >= pass2) {
+                    pass2 = &pass1[1];
+                }
+                continue;
+            }
+            if (pass1 < pass2 && pass1->rdslope <= 0) {
+                continue;
+            }
+            if (!dr) {
+                assert(pass0);
+                pass0->rdslope = 0;
+                break;
+            }
+            slope = dd / dr;
+            if (pass0 && slope >= slope0) {
+                pass0->rdslope = 0;
+                break;
+            }
+            pass1->rdslope = slope;
+            if (pass1 >= pass2) {
+                pass2 = &pass1[1];
+            }
+            pass0 = pass1;
+            slope0 = slope;
+        }
+    }
 
 #if 0
-fprintf(stderr, "ENCODE TILE DATA\n");
+    for (pass0 = cblk->passes; pass0 != endpasses; ++pass0) {
+if (pass0->rdslope > 0.0) {
+        fprintf(stderr, "pass %02d nmsedec=%lf dec=%lf end=%d %lf\n", pass0 - cblk->passes,
+          fixtodbl(pass0->nmsedec), pass0->wmsedec, pass0->end, pass0->rdslope);
+}
+    }
 #endif
-		if (jpc_enc_encodetiledata(enc)) {
-			fprintf(stderr, "dotile failed\n");
-			return -1;
-		}
+}
 
-/************************************************************************/
-/************************************************************************/
-/************************************************************************/
 
-/************************************************************************/
-/************************************************************************/
-/************************************************************************/
 
-		tilelen = jas_stream_tell(enc->tmpstream);
+static void
+traceLayerSizes(const uint_fast32_t * const lyrSizes,
+                unsigned int          const layerCt) {
+
+    if (jas_getdbglevel() > 0) {
+        unsigned int i;
+        for (i = 0; i < layerCt; ++i) {
+            fprintf(stderr, "Layer %u size = ", i);
 
-		if (jas_stream_seek(enc->tmpstream, 6, SEEK_SET) < 0) {
-			return -1;
-		}
-		jpc_putuint32(enc->tmpstream, tilelen);
+            if (lyrSizes[i] == UINT_FAST32_MAX)
+                fprintf(stderr, "Unlimited");
+            else
+                fprintf(stderr, "%u", (unsigned)lyrSizes[i]);
+            fprintf(stderr, "\n");
+        }
+    }
+}
 
-		if (jas_stream_seek(enc->tmpstream, 0, SEEK_SET) < 0) {
-			return -1;
-		}
-		if (jpc_putdata(enc->out, enc->tmpstream, -1)) {
-			return -1;
-		}
-		enc->len += tilelen;
 
-		jas_stream_close(enc->tmpstream);
-		enc->tmpstream = 0;
 
-		jpc_enc_tile_destroy(enc->curtile);
-		enc->curtile = 0;
+static void
+computeLayerSizes(jpc_enc_t *      const encP,
+                  jpc_enc_tile_t * const tileP,
+                  jpc_enc_cp_t *   const cpP,
+                  double           const rho,
+                  long             const tilehdrlen,
+                  const char **    const errorP) {
 
-	}
+    /* Note that in allowed sizes, UINT_FAST32_MAX is a special value meaning
+       "unlimited".
+    */
 
-	return 0;
-}
+    unsigned int const lastLyrno = tileP->numlyrs - 1;
 
-int jpc_enc_encodetiledata(jpc_enc_t *enc)
-{
-assert(enc->tmpstream);
-	if (jpc_enc_encpkts(enc, enc->tmpstream)) {
-		return -1;
-	}
-	return 0;
-}
+    unsigned int lyrno;
 
-void quantize(jas_matrix_t *data, jpc_fix_t stepsize)
-{
-	int i;
-	int j;
-	jpc_fix_t t;
+    assert(tileP->numlyrs > 0);
 
-	if (stepsize == jpc_inttofix(1)) {
-		return;
-	}
+    for (lyrno = 0; lyrno < lastLyrno; ++lyrno) {
+        tileP->lyrsizes[lyrno] = tileP->rawsize * jpc_fixtodbl(
+            cpP->tcp.ilyrrates[lyrno]);
+    }
 
-	for (i = 0; i < jas_matrix_numrows(data); ++i) {
-		for (j = 0; j < jas_matrix_numcols(data); ++j) {
-			t = jas_matrix_get(data, i, j);
+    tileP->lyrsizes[lastLyrno] =
+        (cpP->totalsize != UINT_FAST32_MAX) ?
+        (rho * encP->mainbodysize) : UINT_FAST32_MAX;
 
-{
-	if (t < 0) {
-		t = jpc_fix_neg(jpc_fix_div(jpc_fix_neg(t), stepsize));
-	} else {
-		t = jpc_fix_div(t, stepsize);
-	}
-}
 
-			jas_matrix_set(data, i, j, t);
-		}
-	}
+    /* Subtract 'tilehdrlen' from every layer. */
+
+    for (lyrno = 0; lyrno < tileP->numlyrs; ++lyrno) {
+        if (tileP->lyrsizes[lyrno] != UINT_FAST32_MAX) {
+            if (tilehdrlen <= tileP->lyrsizes[lyrno]) {
+                tileP->lyrsizes[lyrno] -= tilehdrlen;
+            } else {
+                tileP->lyrsizes[lyrno] = 0;
+            }
+        }
+    }
+
+    if (tileP->lyrsizes[lastLyrno] < 1)
+        pm_asprintf(errorP, "Cannot make image that small (%u bytes).  "
+                    "Even with pixels compressed as far as possible, metadata "
+                    "would exceed the limit",
+                    (unsigned)cpP->totalsize);
+    else
+        *errorP = NULL;
+
+    traceLayerSizes(tileP->lyrsizes, tileP->numlyrs);
 }
 
-static void calcrdslopes(jpc_enc_cblk_t *cblk)
+
+
+static void dump_layeringinfo(jpc_enc_t *enc)
 {
-	jpc_enc_pass_t *endpasses;
-	jpc_enc_pass_t *pass0;
-	jpc_enc_pass_t *pass1;
-	jpc_enc_pass_t *pass2;
-	jpc_flt_t slope0;
-	jpc_flt_t slope;
-	jpc_flt_t dd;
-	long dr;
-
-	endpasses = &cblk->passes[cblk->numpasses];
-	pass2 = cblk->passes;
-	slope0 = 0;
-	while (pass2 != endpasses) {
-		pass0 = 0;
-		for (pass1 = cblk->passes; pass1 != endpasses; ++pass1) {
-			dd = pass1->cumwmsedec;
-			dr = pass1->end;
-			if (pass0) {
-				dd -= pass0->cumwmsedec;
-				dr -= pass0->end;
-			}
-			if (dd <= 0) {
-				pass1->rdslope = JPC_BADRDSLOPE;
-				if (pass1 >= pass2) {
-					pass2 = &pass1[1];
-				}
-				continue;
-			}
-			if (pass1 < pass2 && pass1->rdslope <= 0) {
-				continue;
-			}
-			if (!dr) {
-				assert(pass0);
-				pass0->rdslope = 0;
-				break;
-			}
-			slope = dd / dr;
-			if (pass0 && slope >= slope0) {
-				pass0->rdslope = 0;
-				break;
-			}
-			pass1->rdslope = slope;
-			if (pass1 >= pass2) {
-				pass2 = &pass1[1];
-			}
-			pass0 = pass1;
-			slope0 = slope;
-		}
-	}
 
-#if 0
-	for (pass0 = cblk->passes; pass0 != endpasses; ++pass0) {
-if (pass0->rdslope > 0.0) {
-		fprintf(stderr, "pass %02d nmsedec=%lf dec=%lf end=%d %lf\n", pass0 - cblk->passes,
-		  fixtodbl(pass0->nmsedec), pass0->wmsedec, pass0->end, pass0->rdslope);
+    jpc_enc_tcmpt_t *tcmpt;
+    uint_fast16_t tcmptno;
+    jpc_enc_rlvl_t *rlvl;
+    uint_fast16_t rlvlno;
+    jpc_enc_band_t *band;
+    uint_fast16_t bandno;
+    jpc_enc_prc_t *prc;
+    uint_fast32_t prcno;
+    jpc_enc_cblk_t *cblk;
+    uint_fast16_t cblkno;
+    jpc_enc_pass_t *pass;
+    uint_fast16_t passno;
+    int lyrno;
+    jpc_enc_tile_t *tile;
+
+    tile = enc->curtile;
+
+    for (lyrno = 0; lyrno < tile->numlyrs; ++lyrno) {
+        fprintf(stderr, "lyrno = %02d\n", lyrno);
+        for (tcmptno = 0, tcmpt = tile->tcmpts; tcmptno < tile->numtcmpts;
+          ++tcmptno, ++tcmpt) {
+            for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
+              ++rlvlno, ++rlvl) {
+                if (!rlvl->bands) {
+                    continue;
+                }
+                for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
+                  ++bandno, ++band) {
+                    if (!band->data) {
+                        continue;
+                    }
+                    for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs;
+                      ++prcno, ++prc) {
+                        if (!prc->cblks) {
+                            continue;
+                        }
+                        for (cblkno = 0, cblk = prc->cblks; cblkno <
+                          prc->numcblks; ++cblkno, ++cblk) {
+                            for (passno = 0, pass = cblk->passes;
+                                 passno < cblk->numpasses &&
+                                     pass->lyrno == lyrno;
+                                 ++passno, ++pass) {
+                                fprintf(stderr,
+                                        "lyrno=%02d cmptno=%02d "
+                                        "rlvlno=%02d bandno=%02d "
+                                        "prcno=%02d cblkno=%03d "
+                                        "passno=%03d\n",
+                                        lyrno, (int)tcmptno, (int)rlvlno,
+                                        (int)bandno, (int)prcno, (int) cblkno,
+                                        (int)passno);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
 }
-	}
-#endif
+
+
+
+
+static void
+trace_layeringinfo(jpc_enc_t * const encP) {
+
+    if (jas_getdbglevel() >= 5)
+        dump_layeringinfo(encP);
 }
 
-static void dump_layeringinfo(jpc_enc_t *enc)
-{
 
-	jpc_enc_tcmpt_t *tcmpt;
-	uint_fast16_t tcmptno;
-	jpc_enc_rlvl_t *rlvl;
-	uint_fast16_t rlvlno;
-	jpc_enc_band_t *band;
-	uint_fast16_t bandno;
-	jpc_enc_prc_t *prc;
-	uint_fast32_t prcno;
-	jpc_enc_cblk_t *cblk;
-	uint_fast16_t cblkno;
-	jpc_enc_pass_t *pass;
-	uint_fast16_t passno;
-	int lyrno;
-	jpc_enc_tile_t *tile;
-
-	tile = enc->curtile;
-
-	for (lyrno = 0; lyrno < tile->numlyrs; ++lyrno) {
-		fprintf(stderr, "lyrno = %02d\n", lyrno);
-		for (tcmptno = 0, tcmpt = tile->tcmpts; tcmptno < tile->numtcmpts;
-		  ++tcmptno, ++tcmpt) {
-			for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
-			  ++rlvlno, ++rlvl) {
-				if (!rlvl->bands) {
-					continue;
-				}
-				for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
-				  ++bandno, ++band) {
-					if (!band->data) {
-						continue;
-					}
-					for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs;
-					  ++prcno, ++prc) {
-						if (!prc->cblks) {
-							continue;
-						}
-						for (cblkno = 0, cblk = prc->cblks; cblkno <
-						  prc->numcblks; ++cblkno, ++cblk) {
-							for (passno = 0, pass = cblk->passes; passno <
-							  cblk->numpasses && pass->lyrno == lyrno;
-							  ++passno, ++pass) {
-								fprintf(stderr, "lyrno=%02d cmptno=%02d rlvlno=%02d bandno=%02d prcno=%02d cblkno=%03d passno=%03d\n", lyrno, tcmptno, rlvlno, bandno, prcno, cblkno, passno);
-							}
-						}
-					}
-				}
-			}
-		}
-	}
+
+static void
+validateCumlensIncreases(const uint_fast32_t * const cumlens,
+                         unsigned int          const numlyrs) {
+    unsigned int lyrno;
+
+    for (lyrno = 1; lyrno < numlyrs - 1; ++lyrno) {
+        if (cumlens[lyrno - 1] > cumlens[lyrno]) {
+            abort();
+        }
+    }
 }
 
-int rateallocate(jpc_enc_t *enc, int numlyrs, uint_fast32_t *cumlens)
-{
-	jpc_flt_t lo;
-	jpc_flt_t hi;
-	jas_stream_t *out;
-	long cumlen;
-	int lyrno;
-	jpc_flt_t thresh;
-	jpc_flt_t goodthresh;
-	int success;
-	long pos;
-	long oldpos;
-	int numiters;
-
-	jpc_enc_tcmpt_t *comp;
-	jpc_enc_tcmpt_t *endcomps;
-	jpc_enc_rlvl_t *lvl;
-	jpc_enc_rlvl_t *endlvls;
-	jpc_enc_band_t *band;
-	jpc_enc_band_t *endbands;
-	jpc_enc_cblk_t *cblk;
-	jpc_enc_cblk_t *endcblks;
-	jpc_enc_pass_t *pass;
-	jpc_enc_pass_t *endpasses;
-	jpc_enc_pass_t *pass1;
-	jpc_flt_t mxrdslope;
-	jpc_flt_t mnrdslope;
-	jpc_enc_tile_t *tile;
-	jpc_enc_prc_t *prc;
-	uint_fast32_t prcno;
-
-	tile = enc->curtile;
-
-	for (lyrno = 1; lyrno < numlyrs - 1; ++lyrno) {
-		if (cumlens[lyrno - 1] > cumlens[lyrno]) {
-			abort();
-		}
-	}
-
-	if (!(out = jas_stream_memopen(0, 0))) {
-		return -1;
-	}
-
-
-	/* Find minimum and maximum R-D slope values. */
-	mnrdslope = DBL_MAX;
-	mxrdslope = 0;
-	endcomps = &tile->tcmpts[tile->numtcmpts];
-	for (comp = tile->tcmpts; comp != endcomps; ++comp) {
-		endlvls = &comp->rlvls[comp->numrlvls];
-		for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
-			if (!lvl->bands) {
-				continue;
-			}
-			endbands = &lvl->bands[lvl->numbands];
-			for (band = lvl->bands; band != endbands; ++band) {
-				if (!band->data) {
-					continue;
-				}
-				for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) {
-					if (!prc->cblks) {
-						continue;
-					}
-					endcblks = &prc->cblks[prc->numcblks];
-					for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
-						calcrdslopes(cblk);
-						endpasses = &cblk->passes[cblk->numpasses];
-						for (pass = cblk->passes; pass != endpasses; ++pass) {
-							if (pass->rdslope > 0) {
-								if (pass->rdslope < mnrdslope) {
-									mnrdslope = pass->rdslope;
-								}
-								if (pass->rdslope > mxrdslope) {
-									mxrdslope = pass->rdslope;
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-if (jas_getdbglevel()) {
-	fprintf(stderr, "min rdslope = %f max rdslope = %f\n", mnrdslope, mxrdslope);
+
+
+static void
+findMinMaxRDSlopeValues(jpc_enc_tile_t * const tileP,
+                        jpc_flt_t *      const mnrdslopeP,
+                        jpc_flt_t *      const mxrdslopeP) {
+/*----------------------------------------------------------------------------
+  Find minimum and maximum R-D slope values.
+-----------------------------------------------------------------------------*/
+    jpc_flt_t mxrdslope;
+    jpc_flt_t mnrdslope;
+    jpc_enc_tcmpt_t * endcomps;
+    jpc_enc_tcmpt_t * compP;
+
+    mnrdslope = DBL_MAX;
+    mxrdslope = 0;
+    endcomps = &tileP->tcmpts[tileP->numtcmpts];
+
+    for (compP = tileP->tcmpts; compP != endcomps; ++compP) {
+        jpc_enc_rlvl_t * const endlvlsP = &compP->rlvls[compP->numrlvls];
+
+        jpc_enc_rlvl_t * lvlP;
+
+        for (lvlP = compP->rlvls; lvlP != endlvlsP; ++lvlP) {
+            jpc_enc_band_t * endbandsP;
+            jpc_enc_band_t * bandP;
+
+            if (lvlP->bands) {
+                endbandsP = &lvlP->bands[lvlP->numbands];
+                for (bandP = lvlP->bands; bandP != endbandsP; ++bandP) {
+                    uint_fast32_t prcno;
+                    jpc_enc_prc_t * prcP;
+
+                    if (bandP->data) {
+                        for (prcno = 0, prcP = bandP->prcs;
+                             prcno < lvlP->numprcs;
+                             ++prcno, ++prcP) {
+
+                            jpc_enc_cblk_t * endcblksP;
+                            jpc_enc_cblk_t * cblkP;
+
+                            if (prcP->cblks) {
+                                endcblksP = &prcP->cblks[prcP->numcblks];
+                                for (cblkP = prcP->cblks;
+                                     cblkP != endcblksP;
+                                     ++cblkP) {
+                                    jpc_enc_pass_t * endpassesP;
+                                    jpc_enc_pass_t * passP;
+
+                                    calcrdslopes(cblkP);
+                                    endpassesP =
+                                        &cblkP->passes[cblkP->numpasses];
+                                    for (passP = cblkP->passes;
+                                         passP != endpassesP;
+                                         ++passP) {
+                                        if (passP->rdslope > 0) {
+                                            mnrdslope =
+                                                MIN(passP->rdslope, mnrdslope);
+                                            mxrdslope =
+                                                MAX(passP->rdslope, mxrdslope);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    trace("min rdslope = %f max rdslope = %f", mnrdslope, mxrdslope);
+
+    *mnrdslopeP = mnrdslope;
+    *mxrdslopeP = mxrdslope;
 }
 
-	jpc_init_t2state(enc, 1);
-
-	for (lyrno = 0; lyrno < numlyrs; ++lyrno) {
-
-		lo = mnrdslope;
-		hi = mxrdslope;
-
-		success = 0;
-		goodthresh = 0;
-		numiters = 0;
-
-		do {
-
-			cumlen = cumlens[lyrno];
-			if (cumlen == UINT_FAST32_MAX) {
-				/* Only the last layer can be free of a rate
-				  constraint (e.g., for lossless coding). */
-				assert(lyrno == numlyrs - 1);
-				goodthresh = -1;
-				success = 1;
-				break;
-			}
-
-			thresh = (lo + hi) / 2;
-
-			/* Save the tier 2 coding state. */
-			jpc_save_t2state(enc);
-			oldpos = jas_stream_tell(out);
-			assert(oldpos >= 0);
-
-			/* Assign all passes with R-D slopes greater than or
-			  equal to the current threshold to this layer. */
-			endcomps = &tile->tcmpts[tile->numtcmpts];
-			for (comp = tile->tcmpts; comp != endcomps; ++comp) {
-				endlvls = &comp->rlvls[comp->numrlvls];
-				for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
-					if (!lvl->bands) {
-						continue;
-					}
-					endbands = &lvl->bands[lvl->numbands];
-					for (band = lvl->bands; band != endbands; ++band) {
-						if (!band->data) {
-							continue;
-						}
-						for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) {
-							if (!prc->cblks) {
-								continue;
-							}
-							endcblks = &prc->cblks[prc->numcblks];
-							for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
-								if (cblk->curpass) {
-									endpasses = &cblk->passes[cblk->numpasses];
-									pass1 = cblk->curpass;
-									for (pass = cblk->curpass; pass != endpasses; ++pass) {
-										if (pass->rdslope >= thresh) {
-											pass1 = &pass[1];
-										}
-									}
-									for (pass = cblk->curpass; pass != pass1; ++pass) {
-										pass->lyrno = lyrno;
-									}
-									for (; pass != endpasses; ++pass) {
-										pass->lyrno = -1;
-									}
-								}
-							}
-						}
-					}
-				}
-			}
-
-			/* Perform tier 2 coding. */
-			endcomps = &tile->tcmpts[tile->numtcmpts];
-			for (comp = tile->tcmpts; comp != endcomps; ++comp) {
-				endlvls = &comp->rlvls[comp->numrlvls];
-				for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
-					if (!lvl->bands) {
-						continue;
-					}
-					for (prcno = 0; prcno < lvl->numprcs; ++prcno) {
-						if (jpc_enc_encpkt(enc, out, comp - tile->tcmpts, lvl - comp->rlvls, prcno, lyrno)) {
-							return -1;
-						}
-					}
-				}
-			}
-
-			pos = jas_stream_tell(out);
-
-			/* Check the rate constraint. */
-			assert(pos >= 0);
-			if (pos > cumlen) {
-				/* The rate is too high. */
-				lo = thresh;
-			} else if (pos <= cumlen) {
-				/* The rate is low enough, so try higher. */
-				hi = thresh;
-				if (!success || thresh < goodthresh) {
-					goodthresh = thresh;
-					success = 1;
-				}
-			}
-
-			/* Save the tier 2 coding state. */
-			jpc_restore_t2state(enc);
-			if (jas_stream_seek(out, oldpos, SEEK_SET) < 0) {
-				abort();
-			}
-
-if (jas_getdbglevel()) {
-fprintf(stderr, "maxlen=%08ld actuallen=%08ld thresh=%f\n", cumlen, pos, thresh);
+
+
+static void
+performTier2CodingOneLayer(jpc_enc_t *      const encP,
+                           jpc_enc_tile_t * const tileP,
+                           unsigned int     const lyrno,
+                           jas_stream_t *   const outP,
+                           const char **    const errorP) {
+/*----------------------------------------------------------------------------
+  Encode Layer 'lyrno' of tile *tileP to stream *outP.
+
+  Use the pass assignment already in *tileP.
+-----------------------------------------------------------------------------*/
+    jpc_enc_tcmpt_t * const endcompsP = &tileP->tcmpts[tileP->numtcmpts];
+
+    jpc_enc_tcmpt_t * compP;
+
+    *errorP = NULL;  /* initial assumption */
+
+    for (compP = tileP->tcmpts; compP != endcompsP && !*errorP; ++compP) {
+        jpc_enc_rlvl_t * const endlvlsP = &compP->rlvls[compP->numrlvls];
+
+        jpc_enc_rlvl_t * lvlP;
+
+        for (lvlP = compP->rlvls; lvlP != endlvlsP && !*errorP; ++lvlP) {
+            if (lvlP->bands) {
+                uint_fast32_t prcno;
+                for (prcno = 0; prcno < lvlP->numprcs && !*errorP; ++prcno) {
+                    int rc;
+                    rc = jpc_enc_encpkt(encP, outP, compP - tileP->tcmpts,
+                                        lvlP - compP->rlvls, prcno, lyrno);
+                    if (rc != 0)
+                        pm_asprintf(errorP, "jpc_enc_encpkt() failed on "
+                                    "precinct %u", (unsigned)prcno);
+                }
+            }
+        }
+    }
 }
 
-			++numiters;
-		} while (lo < hi - 1e-3 && numiters < 32);
 
-		if (!success) {
-			fprintf(stderr, "warning: empty layer generated\n");
-		}
 
-if (jas_getdbglevel()) {
-fprintf(stderr, "success %d goodthresh %f\n", success, goodthresh);
+static void
+assignHighSlopePassesToLayer(jpc_enc_t *      const encP,
+                             jpc_enc_tile_t * const tileP,
+                             unsigned int     const lyrno,
+                             bool             const haveThresh,
+                             jpc_flt_t        const thresh) {
+/*----------------------------------------------------------------------------
+  Assign all passes with R-D slopes greater than or equal to 'thresh' to layer
+  'lyrno' and the rest to no layer.
+
+  If 'haveThresh' is false, assign all passes to no layer.
+-----------------------------------------------------------------------------*/
+    jpc_enc_tcmpt_t * endcompsP;
+    jpc_enc_tcmpt_t * compP;
+
+    endcompsP = &tileP->tcmpts[tileP->numtcmpts];
+    for (compP = tileP->tcmpts; compP != endcompsP; ++compP) {
+        jpc_enc_rlvl_t * const endlvlsP = &compP->rlvls[compP->numrlvls];
+
+        jpc_enc_rlvl_t * lvlP;
+
+        for (lvlP = compP->rlvls; lvlP != endlvlsP; ++lvlP) {
+            if (lvlP->bands) {
+                jpc_enc_band_t * const endbandsP =
+                    &lvlP->bands[lvlP->numbands];
+                jpc_enc_band_t * bandP;
+                for (bandP = lvlP->bands; bandP != endbandsP; ++bandP) {
+                    if (bandP->data) {
+                        jpc_enc_prc_t * prcP;
+                        uint_fast32_t prcno;
+                        for (prcno = 0, prcP = bandP->prcs;
+                             prcno < lvlP->numprcs;
+                             ++prcno, ++prcP) {
+                            if (prcP->cblks) {
+                                jpc_enc_cblk_t * const endcblksP =
+                                    &prcP->cblks[prcP->numcblks];
+                                jpc_enc_cblk_t * cblkP;
+                                for (cblkP = prcP->cblks;
+                                     cblkP != endcblksP;
+                                     ++cblkP) {
+                                    if (cblkP->curpass) {
+                                        jpc_enc_pass_t *  const endpassesP =
+                                            &cblkP->passes[cblkP->numpasses];
+                                        jpc_enc_pass_t * pass1P;
+                                        jpc_enc_pass_t * passP;
+
+                                        pass1P = cblkP->curpass;
+                                        if (haveThresh) {
+                                            jpc_enc_pass_t * passP;
+                                            for (passP = cblkP->curpass;
+                                                 passP != endpassesP;
+                                                 ++passP) {
+                                                if (passP->rdslope >= thresh)
+                                                    pass1P = passP + 1;
+                                            }
+                                        }
+                                        for (passP = cblkP->curpass;
+                                             passP != pass1P;
+                                             ++passP) {
+                                            passP->lyrno = lyrno;
+                                        }
+                                        for (; passP != endpassesP; ++passP) {
+                                            passP->lyrno = -1;
+                                        }
+                                    
+                                    }   
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
 }
 
-		/* Assign all passes with R-D slopes greater than or
-		  equal to the selected threshold to this layer. */
-		endcomps = &tile->tcmpts[tile->numtcmpts];
-		for (comp = tile->tcmpts; comp != endcomps; ++comp) {
-			endlvls = &comp->rlvls[comp->numrlvls];
-			for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
-if (!lvl->bands) {
-	continue;
+
+
+static void
+doLayer(jpc_enc_t *      const encP,
+        jpc_enc_tile_t * const tileP,
+        unsigned int     const lyrno,
+        uint_fast32_t    const allowedSize,
+        jpc_flt_t        const mnrdslope,
+        jpc_flt_t        const mxrdslope,
+        jas_stream_t *   const outP,
+        const char **    const errorP) {
+/*----------------------------------------------------------------------------
+   Assign passes to layer 'lyrno' such that the cumulative size through
+   this layer is as close as possible to, but not exceeding, 'allowedSize'.
+-----------------------------------------------------------------------------*/
+    bool      haveGoodThresh;
+    jpc_flt_t goodThresh;
+
+    if (allowedSize == UINT_FAST32_MAX) {
+        /* There's no rate constraint (This can be true of the last layer,
+           e.g. for lossless coding). */
+        goodThresh = -1;
+        haveGoodThresh = true;
+        *errorP = NULL;
+    } else {
+        /* Iterate through successive approximations of the threshold, finding
+           the threshold that gets us closest to 'allowedSize' without going
+           over.  In each iteration, we do the full encoding, note the size,
+           and then restore the previous state.
+        */
+        long pos;
+        jpc_flt_t lo;
+        jpc_flt_t hi;
+        unsigned int numiters;
+
+        lo = mnrdslope;  /* initial value */
+        hi = mxrdslope;  /* initial value */
+        numiters = 0;    /* initial value */
+        haveGoodThresh = false;  /* initial value */
+        goodThresh = 0;     /* initial value */
+
+        do {
+            if (allowedSize == UINT_FAST32_MAX) {
+                /* There's no rate constraint (This can be true of the last
+                   layer, e.g. for lossless coding). */
+                goodThresh = -1;
+                haveGoodThresh = true;
+            } else {
+                jpc_flt_t const thresh = (lo + hi) / 2;
+
+                int rc;
+                long oldpos;
+
+                /* Save the tier 2 coding state. */
+                jpc_save_t2state(encP);
+                oldpos = jas_stream_tell(outP);
+                assert(oldpos >= 0);
+
+                assignHighSlopePassesToLayer(encP, tileP, lyrno, true, thresh);
+
+                performTier2CodingOneLayer(encP, tileP, lyrno, outP, errorP);
+
+                if (!*errorP) {
+                    pos = jas_stream_tell(outP);
+
+                    /* Check the rate constraint. */
+                    assert(pos >= 0);
+                    if (pos > allowedSize) {
+                        /* The rate is too high. */
+                        lo = thresh;
+                    } else if (pos <= allowedSize) {
+                        /* The rate is low enough, so try higher. */
+                        hi = thresh;
+                        if (!haveGoodThresh || thresh < goodThresh) {
+                            goodThresh = thresh;
+                            haveGoodThresh = true;
+                        }
+                    }
+                }
+                /* Restore the tier 2 coding state. */
+                jpc_restore_t2state(encP);
+                rc = jas_stream_seek(outP, oldpos, SEEK_SET);
+                if (rc < 0)
+                    abort();
+
+                trace("iter %u: allowedlen=%08ld actuallen=%08ld thresh=%f",
+                      numiters, allowedSize, pos, thresh);
+            }
+            ++numiters;
+        } while (lo < hi - 1e-3 && numiters < 32 && !*errorP);
+    }
+
+    if (!*errorP) {
+        if (!haveGoodThresh)
+            fprintf(stderr, "warning: empty layer generated\n");
+
+        trace("haveGoodThresh %u goodthresh %f", haveGoodThresh, goodThresh);
+
+        assignHighSlopePassesToLayer(encP, tileP, lyrno,
+                                     haveGoodThresh, goodThresh);
+
+        performTier2CodingOneLayer(encP, tileP, lyrno, outP, errorP);
+    }
 }
-				endbands = &lvl->bands[lvl->numbands];
-				for (band = lvl->bands; band != endbands; ++band) {
-					if (!band->data) {
-						continue;
-					}
-					for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) {
-						if (!prc->cblks) {
-							continue;
-						}
-						endcblks = &prc->cblks[prc->numcblks];
-						for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
-							if (cblk->curpass) {
-								endpasses = &cblk->passes[cblk->numpasses];
-								pass1 = cblk->curpass;
-								if (success) {
-									for (pass = cblk->curpass; pass != endpasses; ++pass) {
-										if (pass->rdslope >= goodthresh) {
-											pass1 = &pass[1];
-										}
-									}
-								}
-								for (pass = cblk->curpass; pass != pass1; ++pass) {
-									pass->lyrno = lyrno;
-								}
-								for (; pass != endpasses; ++pass) {
-									pass->lyrno = -1;
-								}
-							}
-						}
-					}
-				}
-			}
-		}
-
-		/* Perform tier 2 coding. */
-		endcomps = &tile->tcmpts[tile->numtcmpts];
-		for (comp = tile->tcmpts; comp != endcomps; ++comp) {
-			endlvls = &comp->rlvls[comp->numrlvls];
-			for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
-				if (!lvl->bands) {
-					continue;
-				}
-				for (prcno = 0; prcno < lvl->numprcs; ++prcno) {
-					if (jpc_enc_encpkt(enc, out, comp - tile->tcmpts, lvl - comp->rlvls, prcno, lyrno)) {
-						return -1;
-					}
-				}
-			}
-		}
-	}
-
-	if (jas_getdbglevel() >= 5) {
-		dump_layeringinfo(enc);
-	}
-
-	jas_stream_close(out);
-
-	JAS_DBGLOG(10, ("done doing rateallocation\n"));
-#if 0
-fprintf(stderr, "DONE RATE ALLOCATE\n");
-#endif
 
-	return 0;
+
+
+static void
+performTier2Coding(jpc_enc_t *     const encP,
+                   unsigned int    const numlyrs,
+                   uint_fast32_t * const cumlens,
+                   const char **   const errorP) {
+/*----------------------------------------------------------------------------
+   Encode in 'numlyrs' layers, such that at each layer L, the size is
+   cumlens[L].
+-----------------------------------------------------------------------------*/
+    jpc_enc_tile_t * const tileP = encP->curtile;
+
+    jas_stream_t * outP;
+    unsigned int lyrno;
+    jpc_flt_t mnrdslope;
+    jpc_flt_t mxrdslope;
+
+    validateCumlensIncreases(cumlens, numlyrs);
+
+    outP = jas_stream_memopen(0, 0);
+
+    if (!outP)
+        pm_asprintf(errorP, "jas_stream_memopen() failed");
+    else {
+        findMinMaxRDSlopeValues(tileP, &mnrdslope, &mxrdslope);
+
+        jpc_init_t2state(encP, 1);
+
+        for (lyrno = 0, *errorP = NULL;
+             lyrno < numlyrs && !*errorP;
+             ++lyrno) {
+            doLayer(encP, tileP, lyrno, cumlens[lyrno],
+                    mnrdslope, mxrdslope, outP, errorP);
+        }
+
+        if (!*errorP) {
+            trace_layeringinfo(encP);
+
+            jas_stream_close(outP);
+        }
+    }
+    JAS_DBGLOG(10, ("done doing rateallocation\n"));
 }
 
-/******************************************************************************\
+/*****************************************************************************\
 * Tile constructors and destructors.
-\******************************************************************************/
+\*****************************************************************************/
 
 jpc_enc_tile_t *jpc_enc_tile_create(jpc_enc_cp_t *cp, jas_image_t *image, int tileno)
 {
-	jpc_enc_tile_t *tile;
-	uint_fast32_t htileno;
-	uint_fast32_t vtileno;
-	uint_fast16_t lyrno;
-	uint_fast16_t cmptno;
-	jpc_enc_tcmpt_t *tcmpt;
-
-	if (!(tile = jas_malloc(sizeof(jpc_enc_tile_t)))) {
-		goto error;
-	}
-
-	/* Initialize a few members used in error recovery. */
-	tile->tcmpts = 0;
-	tile->lyrsizes = 0;
-	tile->numtcmpts = cp->numcmpts;
-	tile->pi = 0;
-
-	tile->tileno = tileno;
-	htileno = tileno % cp->numhtiles;
-	vtileno = tileno / cp->numhtiles;
-
-	/* Calculate the coordinates of the top-left and bottom-right
-	  corners of the tile. */
-	tile->tlx = JAS_MAX(cp->tilegrdoffx + htileno * cp->tilewidth,
-	  cp->imgareatlx);
-	tile->tly = JAS_MAX(cp->tilegrdoffy + vtileno * cp->tileheight,
-	  cp->imgareatly);
-	tile->brx = JAS_MIN(cp->tilegrdoffx + (htileno + 1) * cp->tilewidth,
-	  cp->refgrdwidth);
-	tile->bry = JAS_MIN(cp->tilegrdoffy + (vtileno + 1) * cp->tileheight,
-	  cp->refgrdheight);
-
-	/* Initialize some tile coding parameters. */
-	tile->intmode = cp->tcp.intmode;
-	tile->csty = cp->tcp.csty;
-	tile->prg = cp->tcp.prg;
-	tile->mctid = cp->tcp.mctid;
-
-	tile->numlyrs = cp->tcp.numlyrs;
-	if (!(tile->lyrsizes = jas_malloc(tile->numlyrs *
-	  sizeof(uint_fast32_t)))) {
-		goto error;
-	}
-	for (lyrno = 0; lyrno < tile->numlyrs; ++lyrno) {
-		tile->lyrsizes[lyrno] = 0;
-	}
-
-	/* Allocate an array for the per-tile-component information. */
-	if (!(tile->tcmpts = jas_malloc(cp->numcmpts * sizeof(jpc_enc_tcmpt_t)))) {
-		goto error;
-	}
-	/* Initialize a few members critical for error recovery. */
-	for (cmptno = 0, tcmpt = tile->tcmpts; cmptno < cp->numcmpts;
-	  ++cmptno, ++tcmpt) {
-		tcmpt->rlvls = 0;
-		tcmpt->tsfb = 0;
-		tcmpt->data = 0;
-	}
-	/* Initialize the per-tile-component information. */
-	for (cmptno = 0, tcmpt = tile->tcmpts; cmptno < cp->numcmpts;
-	  ++cmptno, ++tcmpt) {
-		if (!tcmpt_create(tcmpt, cp, image, tile)) {
-			goto error;
-		}
-	}
-
-	/* Initialize the synthesis weights for the MCT. */
-	switch (tile->mctid) {
-	case JPC_MCT_RCT:
-		tile->tcmpts[0].synweight = jpc_dbltofix(sqrt(3.0));
-		tile->tcmpts[1].synweight = jpc_dbltofix(sqrt(0.6875));
-		tile->tcmpts[2].synweight = jpc_dbltofix(sqrt(0.6875));
-		break;
-	case JPC_MCT_ICT:
-		tile->tcmpts[0].synweight = jpc_dbltofix(sqrt(3.0000));
-		tile->tcmpts[1].synweight = jpc_dbltofix(sqrt(3.2584));
-		tile->tcmpts[2].synweight = jpc_dbltofix(sqrt(2.4755));
-		break;
-	default:
-	case JPC_MCT_NONE:
-		for (cmptno = 0, tcmpt = tile->tcmpts; cmptno < cp->numcmpts;
-		  ++cmptno, ++tcmpt) {
-			tcmpt->synweight = JPC_FIX_ONE;
-		}
-		break;
-	}
-
-	if (!(tile->pi = jpc_enc_pi_create(cp, tile))) {
-		goto error;
-	}
-
-	return tile;
+    jpc_enc_tile_t *tile;
+    uint_fast32_t htileno;
+    uint_fast32_t vtileno;
+    uint_fast16_t lyrno;
+    uint_fast16_t cmptno;
+    jpc_enc_tcmpt_t *tcmpt;
+
+    if (!(tile = jas_malloc(sizeof(jpc_enc_tile_t)))) {
+        goto error;
+    }
+
+    /* Initialize a few members used in error recovery. */
+    tile->tcmpts = 0;
+    tile->lyrsizes = 0;
+    tile->numtcmpts = cp->numcmpts;
+    tile->pi = 0;
+
+    tile->tileno = tileno;
+    htileno = tileno % cp->numhtiles;
+    vtileno = tileno / cp->numhtiles;
+
+    /* Calculate the coordinates of the top-left and bottom-right
+      corners of the tile. */
+    tile->tlx = JAS_MAX(cp->tilegrdoffx + htileno * cp->tilewidth,
+      cp->imgareatlx);
+    tile->tly = JAS_MAX(cp->tilegrdoffy + vtileno * cp->tileheight,
+      cp->imgareatly);
+    tile->brx = JAS_MIN(cp->tilegrdoffx + (htileno + 1) * cp->tilewidth,
+      cp->refgrdwidth);
+    tile->bry = JAS_MIN(cp->tilegrdoffy + (vtileno + 1) * cp->tileheight,
+      cp->refgrdheight);
+
+    /* Initialize some tile coding parameters. */
+    tile->intmode = cp->tcp.intmode;
+    tile->csty = cp->tcp.csty;
+    tile->prg = cp->tcp.prg;
+    tile->mctid = cp->tcp.mctid;
+
+    tile->numlyrs = cp->tcp.numlyrs;
+    if (!(tile->lyrsizes = jas_malloc(tile->numlyrs *
+      sizeof(uint_fast32_t)))) {
+        goto error;
+    }
+    for (lyrno = 0; lyrno < tile->numlyrs; ++lyrno) {
+        tile->lyrsizes[lyrno] = 0;
+    }
+
+    /* Allocate an array for the per-tile-component information. */
+    if (!(tile->tcmpts = jas_malloc(cp->numcmpts * sizeof(jpc_enc_tcmpt_t)))) {
+        goto error;
+    }
+    /* Initialize a few members critical for error recovery. */
+    for (cmptno = 0, tcmpt = tile->tcmpts; cmptno < cp->numcmpts;
+      ++cmptno, ++tcmpt) {
+        tcmpt->rlvls = 0;
+        tcmpt->tsfb = 0;
+        tcmpt->data = 0;
+    }
+    /* Initialize the per-tile-component information. */
+    for (cmptno = 0, tcmpt = tile->tcmpts; cmptno < cp->numcmpts;
+      ++cmptno, ++tcmpt) {
+        if (!tcmpt_create(tcmpt, cp, image, tile)) {
+            goto error;
+        }
+    }
+
+    /* Initialize the synthesis weights for the MCT. */
+    switch (tile->mctid) {
+    case JPC_MCT_RCT:
+        tile->tcmpts[0].synweight = jpc_dbltofix(sqrt(3.0));
+        tile->tcmpts[1].synweight = jpc_dbltofix(sqrt(0.6875));
+        tile->tcmpts[2].synweight = jpc_dbltofix(sqrt(0.6875));
+        break;
+    case JPC_MCT_ICT:
+        tile->tcmpts[0].synweight = jpc_dbltofix(sqrt(3.0000));
+        tile->tcmpts[1].synweight = jpc_dbltofix(sqrt(3.2584));
+        tile->tcmpts[2].synweight = jpc_dbltofix(sqrt(2.4755));
+        break;
+    default:
+    case JPC_MCT_NONE:
+        for (cmptno = 0, tcmpt = tile->tcmpts; cmptno < cp->numcmpts;
+          ++cmptno, ++tcmpt) {
+            tcmpt->synweight = JPC_FIX_ONE;
+        }
+        break;
+    }
+
+    if (!(tile->pi = jpc_enc_pi_create(cp, tile))) {
+        goto error;
+    }
+
+    return tile;
 
 error:
 
-	if (tile) {
-		jpc_enc_tile_destroy(tile);
-	}
-	return 0;
+    if (tile) {
+        jpc_enc_tile_destroy(tile);
+    }
+    return 0;
 }
 
 void jpc_enc_tile_destroy(jpc_enc_tile_t *tile)
 {
-	jpc_enc_tcmpt_t *tcmpt;
-	uint_fast16_t cmptno;
-
-	if (tile->tcmpts) {
-		for (cmptno = 0, tcmpt = tile->tcmpts; cmptno <
-		  tile->numtcmpts; ++cmptno, ++tcmpt) {
-			tcmpt_destroy(tcmpt);
-		}
-		jas_free(tile->tcmpts);
-	}
-	if (tile->lyrsizes) {
-		jas_free(tile->lyrsizes);
-	}
-	if (tile->pi) {
-		jpc_pi_destroy(tile->pi);
-	}
-	jas_free(tile);
+    jpc_enc_tcmpt_t *tcmpt;
+    uint_fast16_t cmptno;
+
+    if (tile->tcmpts) {
+        for (cmptno = 0, tcmpt = tile->tcmpts; cmptno <
+          tile->numtcmpts; ++cmptno, ++tcmpt) {
+            tcmpt_destroy(tcmpt);
+        }
+        jas_free(tile->tcmpts);
+    }
+    if (tile->lyrsizes) {
+        jas_free(tile->lyrsizes);
+    }
+    if (tile->pi) {
+        jpc_pi_destroy(tile->pi);
+    }
+    jas_free(tile);
 }
 
 static jpc_enc_tcmpt_t *tcmpt_create(jpc_enc_tcmpt_t *tcmpt, jpc_enc_cp_t *cp,
   jas_image_t *image, jpc_enc_tile_t *tile)
 {
-	uint_fast16_t cmptno;
-	uint_fast16_t rlvlno;
-	jpc_enc_rlvl_t *rlvl;
-	uint_fast32_t tlx;
-	uint_fast32_t tly;
-	uint_fast32_t brx;
-	uint_fast32_t bry;
-	uint_fast32_t cmpttlx;
-	uint_fast32_t cmpttly;
-	jpc_enc_ccp_t *ccp;
-	jpc_tsfb_band_t bandinfos[JPC_MAXBANDS];
-
-	tcmpt->tile = tile;
-	tcmpt->tsfb = 0;
-	tcmpt->data = 0;
-	tcmpt->rlvls = 0;
-
-	/* Deduce the component number. */
-	cmptno = tcmpt - tile->tcmpts;
-
-	ccp = &cp->ccps[cmptno];
-
-	/* Compute the coordinates of the top-left and bottom-right
-	  corners of this tile-component. */
-	tlx = JPC_CEILDIV(tile->tlx, ccp->sampgrdstepx);
-	tly = JPC_CEILDIV(tile->tly, ccp->sampgrdstepy);
-	brx = JPC_CEILDIV(tile->brx, ccp->sampgrdstepx);
-	bry = JPC_CEILDIV(tile->bry, ccp->sampgrdstepy);
-
-	/* Create a sequence to hold the tile-component sample data. */
-	if (!(tcmpt->data = jas_seq2d_create(tlx, tly, brx, bry))) {
-		goto error;
-	}
-
-	/* Get the image data associated with this tile-component. */
-	cmpttlx = JPC_CEILDIV(cp->imgareatlx, ccp->sampgrdstepx);
-	cmpttly = JPC_CEILDIV(cp->imgareatly, ccp->sampgrdstepy);
-	if (jas_image_readcmpt(image, cmptno, tlx - cmpttlx, tly - cmpttly,
-	  brx - tlx, bry - tly, tcmpt->data)) {
-		goto error;
-	}
-
-	tcmpt->synweight = 0;
-	tcmpt->qmfbid = cp->tccp.qmfbid;
-	tcmpt->numrlvls = cp->tccp.maxrlvls;
-	tcmpt->numbands = 3 * tcmpt->numrlvls - 2;
-	if (!(tcmpt->tsfb = jpc_cod_gettsfb(tcmpt->qmfbid, tcmpt->numrlvls - 1))) {
-		goto error;
-	}
-
-	for (rlvlno = 0; rlvlno < tcmpt->numrlvls; ++rlvlno) {
-		tcmpt->prcwidthexpns[rlvlno] = cp->tccp.prcwidthexpns[rlvlno];
-		tcmpt->prcheightexpns[rlvlno] = cp->tccp.prcheightexpns[rlvlno];
-	}
-	tcmpt->cblkwidthexpn = cp->tccp.cblkwidthexpn;
-	tcmpt->cblkheightexpn = cp->tccp.cblkheightexpn;
-	tcmpt->cblksty = cp->tccp.cblksty;
-	tcmpt->csty = cp->tccp.csty;
-
-	tcmpt->numstepsizes = tcmpt->numbands;
-	assert(tcmpt->numstepsizes <= JPC_MAXBANDS);
-	memset(tcmpt->stepsizes, 0, sizeof(tcmpt->numstepsizes *
-	  sizeof(uint_fast16_t)));
-
-	/* Retrieve information about the various bands. */
-	jpc_tsfb_getbands(tcmpt->tsfb, jas_seq2d_xstart(tcmpt->data),
-	  jas_seq2d_ystart(tcmpt->data), jas_seq2d_xend(tcmpt->data),
-	  jas_seq2d_yend(tcmpt->data), bandinfos);
-
-	if (!(tcmpt->rlvls = jas_malloc(tcmpt->numrlvls * sizeof(jpc_enc_rlvl_t)))) {
-		goto error;
-	}
-	for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
-	  ++rlvlno, ++rlvl) {
-		rlvl->bands = 0;
-		rlvl->tcmpt = tcmpt;
-	}
-	for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
-	  ++rlvlno, ++rlvl) {
-		if (!rlvl_create(rlvl, cp, tcmpt, bandinfos)) {
-			goto error;
-		}
-	}
-
-	return tcmpt;
+    uint_fast16_t cmptno;
+    uint_fast16_t rlvlno;
+    jpc_enc_rlvl_t *rlvl;
+    uint_fast32_t tlx;
+    uint_fast32_t tly;
+    uint_fast32_t brx;
+    uint_fast32_t bry;
+    uint_fast32_t cmpttlx;
+    uint_fast32_t cmpttly;
+    jpc_enc_ccp_t *ccp;
+    jpc_tsfb_band_t bandinfos[JPC_MAXBANDS];
+
+    tcmpt->tile = tile;
+    tcmpt->tsfb = 0;
+    tcmpt->data = 0;
+    tcmpt->rlvls = 0;
+
+    /* Deduce the component number. */
+    cmptno = tcmpt - tile->tcmpts;
+
+    ccp = &cp->ccps[cmptno];
+
+    /* Compute the coordinates of the top-left and bottom-right
+      corners of this tile-component. */
+    tlx = JPC_CEILDIV(tile->tlx, ccp->sampgrdstepx);
+    tly = JPC_CEILDIV(tile->tly, ccp->sampgrdstepy);
+    brx = JPC_CEILDIV(tile->brx, ccp->sampgrdstepx);
+    bry = JPC_CEILDIV(tile->bry, ccp->sampgrdstepy);
+
+    /* Create a sequence to hold the tile-component sample data. */
+    if (!(tcmpt->data = jas_seq2d_create(tlx, tly, brx, bry))) {
+        goto error;
+    }
+
+    /* Get the image data associated with this tile-component. */
+    cmpttlx = JPC_CEILDIV(cp->imgareatlx, ccp->sampgrdstepx);
+    cmpttly = JPC_CEILDIV(cp->imgareatly, ccp->sampgrdstepy);
+    if (jas_image_readcmpt(image, cmptno, tlx - cmpttlx, tly - cmpttly,
+      brx - tlx, bry - tly, tcmpt->data)) {
+        goto error;
+    }
+
+    tcmpt->synweight = 0;
+    tcmpt->qmfbid = cp->tccp.qmfbid;
+    tcmpt->numrlvls = cp->tccp.maxrlvls;
+    tcmpt->numbands = 3 * tcmpt->numrlvls - 2;
+    if (!(tcmpt->tsfb = jpc_cod_gettsfb(tcmpt->qmfbid, tcmpt->numrlvls - 1))) {
+        goto error;
+    }
+
+    for (rlvlno = 0; rlvlno < tcmpt->numrlvls; ++rlvlno) {
+        tcmpt->prcwidthexpns[rlvlno] = cp->tccp.prcwidthexpns[rlvlno];
+        tcmpt->prcheightexpns[rlvlno] = cp->tccp.prcheightexpns[rlvlno];
+    }
+    tcmpt->cblkwidthexpn = cp->tccp.cblkwidthexpn;
+    tcmpt->cblkheightexpn = cp->tccp.cblkheightexpn;
+    tcmpt->cblksty = cp->tccp.cblksty;
+    tcmpt->csty = cp->tccp.csty;
+
+    tcmpt->numstepsizes = tcmpt->numbands;
+    assert(tcmpt->numstepsizes <= JPC_MAXBANDS);
+    memset(tcmpt->stepsizes, 0, sizeof(tcmpt->numstepsizes *
+      sizeof(uint_fast16_t)));
+
+    /* Retrieve information about the various bands. */
+    jpc_tsfb_getbands(tcmpt->tsfb, jas_seq2d_xstart(tcmpt->data),
+      jas_seq2d_ystart(tcmpt->data), jas_seq2d_xend(tcmpt->data),
+      jas_seq2d_yend(tcmpt->data), bandinfos);
+
+    if (!(tcmpt->rlvls = jas_malloc(tcmpt->numrlvls * sizeof(jpc_enc_rlvl_t)))) {
+        goto error;
+    }
+    for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
+      ++rlvlno, ++rlvl) {
+        rlvl->bands = 0;
+        rlvl->tcmpt = tcmpt;
+    }
+    for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
+      ++rlvlno, ++rlvl) {
+        if (!rlvl_create(rlvl, cp, tcmpt, bandinfos)) {
+            goto error;
+        }
+    }
+
+    return tcmpt;
 
 error:
 
-	tcmpt_destroy(tcmpt);
-	return 0;
+    tcmpt_destroy(tcmpt);
+    return 0;
 
 }
 
 static void tcmpt_destroy(jpc_enc_tcmpt_t *tcmpt)
 {
-	jpc_enc_rlvl_t *rlvl;
-	uint_fast16_t rlvlno;
-
-	if (tcmpt->rlvls) {
-		for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
-		  ++rlvlno, ++rlvl) {
-			rlvl_destroy(rlvl);
-		}
-		jas_free(tcmpt->rlvls);
-	}
-
-	if (tcmpt->data) {
-		jas_seq2d_destroy(tcmpt->data);
-	}
-	if (tcmpt->tsfb) {
-		jpc_tsfb_destroy(tcmpt->tsfb);
-	}
+    jpc_enc_rlvl_t *rlvl;
+    uint_fast16_t rlvlno;
+
+    if (tcmpt->rlvls) {
+        for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
+          ++rlvlno, ++rlvl) {
+            rlvl_destroy(rlvl);
+        }
+        jas_free(tcmpt->rlvls);
+    }
+
+    if (tcmpt->data) {
+        jas_seq2d_destroy(tcmpt->data);
+    }
+    if (tcmpt->tsfb) {
+        jpc_tsfb_destroy(tcmpt->tsfb);
+    }
 }
 
 static jpc_enc_rlvl_t *rlvl_create(jpc_enc_rlvl_t *rlvl, jpc_enc_cp_t *cp,
   jpc_enc_tcmpt_t *tcmpt, jpc_tsfb_band_t *bandinfos)
 {
-	uint_fast16_t rlvlno;
-	uint_fast32_t tlprctlx;
-	uint_fast32_t tlprctly;
-	uint_fast32_t brprcbrx;
-	uint_fast32_t brprcbry;
-	uint_fast16_t bandno;
-	jpc_enc_band_t *band;
-
-	/* Deduce the resolution level. */
-	rlvlno = rlvl - tcmpt->rlvls;
-
-	/* Initialize members required for error recovery. */
-	rlvl->bands = 0;
-	rlvl->tcmpt = tcmpt;
-
-	/* Compute the coordinates of the top-left and bottom-right
-	  corners of the tile-component at this resolution. */
-	rlvl->tlx = JPC_CEILDIVPOW2(jas_seq2d_xstart(tcmpt->data), tcmpt->numrlvls -
-	  1 - rlvlno);
-	rlvl->tly = JPC_CEILDIVPOW2(jas_seq2d_ystart(tcmpt->data), tcmpt->numrlvls -
-	  1 - rlvlno);
-	rlvl->brx = JPC_CEILDIVPOW2(jas_seq2d_xend(tcmpt->data), tcmpt->numrlvls -
-	  1 - rlvlno);
-	rlvl->bry = JPC_CEILDIVPOW2(jas_seq2d_yend(tcmpt->data), tcmpt->numrlvls -
-	  1 - rlvlno);
-
-	if (rlvl->tlx >= rlvl->brx || rlvl->tly >= rlvl->bry) {
-		rlvl->numhprcs = 0;
-		rlvl->numvprcs = 0;
-		rlvl->numprcs = 0;
-		return rlvl;
-	}
-
-	rlvl->numbands = (!rlvlno) ? 1 : 3;
-	rlvl->prcwidthexpn = cp->tccp.prcwidthexpns[rlvlno];
-	rlvl->prcheightexpn = cp->tccp.prcheightexpns[rlvlno];
-	if (!rlvlno) {
-		rlvl->cbgwidthexpn = rlvl->prcwidthexpn;
-		rlvl->cbgheightexpn = rlvl->prcheightexpn;
-	} else {
-		rlvl->cbgwidthexpn = rlvl->prcwidthexpn - 1;
-		rlvl->cbgheightexpn = rlvl->prcheightexpn - 1;
-	}
-	rlvl->cblkwidthexpn = JAS_MIN(cp->tccp.cblkwidthexpn, rlvl->cbgwidthexpn);
-	rlvl->cblkheightexpn = JAS_MIN(cp->tccp.cblkheightexpn, rlvl->cbgheightexpn);
-
-	/* Compute the number of precincts. */
-	tlprctlx = JPC_FLOORTOMULTPOW2(rlvl->tlx, rlvl->prcwidthexpn);
-	tlprctly = JPC_FLOORTOMULTPOW2(rlvl->tly, rlvl->prcheightexpn);
-	brprcbrx = JPC_CEILTOMULTPOW2(rlvl->brx, rlvl->prcwidthexpn);
-	brprcbry = JPC_CEILTOMULTPOW2(rlvl->bry, rlvl->prcheightexpn);
-	rlvl->numhprcs = JPC_FLOORDIVPOW2(brprcbrx - tlprctlx, rlvl->prcwidthexpn);
-	rlvl->numvprcs = JPC_FLOORDIVPOW2(brprcbry - tlprctly, rlvl->prcheightexpn);
-	rlvl->numprcs = rlvl->numhprcs * rlvl->numvprcs;
-
-	if (!(rlvl->bands = jas_malloc(rlvl->numbands * sizeof(jpc_enc_band_t)))) {
-		goto error;
-	}
-	for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
-	  ++bandno, ++band) {
-		band->prcs = 0;
-		band->data = 0;
-		band->rlvl = rlvl;
-	}
-	for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
-	  ++bandno, ++band) {
-		if (!band_create(band, cp, rlvl, bandinfos)) {
-			goto error;
-		}
-	}
-
-	return rlvl;
+    uint_fast16_t rlvlno;
+    uint_fast32_t tlprctlx;
+    uint_fast32_t tlprctly;
+    uint_fast32_t brprcbrx;
+    uint_fast32_t brprcbry;
+    uint_fast16_t bandno;
+    jpc_enc_band_t *band;
+
+    /* Deduce the resolution level. */
+    rlvlno = rlvl - tcmpt->rlvls;
+
+    /* Initialize members required for error recovery. */
+    rlvl->bands = 0;
+    rlvl->tcmpt = tcmpt;
+
+    /* Compute the coordinates of the top-left and bottom-right
+      corners of the tile-component at this resolution. */
+    rlvl->tlx = JPC_CEILDIVPOW2(jas_seq2d_xstart(tcmpt->data), tcmpt->numrlvls -
+      1 - rlvlno);
+    rlvl->tly = JPC_CEILDIVPOW2(jas_seq2d_ystart(tcmpt->data), tcmpt->numrlvls -
+      1 - rlvlno);
+    rlvl->brx = JPC_CEILDIVPOW2(jas_seq2d_xend(tcmpt->data), tcmpt->numrlvls -
+      1 - rlvlno);
+    rlvl->bry = JPC_CEILDIVPOW2(jas_seq2d_yend(tcmpt->data), tcmpt->numrlvls -
+      1 - rlvlno);
+
+    if (rlvl->tlx >= rlvl->brx || rlvl->tly >= rlvl->bry) {
+        rlvl->numhprcs = 0;
+        rlvl->numvprcs = 0;
+        rlvl->numprcs = 0;
+        return rlvl;
+    }
+
+    rlvl->numbands = (!rlvlno) ? 1 : 3;
+    rlvl->prcwidthexpn = cp->tccp.prcwidthexpns[rlvlno];
+    rlvl->prcheightexpn = cp->tccp.prcheightexpns[rlvlno];
+    if (!rlvlno) {
+        rlvl->cbgwidthexpn = rlvl->prcwidthexpn;
+        rlvl->cbgheightexpn = rlvl->prcheightexpn;
+    } else {
+        rlvl->cbgwidthexpn = rlvl->prcwidthexpn - 1;
+        rlvl->cbgheightexpn = rlvl->prcheightexpn - 1;
+    }
+    rlvl->cblkwidthexpn = JAS_MIN(cp->tccp.cblkwidthexpn, rlvl->cbgwidthexpn);
+    rlvl->cblkheightexpn = JAS_MIN(cp->tccp.cblkheightexpn, rlvl->cbgheightexpn);
+
+    /* Compute the number of precincts. */
+    tlprctlx = JPC_FLOORTOMULTPOW2(rlvl->tlx, rlvl->prcwidthexpn);
+    tlprctly = JPC_FLOORTOMULTPOW2(rlvl->tly, rlvl->prcheightexpn);
+    brprcbrx = JPC_CEILTOMULTPOW2(rlvl->brx, rlvl->prcwidthexpn);
+    brprcbry = JPC_CEILTOMULTPOW2(rlvl->bry, rlvl->prcheightexpn);
+    rlvl->numhprcs = JPC_FLOORDIVPOW2(brprcbrx - tlprctlx, rlvl->prcwidthexpn);
+    rlvl->numvprcs = JPC_FLOORDIVPOW2(brprcbry - tlprctly, rlvl->prcheightexpn);
+    rlvl->numprcs = rlvl->numhprcs * rlvl->numvprcs;
+
+    if (!(rlvl->bands = jas_malloc(rlvl->numbands * sizeof(jpc_enc_band_t)))) {
+        goto error;
+    }
+    for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
+      ++bandno, ++band) {
+        band->prcs = 0;
+        band->data = 0;
+        band->rlvl = rlvl;
+    }
+    for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
+      ++bandno, ++band) {
+        if (!band_create(band, cp, rlvl, bandinfos)) {
+            goto error;
+        }
+    }
+
+    return rlvl;
 error:
 
-	rlvl_destroy(rlvl);
-	return 0;
+    rlvl_destroy(rlvl);
+    return 0;
 }
 
 static void rlvl_destroy(jpc_enc_rlvl_t *rlvl)
 {
-	jpc_enc_band_t *band;
-	uint_fast16_t bandno;
-
-	if (rlvl->bands) {
-		for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
-		  ++bandno, ++band) {
-			band_destroy(band);
-		}
-		jas_free(rlvl->bands);
-	}
+    jpc_enc_band_t *band;
+    uint_fast16_t bandno;
+
+    if (rlvl->bands) {
+        for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
+          ++bandno, ++band) {
+            band_destroy(band);
+        }
+        jas_free(rlvl->bands);
+    }
 }
 
 static jpc_enc_band_t *band_create(jpc_enc_band_t *band, jpc_enc_cp_t *cp,
   jpc_enc_rlvl_t *rlvl, jpc_tsfb_band_t *bandinfos)
 {
-	uint_fast16_t bandno;
-	uint_fast16_t gblbandno;
-	uint_fast16_t rlvlno;
-	jpc_tsfb_band_t *bandinfo;
-	jpc_enc_tcmpt_t *tcmpt;
-	uint_fast32_t prcno;
-	jpc_enc_prc_t *prc;
-
-	tcmpt = rlvl->tcmpt;
-	band->data = 0;
-	band->prcs = 0;
-	band->rlvl = rlvl;
-
-	/* Deduce the resolution level and band number. */
-	rlvlno = rlvl - rlvl->tcmpt->rlvls;
-	bandno = band - rlvl->bands;
-	gblbandno = (!rlvlno) ? 0 : (3 * (rlvlno - 1) + bandno + 1);
-
-	bandinfo = &bandinfos[gblbandno];
+    uint_fast16_t bandno;
+    uint_fast16_t gblbandno;
+    uint_fast16_t rlvlno;
+    jpc_tsfb_band_t *bandinfo;
+    jpc_enc_tcmpt_t *tcmpt;
+    uint_fast32_t prcno;
+    jpc_enc_prc_t *prc;
+
+    tcmpt = rlvl->tcmpt;
+    band->data = 0;
+    band->prcs = 0;
+    band->rlvl = rlvl;
+
+    /* Deduce the resolution level and band number. */
+    rlvlno = rlvl - rlvl->tcmpt->rlvls;
+    bandno = band - rlvl->bands;
+    gblbandno = (!rlvlno) ? 0 : (3 * (rlvlno - 1) + bandno + 1);
+
+    bandinfo = &bandinfos[gblbandno];
 
 if (bandinfo->xstart != bandinfo->xend && bandinfo->ystart != bandinfo->yend) {
-	if (!(band->data = jas_seq2d_create(0, 0, 0, 0))) {
-		goto error;
-	}
-	jas_seq2d_bindsub(band->data, tcmpt->data, bandinfo->locxstart,
-	  bandinfo->locystart, bandinfo->locxend, bandinfo->locyend);
-	jas_seq2d_setshift(band->data, bandinfo->xstart, bandinfo->ystart);
+    if (!(band->data = jas_seq2d_create(0, 0, 0, 0))) {
+        goto error;
+    }
+    jas_seq2d_bindsub(band->data, tcmpt->data, bandinfo->locxstart,
+      bandinfo->locystart, bandinfo->locxend, bandinfo->locyend);
+    jas_seq2d_setshift(band->data, bandinfo->xstart, bandinfo->ystart);
 }
-	band->orient = bandinfo->orient;
-	band->analgain = JPC_NOMINALGAIN(cp->tccp.qmfbid, tcmpt->numrlvls, rlvlno,
-	  band->orient);
-	band->numbps = 0;
-	band->absstepsize = 0;
-	band->stepsize = 0;
-	band->synweight = bandinfo->synenergywt;
+    band->orient = bandinfo->orient;
+    band->analgain = JPC_NOMINALGAIN(cp->tccp.qmfbid, tcmpt->numrlvls, rlvlno,
+      band->orient);
+    band->numbps = 0;
+    band->absstepsize = 0;
+    band->stepsize = 0;
+    band->synweight = bandinfo->synenergywt;
 
 if (band->data) {
-	if (!(band->prcs = jas_malloc(rlvl->numprcs * sizeof(jpc_enc_prc_t)))) {
-		goto error;
-	}
-	for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs; ++prcno,
-	  ++prc) {
-		prc->cblks = 0;
-		prc->incltree = 0;
-		prc->nlibtree = 0;
-		prc->savincltree = 0;
-		prc->savnlibtree = 0;
-		prc->band = band;
-	}
-	for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs; ++prcno,
-	  ++prc) {
-		if (!prc_create(prc, cp, band)) {
-			goto error;
-		}
-	}
+    if (!(band->prcs = jas_malloc(rlvl->numprcs * sizeof(jpc_enc_prc_t)))) {
+        goto error;
+    }
+    for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs; ++prcno,
+      ++prc) {
+        prc->cblks = 0;
+        prc->incltree = 0;
+        prc->nlibtree = 0;
+        prc->savincltree = 0;
+        prc->savnlibtree = 0;
+        prc->band = band;
+    }
+    for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs; ++prcno,
+      ++prc) {
+        if (!prc_create(prc, cp, band)) {
+            goto error;
+        }
+    }
 }
 
-	return band;
+    return band;
 
 error:
-	band_destroy(band);
-	return 0;
+    band_destroy(band);
+    return 0;
 }
 
 static void band_destroy(jpc_enc_band_t *band)
 {
-	jpc_enc_prc_t *prc;
-	jpc_enc_rlvl_t *rlvl;
-	uint_fast32_t prcno;
-
-	if (band->prcs) {
-		rlvl = band->rlvl;
-		for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs;
-		  ++prcno, ++prc) {
-			prc_destroy(prc);
-		}
-		jas_free(band->prcs);
-	}
-	if (band->data) {
-		jas_seq2d_destroy(band->data);
-	}
+    jpc_enc_prc_t *prc;
+    jpc_enc_rlvl_t *rlvl;
+    uint_fast32_t prcno;
+
+    if (band->prcs) {
+        rlvl = band->rlvl;
+        for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs;
+          ++prcno, ++prc) {
+            prc_destroy(prc);
+        }
+        jas_free(band->prcs);
+    }
+    if (band->data) {
+        jas_seq2d_destroy(band->data);
+    }
 }
 
 static jpc_enc_prc_t *prc_create(jpc_enc_prc_t *prc, jpc_enc_cp_t *cp, jpc_enc_band_t *band)
 {
-	uint_fast32_t prcno;
-	uint_fast32_t prcxind;
-	uint_fast32_t prcyind;
-	uint_fast32_t cbgtlx;
-	uint_fast32_t cbgtly;
-	uint_fast32_t tlprctlx;
-	uint_fast32_t tlprctly;
-	uint_fast32_t tlcbgtlx;
-	uint_fast32_t tlcbgtly;
-	uint_fast16_t rlvlno;
-	jpc_enc_rlvl_t *rlvl;
-	uint_fast32_t tlcblktlx;
-	uint_fast32_t tlcblktly;
-	uint_fast32_t brcblkbrx;
-	uint_fast32_t brcblkbry;
-	uint_fast32_t cblkno;
-	jpc_enc_cblk_t *cblk;
-	jpc_enc_tcmpt_t *tcmpt;
-
-	prc->cblks = 0;
-	prc->incltree = 0;
-	prc->savincltree = 0;
-	prc->nlibtree = 0;
-	prc->savnlibtree = 0;
-
-	rlvl = band->rlvl;
-	tcmpt = rlvl->tcmpt;
+    uint_fast32_t prcno;
+    uint_fast32_t prcxind;
+    uint_fast32_t prcyind;
+    uint_fast32_t cbgtlx;
+    uint_fast32_t cbgtly;
+    uint_fast32_t tlprctlx;
+    uint_fast32_t tlprctly;
+    uint_fast32_t tlcbgtlx;
+    uint_fast32_t tlcbgtly;
+    uint_fast16_t rlvlno;
+    jpc_enc_rlvl_t *rlvl;
+    uint_fast32_t tlcblktlx;
+    uint_fast32_t tlcblktly;
+    uint_fast32_t brcblkbrx;
+    uint_fast32_t brcblkbry;
+    uint_fast32_t cblkno;
+    jpc_enc_cblk_t *cblk;
+    jpc_enc_tcmpt_t *tcmpt;
+
+    prc->cblks = 0;
+    prc->incltree = 0;
+    prc->savincltree = 0;
+    prc->nlibtree = 0;
+    prc->savnlibtree = 0;
+
+    rlvl = band->rlvl;
+    tcmpt = rlvl->tcmpt;
 rlvlno = rlvl - tcmpt->rlvls;
-	prcno = prc - band->prcs;
-	prcxind = prcno % rlvl->numhprcs;
-	prcyind = prcno / rlvl->numhprcs;
-	prc->band = band;
+    prcno = prc - band->prcs;
+    prcxind = prcno % rlvl->numhprcs;
+    prcyind = prcno / rlvl->numhprcs;
+    prc->band = band;
 
 tlprctlx = JPC_FLOORTOMULTPOW2(rlvl->tlx, rlvl->prcwidthexpn);
 tlprctly = JPC_FLOORTOMULTPOW2(rlvl->tly, rlvl->prcheightexpn);
 if (!rlvlno) {
-	tlcbgtlx = tlprctlx;
-	tlcbgtly = tlprctly;
+    tlcbgtlx = tlprctlx;
+    tlcbgtly = tlprctly;
 } else {
-	tlcbgtlx = JPC_CEILDIVPOW2(tlprctlx, 1);
-	tlcbgtly = JPC_CEILDIVPOW2(tlprctly, 1);
+    tlcbgtlx = JPC_CEILDIVPOW2(tlprctlx, 1);
+    tlcbgtly = JPC_CEILDIVPOW2(tlprctly, 1);
 }
 
-	/* Compute the coordinates of the top-left and bottom-right
-	  corners of the precinct. */
-	cbgtlx = tlcbgtlx + (prcxind << rlvl->cbgwidthexpn);
-	cbgtly = tlcbgtly + (prcyind << rlvl->cbgheightexpn);
-	prc->tlx = JAS_MAX(jas_seq2d_xstart(band->data), cbgtlx);
-	prc->tly = JAS_MAX(jas_seq2d_ystart(band->data), cbgtly);
-	prc->brx = JAS_MIN(jas_seq2d_xend(band->data), cbgtlx +
-	  (1 << rlvl->cbgwidthexpn));
-	prc->bry = JAS_MIN(jas_seq2d_yend(band->data), cbgtly +
-	  (1 << rlvl->cbgheightexpn));
-
-	if (prc->tlx < prc->brx && prc->tly < prc->bry) {
-		/* The precinct contains at least one code block. */
-
-		tlcblktlx = JPC_FLOORTOMULTPOW2(prc->tlx, rlvl->cblkwidthexpn);
-		tlcblktly = JPC_FLOORTOMULTPOW2(prc->tly, rlvl->cblkheightexpn);
-		brcblkbrx = JPC_CEILTOMULTPOW2(prc->brx, rlvl->cblkwidthexpn);
-		brcblkbry = JPC_CEILTOMULTPOW2(prc->bry, rlvl->cblkheightexpn);
-		prc->numhcblks = JPC_FLOORDIVPOW2(brcblkbrx - tlcblktlx,
-		  rlvl->cblkwidthexpn);
-		prc->numvcblks = JPC_FLOORDIVPOW2(brcblkbry - tlcblktly,
-		  rlvl->cblkheightexpn);
-		prc->numcblks = prc->numhcblks * prc->numvcblks;
-
-		if (!(prc->incltree = jpc_tagtree_create(prc->numhcblks,
-		  prc->numvcblks))) {
-			goto error;
-		}
-		if (!(prc->nlibtree = jpc_tagtree_create(prc->numhcblks,
-		  prc->numvcblks))) {
-			goto error;
-		}
-		if (!(prc->savincltree = jpc_tagtree_create(prc->numhcblks,
-		  prc->numvcblks))) {
-			goto error;
-		}
-		if (!(prc->savnlibtree = jpc_tagtree_create(prc->numhcblks,
-		  prc->numvcblks))) {
-			goto error;
-		}
-
-		if (!(prc->cblks = jas_malloc(prc->numcblks * sizeof(jpc_enc_cblk_t)))) {
-			goto error;
-		}
-		for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
-		  ++cblkno, ++cblk) {
-			cblk->passes = 0;
-			cblk->stream = 0;
-			cblk->mqenc = 0;
-			cblk->data = 0;
-			cblk->flags = 0;
-			cblk->prc = prc;
-		}
-		for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
-		  ++cblkno, ++cblk) {
-			if (!cblk_create(cblk, cp, prc)) {
-				goto error;
-			}
-		}
-	} else {
-		/* The precinct does not contain any code blocks. */
-		prc->tlx = prc->brx;
-		prc->tly = prc->bry;
-		prc->numcblks = 0;
-		prc->numhcblks = 0;
-		prc->numvcblks = 0;
-		prc->cblks = 0;
-		prc->incltree = 0;
-		prc->nlibtree = 0;
-		prc->savincltree = 0;
-		prc->savnlibtree = 0;
-	}
-
-	return prc;
+    /* Compute the coordinates of the top-left and bottom-right
+      corners of the precinct. */
+    cbgtlx = tlcbgtlx + (prcxind << rlvl->cbgwidthexpn);
+    cbgtly = tlcbgtly + (prcyind << rlvl->cbgheightexpn);
+    prc->tlx = JAS_MAX(jas_seq2d_xstart(band->data), cbgtlx);
+    prc->tly = JAS_MAX(jas_seq2d_ystart(band->data), cbgtly);
+    prc->brx = JAS_MIN(jas_seq2d_xend(band->data), cbgtlx +
+      (1 << rlvl->cbgwidthexpn));
+    prc->bry = JAS_MIN(jas_seq2d_yend(band->data), cbgtly +
+      (1 << rlvl->cbgheightexpn));
+
+    if (prc->tlx < prc->brx && prc->tly < prc->bry) {
+        /* The precinct contains at least one code block. */
+
+        tlcblktlx = JPC_FLOORTOMULTPOW2(prc->tlx, rlvl->cblkwidthexpn);
+        tlcblktly = JPC_FLOORTOMULTPOW2(prc->tly, rlvl->cblkheightexpn);
+        brcblkbrx = JPC_CEILTOMULTPOW2(prc->brx, rlvl->cblkwidthexpn);
+        brcblkbry = JPC_CEILTOMULTPOW2(prc->bry, rlvl->cblkheightexpn);
+        prc->numhcblks = JPC_FLOORDIVPOW2(brcblkbrx - tlcblktlx,
+          rlvl->cblkwidthexpn);
+        prc->numvcblks = JPC_FLOORDIVPOW2(brcblkbry - tlcblktly,
+          rlvl->cblkheightexpn);
+        prc->numcblks = prc->numhcblks * prc->numvcblks;
+
+        if (!(prc->incltree = jpc_tagtree_create(prc->numhcblks,
+          prc->numvcblks))) {
+            goto error;
+        }
+        if (!(prc->nlibtree = jpc_tagtree_create(prc->numhcblks,
+          prc->numvcblks))) {
+            goto error;
+        }
+        if (!(prc->savincltree = jpc_tagtree_create(prc->numhcblks,
+          prc->numvcblks))) {
+            goto error;
+        }
+        if (!(prc->savnlibtree = jpc_tagtree_create(prc->numhcblks,
+          prc->numvcblks))) {
+            goto error;
+        }
+
+        if (!(prc->cblks = jas_malloc(prc->numcblks * sizeof(jpc_enc_cblk_t)))) {
+            goto error;
+        }
+        for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
+          ++cblkno, ++cblk) {
+            cblk->passes = 0;
+            cblk->stream = 0;
+            cblk->mqenc = 0;
+            cblk->data = 0;
+            cblk->flags = 0;
+            cblk->prc = prc;
+        }
+        for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
+          ++cblkno, ++cblk) {
+            if (!cblk_create(cblk, cp, prc)) {
+                goto error;
+            }
+        }
+    } else {
+        /* The precinct does not contain any code blocks. */
+        prc->tlx = prc->brx;
+        prc->tly = prc->bry;
+        prc->numcblks = 0;
+        prc->numhcblks = 0;
+        prc->numvcblks = 0;
+        prc->cblks = 0;
+        prc->incltree = 0;
+        prc->nlibtree = 0;
+        prc->savincltree = 0;
+        prc->savnlibtree = 0;
+    }
+
+    return prc;
 
 error:
-	prc_destroy(prc);
-	return 0;
+    prc_destroy(prc);
+    return 0;
 }
 
 static void prc_destroy(jpc_enc_prc_t *prc)
 {
-	jpc_enc_cblk_t *cblk;
-	uint_fast32_t cblkno;
-
-	if (prc->cblks) {
-		for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
-		  ++cblkno, ++cblk) {
-			cblk_destroy(cblk);
-		}
-		jas_free(prc->cblks);
-	}
-	if (prc->incltree) {
-		jpc_tagtree_destroy(prc->incltree);
-	}
-	if (prc->nlibtree) {
-		jpc_tagtree_destroy(prc->nlibtree);
-	}
-	if (prc->savincltree) {
-		jpc_tagtree_destroy(prc->savincltree);
-	}
-	if (prc->savnlibtree) {
-		jpc_tagtree_destroy(prc->savnlibtree);
-	}
+    jpc_enc_cblk_t *cblk;
+    uint_fast32_t cblkno;
+
+    if (prc->cblks) {
+        for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
+          ++cblkno, ++cblk) {
+            cblk_destroy(cblk);
+        }
+        jas_free(prc->cblks);
+    }
+    if (prc->incltree) {
+        jpc_tagtree_destroy(prc->incltree);
+    }
+    if (prc->nlibtree) {
+        jpc_tagtree_destroy(prc->nlibtree);
+    }
+    if (prc->savincltree) {
+        jpc_tagtree_destroy(prc->savincltree);
+    }
+    if (prc->savnlibtree) {
+        jpc_tagtree_destroy(prc->savnlibtree);
+    }
 }
 
 static jpc_enc_cblk_t *cblk_create(jpc_enc_cblk_t *cblk, jpc_enc_cp_t *cp, jpc_enc_prc_t *prc)
 {
-	jpc_enc_band_t *band;
-	uint_fast32_t cblktlx;
-	uint_fast32_t cblktly;
-	uint_fast32_t cblkbrx;
-	uint_fast32_t cblkbry;
-	jpc_enc_rlvl_t *rlvl;
-	uint_fast32_t cblkxind;
-	uint_fast32_t cblkyind;
-	uint_fast32_t cblkno;
-	uint_fast32_t tlcblktlx;
-	uint_fast32_t tlcblktly;
-
-	cblkno = cblk - prc->cblks;
-	cblkxind = cblkno % prc->numhcblks;
-	cblkyind = cblkno / prc->numhcblks;
-	rlvl = prc->band->rlvl;
-	cblk->prc = prc;
-
-	cblk->numpasses = 0;
-	cblk->passes = 0;
-	cblk->numencpasses = 0;
-	cblk->numimsbs = 0;
-	cblk->numlenbits = 0;
-	cblk->stream = 0;
-	cblk->mqenc = 0;
-	cblk->flags = 0;
-	cblk->numbps = 0;
-	cblk->curpass = 0;
-	cblk->data = 0;
-	cblk->savedcurpass = 0;
-	cblk->savednumlenbits = 0;
-	cblk->savednumencpasses = 0;
-
-	band = prc->band;
-	tlcblktlx = JPC_FLOORTOMULTPOW2(prc->tlx, rlvl->cblkwidthexpn);
-	tlcblktly = JPC_FLOORTOMULTPOW2(prc->tly, rlvl->cblkheightexpn);
-	cblktlx = JAS_MAX(tlcblktlx + (cblkxind << rlvl->cblkwidthexpn), prc->tlx);
-	cblktly = JAS_MAX(tlcblktly + (cblkyind << rlvl->cblkheightexpn), prc->tly);
-	cblkbrx = JAS_MIN(tlcblktlx + ((cblkxind + 1) << rlvl->cblkwidthexpn),
-	  prc->brx);
-	cblkbry = JAS_MIN(tlcblktly + ((cblkyind + 1) << rlvl->cblkheightexpn),
-	  prc->bry);
-
-	assert(cblktlx < cblkbrx && cblktly < cblkbry);
-	if (!(cblk->data = jas_seq2d_create(0, 0, 0, 0))) {
-		goto error;
-	}
-	jas_seq2d_bindsub(cblk->data, band->data, cblktlx, cblktly, cblkbrx, cblkbry);
-
-	return cblk;
+    jpc_enc_band_t *band;
+    uint_fast32_t cblktlx;
+    uint_fast32_t cblktly;
+    uint_fast32_t cblkbrx;
+    uint_fast32_t cblkbry;
+    jpc_enc_rlvl_t *rlvl;
+    uint_fast32_t cblkxind;
+    uint_fast32_t cblkyind;
+    uint_fast32_t cblkno;
+    uint_fast32_t tlcblktlx;
+    uint_fast32_t tlcblktly;
+
+    cblkno = cblk - prc->cblks;
+    cblkxind = cblkno % prc->numhcblks;
+    cblkyind = cblkno / prc->numhcblks;
+    rlvl = prc->band->rlvl;
+    cblk->prc = prc;
+
+    cblk->numpasses = 0;
+    cblk->passes = 0;
+    cblk->numencpasses = 0;
+    cblk->numimsbs = 0;
+    cblk->numlenbits = 0;
+    cblk->stream = 0;
+    cblk->mqenc = 0;
+    cblk->flags = 0;
+    cblk->numbps = 0;
+    cblk->curpass = 0;
+    cblk->data = 0;
+    cblk->savedcurpass = 0;
+    cblk->savednumlenbits = 0;
+    cblk->savednumencpasses = 0;
+
+    band = prc->band;
+    tlcblktlx = JPC_FLOORTOMULTPOW2(prc->tlx, rlvl->cblkwidthexpn);
+    tlcblktly = JPC_FLOORTOMULTPOW2(prc->tly, rlvl->cblkheightexpn);
+    cblktlx = JAS_MAX(tlcblktlx + (cblkxind << rlvl->cblkwidthexpn), prc->tlx);
+    cblktly = JAS_MAX(tlcblktly + (cblkyind << rlvl->cblkheightexpn), prc->tly);
+    cblkbrx = JAS_MIN(tlcblktlx + ((cblkxind + 1) << rlvl->cblkwidthexpn),
+      prc->brx);
+    cblkbry = JAS_MIN(tlcblktly + ((cblkyind + 1) << rlvl->cblkheightexpn),
+      prc->bry);
+
+    assert(cblktlx < cblkbrx && cblktly < cblkbry);
+    if (!(cblk->data = jas_seq2d_create(0, 0, 0, 0))) {
+        goto error;
+    }
+    jas_seq2d_bindsub(cblk->data, band->data, cblktlx, cblktly, cblkbrx, cblkbry);
+
+    return cblk;
 
 error:
-	cblk_destroy(cblk);
-	return 0;
+    cblk_destroy(cblk);
+    return 0;
 }
 
 static void cblk_destroy(jpc_enc_cblk_t *cblk)
 {
-	uint_fast16_t passno;
-	jpc_enc_pass_t *pass;
-	if (cblk->passes) {
-		for (passno = 0, pass = cblk->passes; passno < cblk->numpasses;
-		  ++passno, ++pass) {
-			pass_destroy(pass);
-		}
-		jas_free(cblk->passes);
-	}
-	if (cblk->stream) {
-		jas_stream_close(cblk->stream);
-	}
-	if (cblk->mqenc) {
-		jpc_mqenc_destroy(cblk->mqenc);
-	}
-	if (cblk->data) {
-		jas_seq2d_destroy(cblk->data);
-	}
-	if (cblk->flags) {
-		jas_seq2d_destroy(cblk->flags);
-	}
+    uint_fast16_t passno;
+    jpc_enc_pass_t *pass;
+    if (cblk->passes) {
+        for (passno = 0, pass = cblk->passes; passno < cblk->numpasses;
+          ++passno, ++pass) {
+            pass_destroy(pass);
+        }
+        jas_free(cblk->passes);
+    }
+    if (cblk->stream) {
+        jas_stream_close(cblk->stream);
+    }
+    if (cblk->mqenc) {
+        jpc_mqenc_destroy(cblk->mqenc);
+    }
+    if (cblk->data) {
+        jas_seq2d_destroy(cblk->data);
+    }
+    if (cblk->flags) {
+        jas_seq2d_destroy(cblk->flags);
+    }
 }
 
 static void pass_destroy(jpc_enc_pass_t *pass)
 {
-	/* XXX - need to free resources here */
+    /* XXX - need to free resources here */
 }
 
 void jpc_enc_dump(jpc_enc_t *enc)
 {
-	jpc_enc_tile_t *tile;
-	jpc_enc_tcmpt_t *tcmpt;
-	jpc_enc_rlvl_t *rlvl;
-	jpc_enc_band_t *band;
-	jpc_enc_prc_t *prc;
-	jpc_enc_cblk_t *cblk;
-	uint_fast16_t cmptno;
-	uint_fast16_t rlvlno;
-	uint_fast16_t bandno;
-	uint_fast32_t prcno;
-	uint_fast32_t cblkno;
-
-	tile = enc->curtile;
-
-	for (cmptno = 0, tcmpt = tile->tcmpts; cmptno < tile->numtcmpts; ++cmptno,
-	  ++tcmpt) {
-		fprintf(stderr, "  tcmpt %5d %5d %5d %5d\n", jas_seq2d_xstart(tcmpt->data), jas_seq2d_ystart(tcmpt->data), jas_seq2d_xend(tcmpt->data), jas_seq2d_yend(tcmpt->data));
-		for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
-		  ++rlvlno, ++rlvl) {
-			fprintf(stderr, "    rlvl %5d %5d %5d %5d\n", rlvl->tlx, rlvl->tly, rlvl->brx, rlvl->bry);
-			for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
-			  ++bandno, ++band) {
-				if (!band->data) {
-					continue;
-				}
-				fprintf(stderr, "      band %5d %5d %5d %5d\n", jas_seq2d_xstart(band->data), jas_seq2d_ystart(band->data), jas_seq2d_xend(band->data), jas_seq2d_yend(band->data));
-				for (prcno = 0, prc = band->prcs; prcno < rlvl->numprcs;
-				  ++prcno, ++prc) {
-					fprintf(stderr, "        prc %5d %5d %5d %5d (%5d %5d)\n", prc->tlx, prc->tly, prc->brx, prc->bry, prc->brx - prc->tlx, prc->bry - prc->tly);
-					if (!prc->cblks) {
-						continue;
-					}
-					for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
-					  ++cblkno, ++cblk) {
-						fprintf(stderr, "         cblk %5d %5d %5d %5d\n", jas_seq2d_xstart(cblk->data), jas_seq2d_ystart(cblk->data), jas_seq2d_xend(cblk->data), jas_seq2d_yend(cblk->data));
-					}
-				}
-			}
-		}
-	}
+    jpc_enc_tile_t *tile;
+    jpc_enc_tcmpt_t *tcmpt;
+    jpc_enc_rlvl_t *rlvl;
+    jpc_enc_band_t *band;
+    jpc_enc_prc_t *prc;
+    jpc_enc_cblk_t *cblk;
+    uint_fast16_t cmptno;
+    uint_fast16_t rlvlno;
+    uint_fast16_t bandno;
+    uint_fast32_t prcno;
+    uint_fast32_t cblkno;
+
+    tile = enc->curtile;
+
+    for (cmptno = 0, tcmpt = tile->tcmpts;
+         cmptno < tile->numtcmpts;
+         ++cmptno, ++tcmpt) {
+        fprintf(stderr, "  tcmpt %5d %5d %5d %5d\n",
+                (int)jas_seq2d_xstart(tcmpt->data),
+                (int)jas_seq2d_ystart(tcmpt->data),
+                (int)jas_seq2d_xend(tcmpt->data),
+                (int)jas_seq2d_yend(tcmpt->data));
+        for (rlvlno = 0, rlvl = tcmpt->rlvls;
+             rlvlno < tcmpt->numrlvls;
+          ++rlvlno, ++rlvl) {
+            fprintf(stderr, "    rlvl %5d %5d %5d %5d\n",
+                    (int)rlvl->tlx, (int)rlvl->tly,
+                    (int)rlvl->brx, (int)rlvl->bry);
+            for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
+              ++bandno, ++band) {
+                if (!band->data) {
+                    continue;
+                }
+                fprintf(stderr, "      band %5d %5d %5d %5d\n",
+                        (int)jas_seq2d_xstart(band->data),
+                        (int)jas_seq2d_ystart(band->data),
+                        (int)jas_seq2d_xend(band->data),
+                        (int)jas_seq2d_yend(band->data));
+                for (prcno = 0, prc = band->prcs;
+                     prcno < rlvl->numprcs;
+                     ++prcno, ++prc) {
+                    fprintf(stderr, "        prc %5d %5d %5d %5d (%5d %5d)\n",
+                            (int)prc->tlx, (int)prc->tly, 
+                            (int)prc->brx, (int)prc->bry,
+                            (int)(prc->brx - prc->tlx), 
+                            (int)(prc->bry - prc->tly));
+                    if (!prc->cblks) {
+                        continue;
+                    }
+                    for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
+                      ++cblkno, ++cblk) {
+                        fprintf(stderr, "         cblk %5d %5d %5d %5d\n",
+                                (int)jas_seq2d_xstart(cblk->data),
+                                (int)jas_seq2d_ystart(cblk->data),
+                                (int)jas_seq2d_xend(cblk->data),
+                                (int)jas_seq2d_yend(cblk->data));
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+
+static int jpc_enc_encodemainbody(jpc_enc_t *enc)
+{
+    int tileno;
+    int tilex;
+    int tiley;
+    int i;
+    jpc_sot_t *sot;
+    jpc_enc_tcmpt_t *comp;
+    jpc_enc_tcmpt_t *endcomps;
+    jpc_enc_band_t *band;
+    jpc_enc_band_t *endbands;
+    jpc_enc_rlvl_t *lvl;
+    int rlvlno;
+    jpc_qcc_t *qcc;
+    jpc_cod_t *cod;
+    int adjust;
+    int j;
+    int absbandno;
+    long numbytes;
+    long tilehdrlen;
+    long tilelen;
+    jpc_enc_tile_t *tile;
+    jpc_enc_cp_t *cp;
+    double rho;
+    uint_fast16_t cmptno;
+    int samestepsizes;
+    jpc_enc_ccp_t *ccps;
+    jpc_enc_tccp_t *tccp;
+    int bandno;
+    uint_fast32_t x;
+    uint_fast32_t y;
+    int mingbits;
+    int actualnumbps;
+    jpc_fix_t mxmag;
+    jpc_fix_t mag;
+    int numgbits;
+    const char * error;
+
+    cp = enc->cp;
+
+    /* Avoid compile warnings. */
+    numbytes = 0;
+
+    for (tileno = 0; tileno < cp->numtiles; ++tileno) {
+        tilex = tileno % cp->numhtiles;
+        tiley = tileno / cp->numhtiles;
+
+        enc->curtile = jpc_enc_tile_create(enc->cp, enc->image, tileno);
+        if (!enc->curtile)
+            abort();
+
+        tile = enc->curtile;
+
+        if (jas_getdbglevel() >= 10) {
+            jpc_enc_dump(enc);
+        }
+
+        endcomps = &tile->tcmpts[tile->numtcmpts];
+        for (cmptno = 0, comp = tile->tcmpts;
+             cmptno < tile->numtcmpts;
+             ++cmptno, ++comp) {
+            if (!cp->ccps[cmptno].sgnd) {
+                adjust = 1 << (cp->ccps[cmptno].prec - 1);
+                for (i = 0; i < jas_matrix_numrows(comp->data); ++i) {
+                    for (j = 0; j < jas_matrix_numcols(comp->data); ++j) {
+                        *jas_matrix_getref(comp->data, i, j) -= adjust;
+                    }
+                }
+            }
+        }
+
+        if (!tile->intmode) {
+            endcomps = &tile->tcmpts[tile->numtcmpts];
+            for (comp = tile->tcmpts; comp != endcomps; ++comp) {
+                jas_matrix_asl(comp->data, JPC_FIX_FRACBITS);
+            }
+        }
+
+        switch (tile->mctid) {
+        case JPC_MCT_RCT:
+            assert(jas_image_numcmpts(enc->image) == 3);
+            jpc_rct(tile->tcmpts[0].data, tile->tcmpts[1].data,
+                    tile->tcmpts[2].data);
+            break;
+        case JPC_MCT_ICT:
+            assert(jas_image_numcmpts(enc->image) == 3);
+            jpc_ict(tile->tcmpts[0].data, tile->tcmpts[1].data,
+                    tile->tcmpts[2].data);
+            break;
+        default:
+            break;
+        }
+
+        for (i = 0; i < jas_image_numcmpts(enc->image); ++i) {
+            comp = &tile->tcmpts[i];
+            jpc_tsfb_analyze(comp->tsfb,
+                             ((comp->qmfbid == JPC_COX_RFT) ?
+                              JPC_TSFB_RITIMODE : 0), comp->data);
+
+        }
+
+
+        endcomps = &tile->tcmpts[tile->numtcmpts];
+        for (cmptno = 0, comp = tile->tcmpts;
+             comp != endcomps;
+             ++cmptno, ++comp) {
+            mingbits = 0;
+            absbandno = 0;
+            /* All bands must have a corresponding quantizer step size,
+               even if they contain no samples and are never coded. */
+            /* Some bands may not be hit by the loop below, so we must
+               initialize all of the step sizes to a sane value. */
+            memset(comp->stepsizes, 0, sizeof(comp->stepsizes));
+            for (rlvlno = 0, lvl = comp->rlvls;
+                 rlvlno < comp->numrlvls;
+                 ++rlvlno, ++lvl) {
+                if (!lvl->bands) {
+                    absbandno += rlvlno ? 3 : 1;
+                    continue;
+                }
+                endbands = &lvl->bands[lvl->numbands];
+                for (band = lvl->bands; band != endbands; ++band) {
+                    if (!band->data) {
+                        ++absbandno;
+                        continue;
+                    }
+                    actualnumbps = 0;
+                    mxmag = 0;
+                    for (y = 0; y < jas_matrix_numrows(band->data); ++y) {
+                        for (x = 0; x < jas_matrix_numcols(band->data); ++x) {
+                            mag = abs(jas_matrix_get(band->data, y, x));
+                            if (mag > mxmag) {
+                                mxmag = mag;
+                            }
+                        }
+                    }
+                    if (tile->intmode) {
+                        actualnumbps =
+                            jpc_firstone(mxmag) + 1;
+                    } else {
+                        actualnumbps =
+                            jpc_firstone(mxmag) + 1 - JPC_FIX_FRACBITS;
+                    }
+                    numgbits = actualnumbps - (cp->ccps[cmptno].prec - 1 +
+                                               band->analgain);
+                    if (numgbits > mingbits) {
+                        mingbits = numgbits;
+                    }
+                    if (!tile->intmode) {
+                        band->absstepsize =
+                            jpc_fix_div(
+                                jpc_inttofix(1 << (band->analgain + 1)),
+                                band->synweight);
+                    } else {
+                        band->absstepsize = jpc_inttofix(1);
+                    }
+                    band->stepsize = jpc_abstorelstepsize(
+                        band->absstepsize, cp->ccps[cmptno].prec +
+                        band->analgain);
+                    band->numbps = cp->tccp.numgbits +
+                        JPC_QCX_GETEXPN(band->stepsize) - 1;
+
+                    if ((!tile->intmode) && band->data) {
+                        quantize(band->data, band->absstepsize);
+                    }
+
+                    comp->stepsizes[absbandno] = band->stepsize;
+                    ++absbandno;
+                }
+            }
+
+            assert(JPC_FIX_FRACBITS >= JPC_NUMEXTRABITS);
+            if (!tile->intmode) {
+                jas_matrix_divpow2(comp->data,
+                                   JPC_FIX_FRACBITS - JPC_NUMEXTRABITS);
+            } else {
+                jas_matrix_asl(comp->data, JPC_NUMEXTRABITS);
+            }
+        }
+
+        if (mingbits > cp->tccp.numgbits) {
+            fprintf(stderr, "error: too few guard bits (need at least %d)\n",
+                    mingbits);
+            return -1;
+        }
+
+        if (!(enc->tmpstream = jas_stream_memopen(0, 0))) {
+            fprintf(stderr, "cannot open tmp file\n");
+            return -1;
+        }
+
+        /* Write the tile header. */
+        if (!(enc->mrk = jpc_ms_create(JPC_MS_SOT))) {
+            return -1;
+        }
+        sot = &enc->mrk->parms.sot;
+        sot->len = 0;
+        sot->tileno = tileno;
+        sot->partno = 0;
+        sot->numparts = 1;
+        if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
+            fprintf(stderr, "cannot write SOT marker\n");
+            return -1;
+        }
+        jpc_ms_destroy(enc->mrk);
+        enc->mrk = 0;
+
+/************************************************************************/
+/************************************************************************/
+/************************************************************************/
+
+        tccp = &cp->tccp;
+        for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) {
+            comp = &tile->tcmpts[cmptno];
+            if (comp->numrlvls != tccp->maxrlvls) {
+                if (!(enc->mrk = jpc_ms_create(JPC_MS_COD))) {
+                    return -1;
+                }
+                /* XXX = this is not really correct. we are using comp #0's
+                   precint sizes and other characteristics */
+                comp = &tile->tcmpts[0];
+                cod = &enc->mrk->parms.cod;
+                cod->compparms.csty = 0;
+                cod->compparms.numdlvls = comp->numrlvls - 1;
+                cod->prg = tile->prg;
+                cod->numlyrs = tile->numlyrs;
+                cod->compparms.cblkwidthval =
+                    JPC_COX_CBLKSIZEEXPN(comp->cblkwidthexpn);
+                cod->compparms.cblkheightval =
+                    JPC_COX_CBLKSIZEEXPN(comp->cblkheightexpn);
+                cod->compparms.cblksty = comp->cblksty;
+                cod->compparms.qmfbid = comp->qmfbid;
+                cod->mctrans = (tile->mctid != JPC_MCT_NONE);
+                for (i = 0; i < comp->numrlvls; ++i) {
+                    cod->compparms.rlvls[i].parwidthval =
+                        comp->rlvls[i].prcwidthexpn;
+                    cod->compparms.rlvls[i].parheightval =
+                        comp->rlvls[i].prcheightexpn;
+                }
+                if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
+                    return -1;
+                }
+                jpc_ms_destroy(enc->mrk);
+                enc->mrk = 0;
+            }
+        }
+
+        for (cmptno = 0, comp = tile->tcmpts;
+             cmptno < cp->numcmpts;
+             ++cmptno, ++comp) {
+            ccps = &cp->ccps[cmptno];
+            if (ccps->numstepsizes == comp->numstepsizes) {
+                samestepsizes = 1;
+                for (bandno = 0; bandno < ccps->numstepsizes; ++bandno) {
+                    if (ccps->stepsizes[bandno] != comp->stepsizes[bandno]) {
+                        samestepsizes = 0;
+                        break;
+                    }
+                }
+            } else {
+                samestepsizes = 0;
+            }
+            if (!samestepsizes) {
+                if (!(enc->mrk = jpc_ms_create(JPC_MS_QCC))) {
+                    return -1;
+                }
+                qcc = &enc->mrk->parms.qcc;
+                qcc->compno = cmptno;
+                qcc->compparms.numguard = cp->tccp.numgbits;
+                qcc->compparms.qntsty = (comp->qmfbid == JPC_COX_INS) ?
+                    JPC_QCX_SEQNT : JPC_QCX_NOQNT;
+                qcc->compparms.numstepsizes = comp->numstepsizes;
+                qcc->compparms.stepsizes = comp->stepsizes;
+                if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
+                    return -1;
+                }
+                qcc->compparms.stepsizes = 0;
+                jpc_ms_destroy(enc->mrk);
+                enc->mrk = 0;
+            }
+        }
+
+        /* Write a SOD marker to indicate the end of the tile header. */
+        if (!(enc->mrk = jpc_ms_create(JPC_MS_SOD))) {
+            return -1;
+        }
+        if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
+            fprintf(stderr, "cannot write SOD marker\n");
+            return -1;
+        }
+        jpc_ms_destroy(enc->mrk);
+        enc->mrk = 0;
+        tilehdrlen = jas_stream_getrwcount(enc->tmpstream);
+
+/************************************************************************/
+/************************************************************************/
+/************************************************************************/
+
+        if (jpc_enc_enccblks(enc)) {
+            abort();
+            return -1;
+        }
+
+        cp = enc->cp;
+        rho = (double) (tile->brx - tile->tlx) * (tile->bry - tile->tly) /
+            ((cp->refgrdwidth - cp->imgareatlx) * (cp->refgrdheight -
+                                                   cp->imgareatly));
+        tile->rawsize = cp->rawsize * rho;
+
+        computeLayerSizes(enc, tile, cp, rho, tilehdrlen, &error);
+        
+        if (!error) {
+            int rc;
+            performTier2Coding(enc, tile->numlyrs, tile->lyrsizes, &error);
+
+            rc =  jpc_enc_encodetiledata(enc);
+            if (rc != 0)
+                pm_asprintf(&error, "jpc_enc_encodetiledata() failed\n");
+        }
+
+        if (error) {
+            fprintf(stderr, "%s\n", error);
+            pm_strfree(error);
+            return -1;
+        }
+
+        tilelen = jas_stream_tell(enc->tmpstream);
+
+        if (jas_stream_seek(enc->tmpstream, 6, SEEK_SET) < 0) {
+            return -1;
+        }
+        jpc_putuint32(enc->tmpstream, tilelen);
+
+        if (jas_stream_seek(enc->tmpstream, 0, SEEK_SET) < 0) {
+            return -1;
+        }
+        if (jpc_putdata(enc->out, enc->tmpstream, -1)) {
+            return -1;
+        }
+        enc->len += tilelen;
+
+        jas_stream_close(enc->tmpstream);
+        enc->tmpstream = 0;
+
+        jpc_enc_tile_destroy(enc->curtile);
+        enc->curtile = 0;
+
+    }
+
+    return 0;
 }
+
+
+
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * 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__
+ */
+
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_math.h b/converter/other/jpeg2000/libjasper/jpc/jpc_math.h
index e343bab1..77df0c62 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_math.h
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_math.h
@@ -135,7 +135,7 @@
 #define	JPC_CEILDIVPOW2(x, y) \
 	(assert(x >= 0 && y >= 0), ((x) + (1 << (y)) - 1) >> (y))
 /* JPC_CEILDIVPOW2U is for unsigned arguments (JPC_CEILDIVPOW2 would generate
-   compiler warnings due to its superfluous >= 0 check)
+   compiler warnings because of its superfluous >= 0 check)
 */
 #define	JPC_CEILDIVPOW2U(x, y) \
 	(((x) + (1 << (y)) - 1) >> (y))
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h b/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h
index 914f8e8a..5f99e021 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_mqcod.h
@@ -123,7 +123,7 @@
 * Includes.
 \*****************************************************************************/
 
-#include "pm_c_util.h"
+#include "netpbm/pm_c_util.h"
 #include "jasper/jas_types.h"
 
 /*****************************************************************************\
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.c b/converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.c
index 0219a000..3f6122e3 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_mqenc.c
@@ -135,91 +135,91 @@
 \******************************************************************************/
 
 #if defined(DEBUG)
-#define	JPC_MQENC_CALL(n, x) \
-	((jas_getdbglevel() >= (n)) ? ((void)(x)) : ((void)0))
+#define JPC_MQENC_CALL(n, x) \
+    ((jas_getdbglevel() >= (n)) ? ((void)(x)) : ((void)0))
 #else
-#define	JPC_MQENC_CALL(n, x)
+#define JPC_MQENC_CALL(n, x)
 #endif
 
-#define	jpc_mqenc_codemps9(areg, creg, ctreg, curctx, enc) \
+#define jpc_mqenc_codemps9(areg, creg, ctreg, curctx, enc) \
 { \
-	jpc_mqstate_t *state = *(curctx); \
-	(areg) -= state->qeval; \
-	if (!((areg) & 0x8000)) { \
-		if ((areg) < state->qeval) { \
-			(areg) = state->qeval; \
-		} else { \
-			(creg) += state->qeval; \
-		} \
-		*(curctx) = state->nmps; \
-		jpc_mqenc_renorme((areg), (creg), (ctreg), (enc)); \
-	} else { \
-		(creg) += state->qeval; \
-	} \
+    jpc_mqstate_t *state = *(curctx); \
+    (areg) -= state->qeval; \
+    if (!((areg) & 0x8000)) { \
+        if ((areg) < state->qeval) { \
+            (areg) = state->qeval; \
+        } else { \
+            (creg) += state->qeval; \
+        } \
+        *(curctx) = state->nmps; \
+        jpc_mqenc_renorme((areg), (creg), (ctreg), (enc)); \
+    } else { \
+        (creg) += state->qeval; \
+    } \
 }
 
-#define	jpc_mqenc_codelps2(areg, creg, ctreg, curctx, enc) \
+#define jpc_mqenc_codelps2(areg, creg, ctreg, curctx, enc) \
 { \
-	jpc_mqstate_t *state = *(curctx); \
-	(areg) -= state->qeval; \
-	if ((areg) < state->qeval) { \
-		(creg) += state->qeval; \
-	} else { \
-		(areg) = state->qeval; \
-	} \
-	*(curctx) = state->nlps; \
-	jpc_mqenc_renorme((areg), (creg), (ctreg), (enc)); \
+    jpc_mqstate_t *state = *(curctx); \
+    (areg) -= state->qeval; \
+    if ((areg) < state->qeval) { \
+        (creg) += state->qeval; \
+    } else { \
+        (areg) = state->qeval; \
+    } \
+    *(curctx) = state->nlps; \
+    jpc_mqenc_renorme((areg), (creg), (ctreg), (enc)); \
 }
 
-#define	jpc_mqenc_renorme(areg, creg, ctreg, enc) \
+#define jpc_mqenc_renorme(areg, creg, ctreg, enc) \
 { \
-	do { \
-		(areg) <<= 1; \
-		(creg) <<= 1; \
-		if (!--(ctreg)) { \
-			jpc_mqenc_byteout((areg), (creg), (ctreg), (enc)); \
-		} \
-	} while (!((areg) & 0x8000)); \
+    do { \
+        (areg) <<= 1; \
+        (creg) <<= 1; \
+        if (!--(ctreg)) { \
+            jpc_mqenc_byteout((areg), (creg), (ctreg), (enc)); \
+        } \
+    } while (!((areg) & 0x8000)); \
 }
 
-#define	jpc_mqenc_byteout(areg, creg, ctreg, enc) \
+#define jpc_mqenc_byteout(areg, creg, ctreg, enc) \
 { \
-	if ((enc)->outbuf != 0xff) { \
-		if ((creg) & 0x8000000) { \
-			if (++((enc)->outbuf) == 0xff) { \
-				(creg) &= 0x7ffffff; \
-				jpc_mqenc_byteout2(enc); \
-				enc->outbuf = ((creg) >> 20) & 0xff; \
-				(creg) &= 0xfffff; \
-				(ctreg) = 7; \
-			} else { \
-				jpc_mqenc_byteout2(enc); \
-				enc->outbuf = ((creg) >> 19) & 0xff; \
-				(creg) &= 0x7ffff; \
-				(ctreg) = 8; \
-			} \
-		} else { \
-			jpc_mqenc_byteout2(enc); \
-			(enc)->outbuf = ((creg) >> 19) & 0xff; \
-			(creg) &= 0x7ffff; \
-			(ctreg) = 8; \
-		} \
-	} else { \
-		jpc_mqenc_byteout2(enc); \
-		(enc)->outbuf = ((creg) >> 20) & 0xff; \
-		(creg) &= 0xfffff; \
-		(ctreg) = 7; \
-	} \
+    if ((enc)->outbuf != 0xff) { \
+        if ((creg) & 0x8000000) { \
+            if (++((enc)->outbuf) == 0xff) { \
+                (creg) &= 0x7ffffff; \
+                jpc_mqenc_byteout2(enc); \
+                enc->outbuf = ((creg) >> 20) & 0xff; \
+                (creg) &= 0xfffff; \
+                (ctreg) = 7; \
+            } else { \
+                jpc_mqenc_byteout2(enc); \
+                enc->outbuf = ((creg) >> 19) & 0xff; \
+                (creg) &= 0x7ffff; \
+                (ctreg) = 8; \
+            } \
+        } else { \
+            jpc_mqenc_byteout2(enc); \
+            (enc)->outbuf = ((creg) >> 19) & 0xff; \
+            (creg) &= 0x7ffff; \
+            (ctreg) = 8; \
+        } \
+    } else { \
+        jpc_mqenc_byteout2(enc); \
+        (enc)->outbuf = ((creg) >> 20) & 0xff; \
+        (creg) &= 0xfffff; \
+        (ctreg) = 7; \
+    } \
 }
 
-#define	jpc_mqenc_byteout2(enc) \
+#define jpc_mqenc_byteout2(enc) \
 { \
-	if (enc->outbuf >= 0) { \
-		if (jas_stream_putc(enc->out, (unsigned char)enc->outbuf) == EOF) { \
-			enc->err |= 1; \
-		} \
-	} \
-	enc->lastbyte = enc->outbuf; \
+    if (enc->outbuf >= 0) { \
+        if (jas_stream_putc(enc->out, (unsigned char)enc->outbuf) == EOF) { \
+            enc->err |= 1; \
+        } \
+    } \
+    enc->lastbyte = enc->outbuf; \
 }
 
 /******************************************************************************\
@@ -236,45 +236,45 @@ static void jpc_mqenc_setbits(jpc_mqenc_t *mqenc);
 
 jpc_mqenc_t *jpc_mqenc_create(int maxctxs, jas_stream_t *out)
 {
-	jpc_mqenc_t *mqenc;
+    jpc_mqenc_t *mqenc;
 
-	/* Allocate memory for the MQ encoder. */
-	if (!(mqenc = jas_malloc(sizeof(jpc_mqenc_t)))) {
-		goto error;
-	}
-	mqenc->out = out;
-	mqenc->maxctxs = maxctxs;
+    /* Allocate memory for the MQ encoder. */
+    if (!(mqenc = jas_malloc(sizeof(jpc_mqenc_t)))) {
+        goto error;
+    }
+    mqenc->out = out;
+    mqenc->maxctxs = maxctxs;
 
-	/* Allocate memory for the per-context state information. */
-	if (!(mqenc->ctxs = jas_malloc(mqenc->maxctxs * sizeof(jpc_mqstate_t *)))) {
-		goto error;
-	}
+    /* Allocate memory for the per-context state information. */
+    if (!(mqenc->ctxs = jas_malloc(mqenc->maxctxs * sizeof(jpc_mqstate_t *)))) {
+        goto error;
+    }
 
-	/* Set the current context to the first one. */
-	mqenc->curctx = mqenc->ctxs;
+    /* Set the current context to the first one. */
+    mqenc->curctx = mqenc->ctxs;
 
-	jpc_mqenc_init(mqenc);
+    jpc_mqenc_init(mqenc);
 
-	/* Initialize the per-context state information to something sane. */
-	jpc_mqenc_setctxs(mqenc, 0, 0);
+    /* Initialize the per-context state information to something sane. */
+    jpc_mqenc_setctxs(mqenc, 0, 0);
 
-	return mqenc;
+    return mqenc;
 
 error:
-	if (mqenc) {
-		jpc_mqenc_destroy(mqenc);
-	}
-	return 0;
+    if (mqenc) {
+        jpc_mqenc_destroy(mqenc);
+    }
+    return 0;
 }
 
 /* Destroy a MQ encoder. */
 
 void jpc_mqenc_destroy(jpc_mqenc_t *mqenc)
 {
-	if (mqenc->ctxs) {
-		jas_free(mqenc->ctxs);
-	}
-	jas_free(mqenc);
+    if (mqenc->ctxs) {
+        jas_free(mqenc->ctxs);
+    }
+    jas_free(mqenc);
 }
 
 /******************************************************************************\
@@ -285,33 +285,33 @@ void jpc_mqenc_destroy(jpc_mqenc_t *mqenc)
 
 void jpc_mqenc_init(jpc_mqenc_t *mqenc)
 {
-	mqenc->areg = 0x8000;
-	mqenc->outbuf = -1;
-	mqenc->creg = 0;
-	mqenc->ctreg = 12;
-	mqenc->lastbyte = -1;
-	mqenc->err = 0;
+    mqenc->areg = 0x8000;
+    mqenc->outbuf = -1;
+    mqenc->creg = 0;
+    mqenc->ctreg = 12;
+    mqenc->lastbyte = -1;
+    mqenc->err = 0;
 }
 
 /* Initialize one or more contexts. */
 
 void jpc_mqenc_setctxs(jpc_mqenc_t *mqenc, int numctxs, jpc_mqctx_t *ctxs)
 {
-	jpc_mqstate_t **ctx;
-	int n;
-
-	ctx = mqenc->ctxs;
-	n = JAS_MIN(mqenc->maxctxs, numctxs);
-	while (--n >= 0) {
-		*ctx = &jpc_mqstates[2 * ctxs->ind + ctxs->mps];
-		++ctx;
-		++ctxs;
-	}
-	n = mqenc->maxctxs - numctxs;
-	while (--n >= 0) {
-		*ctx = &jpc_mqstates[0];
-		++ctx;
-	}
+    jpc_mqstate_t **ctx;
+    int n;
+
+    ctx = mqenc->ctxs;
+    n = JAS_MIN(mqenc->maxctxs, numctxs);
+    while (--n >= 0) {
+        *ctx = &jpc_mqstates[2 * ctxs->ind + ctxs->mps];
+        ++ctx;
+        ++ctxs;
+    }
+    n = mqenc->maxctxs - numctxs;
+    while (--n >= 0) {
+        *ctx = &jpc_mqstates[0];
+        ++ctx;
+    }
 
 }
 
@@ -319,10 +319,10 @@ void jpc_mqenc_setctxs(jpc_mqenc_t *mqenc, int numctxs, jpc_mqctx_t *ctxs)
 
 void jpc_mqenc_getstate(jpc_mqenc_t *mqenc, jpc_mqencstate_t *state)
 {
-	state->areg = mqenc->areg;
-	state->creg = mqenc->creg;
-	state->ctreg = mqenc->ctreg;
-	state->lastbyte = mqenc->lastbyte;
+    state->areg = mqenc->areg;
+    state->creg = mqenc->creg;
+    state->ctreg = mqenc->ctreg;
+    state->lastbyte = mqenc->lastbyte;
 }
 
 /******************************************************************************\
@@ -333,49 +333,49 @@ void jpc_mqenc_getstate(jpc_mqenc_t *mqenc, jpc_mqencstate_t *state)
 
 int jpc_mqenc_putbit_func(jpc_mqenc_t *mqenc, int bit)
 {
-	const jpc_mqstate_t *state;
-	JAS_DBGLOG(100, ("jpc_mqenc_putbit(%p, %d)\n", mqenc, bit));
-	JPC_MQENC_CALL(100, jpc_mqenc_dump(mqenc, stderr));
-
-	state = *(mqenc->curctx);
-
-	if (state->mps == bit) {
-		/* Apply the CODEMPS algorithm as defined in the standard. */
-		mqenc->areg -= state->qeval;
-		if (!(mqenc->areg & 0x8000)) {
-			jpc_mqenc_codemps2(mqenc);
-		} else {
-			mqenc->creg += state->qeval;
-		}
-	} else {
-		/* Apply the CODELPS algorithm as defined in the standard. */
-		jpc_mqenc_codelps2(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc->curctx, mqenc);
-	}
-
-	return jpc_mqenc_error(mqenc) ? (-1) : 0;
+    const jpc_mqstate_t *state;
+    JAS_DBGLOG(100, ("jpc_mqenc_putbit(%p, %d)\n", mqenc, bit));
+    JPC_MQENC_CALL(100, jpc_mqenc_dump(mqenc, stderr));
+
+    state = *(mqenc->curctx);
+
+    if (state->mps == bit) {
+        /* Apply the CODEMPS algorithm as defined in the standard. */
+        mqenc->areg -= state->qeval;
+        if (!(mqenc->areg & 0x8000)) {
+            jpc_mqenc_codemps2(mqenc);
+        } else {
+            mqenc->creg += state->qeval;
+        }
+    } else {
+        /* Apply the CODELPS algorithm as defined in the standard. */
+        jpc_mqenc_codelps2(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc->curctx, mqenc);
+    }
+
+    return jpc_mqenc_error(mqenc) ? (-1) : 0;
 }
 
 int jpc_mqenc_codemps2(jpc_mqenc_t *mqenc)
 {
-	/* Note: This function only performs part of the work associated with
-	the CODEMPS algorithm from the standard.  Some of the work is also
-	performed by the caller. */
-
-	jpc_mqstate_t *state = *(mqenc->curctx);
-	if (mqenc->areg < state->qeval) {
-		mqenc->areg = state->qeval;
-	} else {
-		mqenc->creg += state->qeval;
-	}
-	*mqenc->curctx = state->nmps;
-	jpc_mqenc_renorme(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
-	return jpc_mqenc_error(mqenc) ? (-1) : 0;
+    /* Note: This function only performs part of the work associated with
+    the CODEMPS algorithm from the standard.  Some of the work is also
+    performed by the caller. */
+
+    jpc_mqstate_t *state = *(mqenc->curctx);
+    if (mqenc->areg < state->qeval) {
+        mqenc->areg = state->qeval;
+    } else {
+        mqenc->creg += state->qeval;
+    }
+    *mqenc->curctx = state->nmps;
+    jpc_mqenc_renorme(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
+    return jpc_mqenc_error(mqenc) ? (-1) : 0;
 }
 
 int jpc_mqenc_codelps(jpc_mqenc_t *mqenc)
 {
-	jpc_mqenc_codelps2(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc->curctx, mqenc);
-	return jpc_mqenc_error(mqenc) ? (-1) : 0;
+    jpc_mqenc_codelps2(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc->curctx, mqenc);
+    return jpc_mqenc_error(mqenc) ? (-1) : 0;
 }
 
 /******************************************************************************\
@@ -386,56 +386,56 @@ int jpc_mqenc_codelps(jpc_mqenc_t *mqenc)
 
 int jpc_mqenc_flush(jpc_mqenc_t *mqenc, int termmode)
 {
-	int_fast16_t k;
-
-	switch (termmode) {
-	case JPC_MQENC_PTERM:
-		k = 11 - mqenc->ctreg + 1;
-		while (k > 0) {
-			mqenc->creg <<= mqenc->ctreg;
-			mqenc->ctreg = 0;
-			jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg,
-			  mqenc);
-			k -= mqenc->ctreg;
-		}
-		if (mqenc->outbuf != 0xff) {
-			jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
-		}
-		break;
-	case JPC_MQENC_DEFTERM:
-		jpc_mqenc_setbits(mqenc);
-		mqenc->creg <<= mqenc->ctreg;
-		jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
-		mqenc->creg <<= mqenc->ctreg;
-		jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
-		if (mqenc->outbuf != 0xff) {
-			jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
-		}
-		break;
-	default:
-		abort();
-		break;
-	}
-	return 0;
+    int_fast16_t k;
+
+    switch (termmode) {
+    case JPC_MQENC_PTERM:
+        k = 11 - mqenc->ctreg + 1;
+        while (k > 0) {
+            mqenc->creg <<= mqenc->ctreg;
+            mqenc->ctreg = 0;
+            jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg,
+              mqenc);
+            k -= mqenc->ctreg;
+        }
+        if (mqenc->outbuf != 0xff) {
+            jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
+        }
+        break;
+    case JPC_MQENC_DEFTERM:
+        jpc_mqenc_setbits(mqenc);
+        mqenc->creg <<= mqenc->ctreg;
+        jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
+        mqenc->creg <<= mqenc->ctreg;
+        jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
+        if (mqenc->outbuf != 0xff) {
+            jpc_mqenc_byteout(mqenc->areg, mqenc->creg, mqenc->ctreg, mqenc);
+        }
+        break;
+    default:
+        abort();
+        break;
+    }
+    return 0;
 }
 
 static void jpc_mqenc_setbits(jpc_mqenc_t *mqenc)
 {
-	uint_fast32_t tmp = mqenc->creg + mqenc->areg;
-	mqenc->creg |= 0xffff;
-	if (mqenc->creg >= tmp) {
-		mqenc->creg -= 0x8000;
-	}
+    uint_fast32_t tmp = mqenc->creg + mqenc->areg;
+    mqenc->creg |= 0xffff;
+    if (mqenc->creg >= tmp) {
+        mqenc->creg -= 0x8000;
+    }
 }
 
 /* Dump a MQ encoder to a stream for debugging. */
 
 int jpc_mqenc_dump(jpc_mqenc_t *mqenc, FILE *out)
 {
-	fprintf(out, "AREG = %08x, CREG = %08x, CTREG = %d\n",
-	  mqenc->areg, mqenc->creg, mqenc->ctreg);
-	fprintf(out, "IND = %02d, MPS = %d, QEVAL = %04x\n",
-	  *mqenc->curctx - jpc_mqstates, (*mqenc->curctx)->mps,
-	  (*mqenc->curctx)->qeval);
-	return 0;
+    fprintf(out, "AREG = %08x, CREG = %08x, CTREG = %d\n",
+            (unsigned)mqenc->areg, (unsigned)mqenc->creg, (int)mqenc->ctreg);
+    fprintf(out, "IND = %02d, MPS = %d, QEVAL = %04x\n",
+            (int)(*mqenc->curctx - jpc_mqstates), (int)(*mqenc->curctx)->mps,
+            (unsigned)(*mqenc->curctx)->qeval);
+    return 0;
 }
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c b/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c
index 1d41d5c5..80bc5aa5 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c
@@ -900,8 +900,8 @@ static void jpc_ns_analyze(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
 			startptr += interstep;
 		}
 	} else {
-		/* The reversible integer-to-integer mode is not supported
-		  for this transform. */
+		/* The reversible integer-to-integer mode is not valid for this
+		  transform. */
 		abort();
 	}
 }
@@ -973,8 +973,8 @@ static void jpc_ns_synthesize(jpc_qmfb1d_t *qmfb, int flags, jas_seq2d_t *x)
 			startptr += interstep;
 		}
 	} else {
-		/* The reversible integer-to-integer mode is not supported
-		  for this transform. */
+		/* The reversible integer-to-integer mode is not valid
+           for this transform. */
 		abort();
 	}
 }
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.h b/converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.h
index 05f41b9e..82dafcce 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.h
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t2cod.h
@@ -136,7 +136,7 @@ typedef struct {
 	/* The number of progression changes. */
 	int numpchgs;
 
-	/* The maximum number of progression changes that can be accomodated
+	/* The maximum number of progression changes that can be accommodated
 	  without growing the progression change array. */
 	int maxpchgs;
 
@@ -253,7 +253,7 @@ typedef struct {
 	/* The progression change list. */
 	jpc_pchglist_t *pchglist;
 
-	/* The progression to use in the absense of explicit specification. */
+	/* The progression to use in the absence of explicit specification. */
 	jpc_pchg_t defaultpchg;
 
 	/* The current progression change number. */
diff --git a/converter/other/jpeg2000/pamtojpeg2k.c b/converter/other/jpeg2000/pamtojpeg2k.c
index 69ceb22b..142e452f 100644
--- a/converter/other/jpeg2000/pamtojpeg2k.c
+++ b/converter/other/jpeg2000/pamtojpeg2k.c
@@ -9,17 +9,24 @@
 *****************************************************************************/
 
 #define _BSD_SOURCE 1    /* Make sure strdup() is in string.h */
-/* Make sure strdup() is in string.h and int_fast32_t is in inttypes.h */
-#define _XOPEN_SOURCE 600
+#define _XOPEN_SOURCE 500 /* Make sure strdup() is in string.h */
+    /* In 2014.09, this was _XOPEN_SOURCE 600, with a comment saying it was
+       necessary to make <inttypes.h> define int_fast32_t, etc. on AIX.
+       <jasper/jasper.h> does use int_fast32_t and does include <inttypes.h>,
+       but plenty of source files of libjasper do too, and they did not have
+       _XOPEN_SOURCE 600, so it would seem to be superfluous here too.
+    */
+
 #include <string.h>
 
+#include <jasper/jasper.h>
+
 #include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "nstring.h"
 #include "mallocvar.h"
 
-#include <jasper/jasper.h>
 #include "libjasper_compat.h"
 
 
@@ -43,7 +50,8 @@ struct cmdlineInfo {
     unsigned int cblkwidth;
     unsigned int cblkheight;
     enum compmode compmode;
-    float        compressionRatio;
+    unsigned int compressionSpec;
+    float        compression;
     char *       ilyrrates;
     enum progression progression;
     unsigned int numrlvls;
@@ -81,7 +89,7 @@ parseCommandLine(int argc, char ** argv,
     unsigned int tilewidthSpec, tileheightSpec;
     unsigned int prcwidthSpec, prcheightSpec;
     unsigned int cblkwidthSpec, cblkheightSpec;
-    unsigned int modeSpec, compressionSpec, ilyrratesSpec;
+    unsigned int modeSpec, ilyrratesSpec;
     unsigned int progressionSpec, numrlvlsSpec, numgbitsSpec;
     unsigned int debuglevelSpec;
 
@@ -115,8 +123,8 @@ parseCommandLine(int argc, char ** argv,
             &cblkheightSpec,     0);
     OPTENT3(0, "mode",         OPT_STRING, &modeOpt,
             &modeSpec,           0);
-    OPTENT3(0, "compression",  OPT_FLOAT,  &cmdlineP->compressionRatio,
-            &compressionSpec,    0);
+    OPTENT3(0, "compression",  OPT_FLOAT,  &cmdlineP->compression,
+            &cmdlineP->compressionSpec,    0);
     OPTENT3(0, "ilyrrates",    OPT_STRING, &cmdlineP->ilyrrates,
             &ilyrratesSpec,      0);
     OPTENT3(0, "progression",  OPT_STRING, &progressionOpt,
@@ -152,7 +160,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
 
     if (!imgareatlxSpec)
         cmdlineP->imgareatlx = 0;
@@ -184,11 +192,6 @@ parseCommandLine(int argc, char ** argv,
                      "valid values are 'INTEGER' and 'REAL'", modeOpt);
     } else
         cmdlineP->compmode = COMPMODE_INTEGER;
-    if (compressionSpec) {
-        if (cmdlineP->compressionRatio < 1.0)
-            pm_error("Compression ratio less than 1 does not make sense.");
-    } else
-        cmdlineP->compressionRatio = 1.0;
     if (!ilyrratesSpec)
         cmdlineP->ilyrrates = (char*) "";
     if (progressionSpec) {
@@ -230,7 +233,10 @@ parseCommandLine(int argc, char ** argv,
 static void
 createJasperRaster(struct pam *  const inpamP, 
                    jas_image_t * const jasperP) {
-
+/*----------------------------------------------------------------------------
+   Create the raster in the *jasperP object, reading the raster from the
+   input file described by *inpamP, which is positioned to the raster.
+-----------------------------------------------------------------------------*/
     jas_matrix_t ** matrix;  /* malloc'ed */
         /* matrix[X] is the data for Plane X of the current row */
     unsigned int plane;
@@ -380,7 +386,7 @@ writeJpc(jas_image_t *      const jasperP,
        specifying garbage for the -ilyrrates option 
     */
     if (strlen(cmdline.ilyrrates) > 0)
-        asprintfN(&ilyrratesOpt, "ilyrrates=%s", cmdline.ilyrrates);
+        pm_asprintf(&ilyrratesOpt, "ilyrrates=%s", cmdline.ilyrrates);
     else
         ilyrratesOpt = strdup("");
 
@@ -394,55 +400,62 @@ writeJpc(jas_image_t *      const jasperP,
 
     /* Note that asprintfN() doesn't understand %f, but sprintf() does */
 
-    sprintf(rateOpt, "%1.9f", 1.0/cmdline.compressionRatio);
-
-    asprintfN(&options, 
-              "imgareatlx=%u "
-              "imgareatly=%u "
-              "tilegrdtlx=%u "
-              "tilegrdtly=%u "
-              "tilewidth=%u "
-              "tileheight=%u "
-              "prcwidth=%u "
-              "prcheight=%u "
-              "cblkwidth=%u "
-              "cblkheight=%u "
-              "mode=%s "
-              "rate=%s "
-              "%s "
-              "prg=%s "
-              "numrlvls=%u "
-              "numgbits=%u "
-              "%s %s %s %s %s %s %s %s %s",
-
-              cmdline.imgareatlx,
-              cmdline.imgareatly,
-              cmdline.tilegrdtlx,
-              cmdline.tilegrdtlx,
-              cmdline.tilewidth,
-              cmdline.tileheight,
-              cmdline.prcwidth,
-              cmdline.prcheight,
-              cmdline.cblkwidth,
-              cmdline.cblkheight,
-              cmdline.compmode == COMPMODE_INTEGER ? "int" : "real",
-              rateOpt,
-              ilyrratesOpt,
-              prgValue,
-              cmdline.numrlvls,
-              cmdline.numgbits,
-              cmdline.nomct     ? "nomct"     : "",
-              cmdline.sop       ? "sop"       : "",
-              cmdline.eph       ? "eph"       : "",
-              cmdline.lazy      ? "lazy"      : "",
-              cmdline.termall   ? "termall"   : "",
-              cmdline.segsym    ? "segsym"    : "",
-              cmdline.vcausal   ? "vcausal"   : "",
-              cmdline.pterm     ? "pterm"     : "",
-              cmdline.resetprob ? "resetprob" : ""
+    if (cmdline.compressionSpec)
+        sprintf(rateOpt, "rate=%1.9f", 1.0/cmdline.compression);
+    else {
+        /* No 'rate' option.  This means there is no constraint on the image
+           size, so the encoder will compress losslessly.  Note that the
+           image may get larger, because of metadata.
+        */
+        rateOpt[0] = '\0';
+    }
+    pm_asprintf(&options, 
+                "imgareatlx=%u "
+                "imgareatly=%u "
+                "tilegrdtlx=%u "
+                "tilegrdtly=%u "
+                "tilewidth=%u "
+                "tileheight=%u "
+                "prcwidth=%u "
+                "prcheight=%u "
+                "cblkwidth=%u "
+                "cblkheight=%u "
+                "mode=%s "
+                "%s "    /* rate */
+                "%s "    /* ilyrrates */
+                "prg=%s "
+                "numrlvls=%u "
+                "numgbits=%u "
+                "%s %s %s %s %s %s %s %s %s",
+                
+                cmdline.imgareatlx,
+                cmdline.imgareatly,
+                cmdline.tilegrdtlx,
+                cmdline.tilegrdtlx,
+                cmdline.tilewidth,
+                cmdline.tileheight,
+                cmdline.prcwidth,
+                cmdline.prcheight,
+                cmdline.cblkwidth,
+                cmdline.cblkheight,
+                cmdline.compmode == COMPMODE_INTEGER ? "int" : "real",
+                rateOpt,
+                ilyrratesOpt,
+                prgValue,
+                cmdline.numrlvls,
+                cmdline.numgbits,
+                cmdline.nomct     ? "nomct"     : "",
+                cmdline.sop       ? "sop"       : "",
+                cmdline.eph       ? "eph"       : "",
+                cmdline.lazy      ? "lazy"      : "",
+                cmdline.termall   ? "termall"   : "",
+                cmdline.segsym    ? "segsym"    : "",
+                cmdline.vcausal   ? "vcausal"   : "",
+                cmdline.pterm     ? "pterm"     : "",
+                cmdline.resetprob ? "resetprob" : ""
         );
-    strfree(ilyrratesOpt);
 
+    pm_strfree(ilyrratesOpt);
 
     /* Open the output image file (Standard Output) */
     outStreamP = jas_stream_fdopen(fileno(ofP), "w+b");
@@ -459,7 +472,7 @@ writeJpc(jas_image_t *      const jasperP,
 
         rc = jas_image_encode(jasperP, outStreamP, 
                               jas_image_strtofmt((char*)"jpc"), 
-                              (char*)options);
+                              (char *)options);
         if (rc != 0)
             pm_error("jas_image_encode() failed to encode the JPEG 2000 "
                      "image.  Rc=%d", rc);
@@ -478,7 +491,7 @@ writeJpc(jas_image_t *      const jasperP,
 
 	jas_image_clearfmts();
 
-    strfree(options);
+    pm_strfree(options);
 }
 
 
@@ -487,7 +500,7 @@ int
 main(int argc, char **argv)
 {
     struct cmdlineInfo cmdline;
-    FILE *ifP;
+    FILE * ifP;
     struct pam inpam;
     jas_image_t * jasperP;
 
diff --git a/converter/other/jpegtopnm.c b/converter/other/jpegtopnm.c
index 07a7dfb0..ab3b18e5 100644
--- a/converter/other/jpegtopnm.c
+++ b/converter/other/jpegtopnm.c
@@ -22,7 +22,7 @@
     Extend pamtoppm to convert this to ppm using the standard
     transformation.
 
-    See if additional decompressor options effects signficant speedup.
+    See if additional decompressor options effects significant speedup.
     grayscale output of color image, downscaling, color quantization, and
     dithering are possibilities.  Djpeg's man page says they make it faster.
 
@@ -173,7 +173,7 @@ parseCommandLine(int                  const argc,
    not change argv at all.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -216,12 +216,12 @@ parseCommandLine(int                  const argc,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    /* Make private copy of arguments for optParseOptions to corrupt */
+    /* Make private copy of arguments for pm_optParseOptions to corrupt */
     argc_parse = argc;
     for (i=0; i < argc; ++i)
         argv_parse[i] = argv[i];
 
-    optParseOptions3( &argc_parse, argv_parse, opt, sizeof(opt), 0);
+    pm_optParseOptions3( &argc_parse, argv_parse, opt, sizeof(opt), 0);
         /* Uses and sets argc_parse, argv_parse, 
            and some of *cmdlineP and others. */
 
@@ -582,7 +582,7 @@ print_verbose_info_about_header(struct jpeg_decompress_struct const cinfo){
                colorspace_name(cinfo.jpeg_color_space));
 
     /* Note that raw information about marker, including marker type code,
-       was already printed by the jpeg library, due to the jpeg library
+       was already printed by the jpeg library, because of the jpeg library
        trace level >= 1.  Our job is to interpret it a little bit.
     */
     if (cinfo.marker_list)
@@ -652,19 +652,20 @@ print_exif_info(struct jpeg_marker_struct const marker) {
    Dump as informational messages the contents of the Jpeg miscellaneous
    marker 'marker', assuming it is an Exif header.
 -----------------------------------------------------------------------------*/
-    ImageInfo_t imageInfo;
+    bool const wantTagTrace = false;
+    exif_ImageInfo imageInfo;
     const char * error;
 
     assert(marker.data_length >= 6);
 
-    process_EXIF(marker.data+6, marker.data_length-6, 
-                 &imageInfo, FALSE, &error);
+    exif_parse(marker.data+6, marker.data_length-6, 
+               &imageInfo, wantTagTrace, &error);
 
     if (error) {
         pm_message("EXIF header is invalid.  %s", error);
-        strfree(error);
+        pm_strfree(error);
     } else
-        ShowImageInfo(&imageInfo);
+        exif_showImageInfo(&imageInfo, stderr);
 }
 
 
@@ -700,8 +701,7 @@ dump_exif(struct jpeg_decompress_struct const cinfo) {
 
     found_one = FALSE;  /* initial value */
 
-    for (markerP = cinfo.marker_list;
-         markerP; markerP = markerP->next) 
+    for (markerP = cinfo.marker_list; markerP; markerP = markerP->next) 
         if (is_exif(*markerP)) {
             pm_message("EXIF INFO:");
             print_exif_info(*markerP);
diff --git a/converter/other/pamrgbatopng.c b/converter/other/pamrgbatopng.c
deleted file mode 100644
index 7babf9f9..00000000
--- a/converter/other/pamrgbatopng.c
+++ /dev/null
@@ -1,150 +0,0 @@
-#include <png.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <setjmp.h>
-
-#include "pam.h"
-#include "mallocvar.h"
-
-struct cmdlineInfo {
-    const char * inputFileName;
-};
-
-
-
-static void
-processCommandLine(int                  const argc,
-                   char *               const argv[],
-                   struct cmdlineInfo * const cmdlineP) {
-        
-    if (argc-1 < 1)
-        cmdlineP->inputFileName = "-";
-    else {
-        cmdlineP->inputFileName = argv[1];
-
-        if (argc-1 > 1)
-            pm_error("Too many arguments.  "
-                     "The only argument is the input file name.");
-    }
-}
-
-
-
-static void
-convertPamToPng(const struct pam * const pamP,
-                const tuple *      const tuplerow,
-                png_byte *         const pngRow) {
-    
-    unsigned int col;
-    
-    for (col = 0; col < pamP->width; ++col) {
-        unsigned int plane;
-        
-        for (plane = 0; plane < 4; ++plane)
-            pngRow[4 * col + plane] = tuplerow[col][plane];
-    }
-}
-
-
-
-static void
-writeRaster(const struct pam * const pamP,
-            png_struct *       const pngP) {
-    
-    tuple * tupleRow;
-    png_byte * pngRow;
-    
-    tupleRow = pnm_allocpamrow(pamP);
-    MALLOCARRAY(pngRow, pamP->width * 4);
-
-    if (pngRow == NULL)
-        pm_error("Unable to allocate space for PNG pixel row.");
-    else {
-        unsigned int row;
-        for (row = 0; row < pamP->height; ++row) {
-            pnm_readpamrow(pamP, tupleRow);
-            
-            convertPamToPng(pamP, tupleRow, pngRow);
-            
-            png_write_row(pngP, pngRow);
-        }
-        free(pngRow);
-    }
-    pnm_freepamrow(tupleRow);
-}
-
-
-
-static void
-pngErrorHandler(png_struct * const pngP,
-                const char * const message) {
-
-    pm_error("Error generating PNG image.  libpng says: %s", message);
-}
-
-
-
-static void
-writePng(const struct pam * const pamP,
-         FILE *             const ofP) {
-
-    png_struct * pngP;
-    png_info * infoP;
-
-    pngP = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
-    if (!pngP)
-        pm_error("Could not allocate png struct.");
-
-    png_set_error_fn(pngP, NULL, &pngErrorHandler, NULL);
-
-    infoP = png_create_info_struct(pngP);
-    if (!infoP)
-        pm_error("Could not allocate PNG info structure");
-    else {
-        infoP->width      = pamP->width;
-        infoP->height     = pamP->height;
-        infoP->bit_depth  = 8;
-        infoP->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
-        
-        png_init_io(pngP, ofP);
-
-        png_write_info(pngP, infoP);
-        
-        writeRaster(pamP, pngP);
-
-        png_write_end(pngP, infoP);
-        
-        png_destroy_write_struct(&pngP, &infoP);
-    }
-}
-    
-
-
-int
-main(int argc, char * argv[]) {
-
-    FILE * ifP;
-    struct cmdlineInfo cmdline;
-    struct pam pam;
-
-    pnm_init(&argc, argv);
-
-    processCommandLine(argc, argv, &cmdline);
-
-    ifP = pm_openr(cmdline.inputFileName);
-
-    pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
-    
-    if (pam.depth < 4)
-        pm_error("PAM must have depth at least 4 (red, green, blue, alpha).  "
-                 "This one has depth %u", pam.depth);
-        
-    if (pam.maxval != 255)
-        pm_error("PAM must have maxval 255.  This one has %lu", pam.maxval);
-
-    writePng(&pam, stdout);
-
-    pm_close(ifP);
-
-    return 0;
-}
diff --git a/converter/other/pamtoavs.c b/converter/other/pamtoavs.c
new file mode 100644
index 00000000..4764c9e8
--- /dev/null
+++ b/converter/other/pamtoavs.c
@@ -0,0 +1,150 @@
+/* ----------------------------------------------------------------------
+ *
+ * Convert a PAM image to an AVS X image
+ *
+ * By Scott Pakin <scott+pbm@pakin.org>
+ *
+ * ----------------------------------------------------------------------
+ *
+ * Copyright (C) 2010 Scott Pakin <scott+pbm@pakin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+#include "pm.h"
+#include "pam.h"
+
+
+
+static char
+sample2char(sample const s,
+            sample const maxval) {
+/* Scale down a sample to a single byte. */
+
+    return maxval==255 ? s : s * 255 / maxval;
+}
+
+
+#define THIS_SAMPLE_CHAR(PLANE) \
+  sample2char(tuplerow[col][PLANE], pamP->maxval)
+
+static void
+produceAvs(struct pam * const pamP,
+           FILE *       const avsFileP) {
+
+    tuple * tuplerow;
+
+    /* Write the AVS header (image width and height as 4-byte
+       big-endian integers).
+    */
+    pm_writebiglong(avsFileP, pamP->width);
+    pm_writebiglong(avsFileP, pamP->height);
+
+    /* Write the AVS data (alpha, red, green, blue -- one byte apiece. */
+    tuplerow = pnm_allocpamrow(pamP);
+    switch (pamP->depth) {
+    case 1: {
+        /* Black-and-white or grayscale, no alpha */
+        unsigned int row;
+        for (row = 0; row < pamP->height; ++row) {
+            unsigned int col;
+            pnm_readpamrow(pamP, tuplerow);
+            for (col = 0; col < pamP->width; ++col) {
+                pm_writechar(avsFileP, (char)255);
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+            }
+        }
+    } break;
+
+    case 2: {
+        /* Black-and-white or grayscale plus alpha */
+        unsigned int row;
+        for (row = 0; row < pamP->height; ++row) {
+            unsigned int col;
+            pnm_readpamrow(pamP, tuplerow);
+            for (col = 0; col < pamP->width; ++col) {
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(1));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+            }
+        }
+    } break;
+
+    case 3: {
+        /* RGB, no alpha */
+        unsigned int row;
+        for (row = 0; row < pamP->height; ++row) {
+            unsigned int col;
+            pnm_readpamrow(pamP, tuplerow);
+            for (col = 0; col < pamP->width; ++col) {
+                pm_writechar(avsFileP, (char)255);
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(1));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(2));
+            }
+        }
+    } break;
+
+    case 4: {
+        /* RGB plus alpha */
+        unsigned int row;
+        for (row = 0; row < pamP->height; ++row) {
+            unsigned int col;
+            pnm_readpamrow( pamP, tuplerow );
+            for (col = 0; col < pamP->width; ++col) {
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(3));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(0));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(1));
+                pm_writechar(avsFileP, THIS_SAMPLE_CHAR(2));
+            }
+        }
+    } break;
+
+    default:
+        pm_error("Unrecognized PAM depth %u.  We understand only "
+                 "1, 2, 3, and 4", pamP->depth);
+        break;
+    }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+    struct pam   inPam;
+    const char * inputFilename;
+    FILE       * inFileP;
+
+    pm_proginit(&argc, argv);
+
+    inputFilename = (argc > 1) ? argv[1] : "-";
+
+    inFileP = pm_openr(inputFilename);
+
+    pnm_readpaminit(inFileP, &inPam, PAM_STRUCT_SIZE(tuple_type));
+
+    produceAvs(&inPam, stdout);
+
+    pm_closer(inFileP);
+
+    return 0;
+}
+
diff --git a/converter/other/pamtodjvurle.c b/converter/other/pamtodjvurle.c
index 2d26eeb0..cae9e026 100644
--- a/converter/other/pamtodjvurle.c
+++ b/converter/other/pamtodjvurle.c
@@ -45,7 +45,7 @@ parseCommandLine(int argc,
    was passed to us as the argv array.  We also trash *argv.
 --------------------------------------------------------------------------*/
     optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-    /* Instructions to optParseOptions3 on how to parse our options. */
+    /* Instructions to pm_optParseOptions3 on how to parse our options. */
     optStruct3 opt;
   
     unsigned int option_def_index;
@@ -61,7 +61,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 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
     /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (!transparentSpec)
diff --git a/converter/other/pamtofits.c b/converter/other/pamtofits.c
index 7a1c70de..92e29c7e 100644
--- a/converter/other/pamtofits.c
+++ b/converter/other/pamtofits.c
@@ -59,7 +59,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 --------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options. */
+        /* Instructions to pm_optParseOptions3 on how to parse our options. */
     optStruct3 opt;
 
     unsigned int minSpec;
@@ -79,7 +79,7 @@ parseCommandLine(int argc, char ** argv,
 
     /* Set some defaults the lazy way (using multiple setting of variables) */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
     /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!minSpec)
@@ -111,11 +111,11 @@ writeHeaderCard(const char * const s) {
 -----------------------------------------------------------------------------*/
     const char * card;
 
-    asprintfN(&card, "%-80.80s", s);
+    pm_asprintf(&card, "%-80.80s", s);
 
     fwrite(card, sizeof(card[0]), 80, stdout);
 
-    strfree(card);
+    pm_strfree(card);
 }
 
 
diff --git a/converter/other/pamtogif.c b/converter/other/pamtogif.c
index 0c8c0f9e..aabf7fc2 100644
--- a/converter/other/pamtogif.c
+++ b/converter/other/pamtogif.c
@@ -35,7 +35,7 @@ typedef int stringCode;
        changes throughout the image.
 
        A variable of this type sometimes has the value -1 instead of
-       a string code due to cheesy programming.
+       a string code because of cheesy programming.
 
        Ergo, this data structure must be signed and at least BITS bits
        wide plus sign bit.
@@ -163,7 +163,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 == 0) 
@@ -220,7 +220,7 @@ closestColor(tuple         const color,
     
     unsigned int i;
     unsigned int imin, dmin;
-    bool fits;
+    int fits;
 
     dmin = UINT_MAX;
     imin = 0;
@@ -627,11 +627,11 @@ 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
+  Add a byte to the end of the current data block, and if it is now 255
   characters, flush the data block to the output file.
 -----------------------------------------------------------------------------*/
     byteBufferP->buffer[byteBufferP->count++] = c;
-    if (byteBufferP->count >= 254)
+    if (byteBufferP->count >= 255)
         byteBuffer_flush(byteBufferP);
 }
 
@@ -893,7 +893,7 @@ lzw_create(FILE *       const ofP,
     
        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.
+       Because of the extra 20% the table itself never fills up.
        
        lzw.hsize and lzw.hshift stay constant through the image.
 
@@ -1544,7 +1544,7 @@ computeTransparent(char          const colorarg[],
         const char * colorspec;
         bool exact;
         tuple transcolor;
-        bool found;
+        int found;
         int colorindex;
         
         if (colorarg[0] == '=') {
diff --git a/converter/other/pamtohdiff.c b/converter/other/pamtohdiff.c
index 2d5f6a61..8d785f5b 100644
--- a/converter/other/pamtohdiff.c
+++ b/converter/other/pamtohdiff.c
@@ -38,7 +38,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -51,7 +51,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 < 1)
diff --git a/converter/other/pamtohtmltbl.c b/converter/other/pamtohtmltbl.c
index 5335ff9f..d1482073 100644
--- a/converter/other/pamtohtmltbl.c
+++ b/converter/other/pamtohtmltbl.c
@@ -33,7 +33,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -53,7 +53,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
diff --git a/converter/other/pamtompfont.c b/converter/other/pamtompfont.c
index ba170fef..92f8de29 100644
--- a/converter/other/pamtompfont.c
+++ b/converter/other/pamtompfont.c
@@ -42,7 +42,7 @@ parseCommandLine(int argc, char ** argv,
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def;
+    optEntry * option_def;
         /* Instructions to OptParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
@@ -59,7 +59,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 == 0) 
@@ -69,6 +69,8 @@ parseCommandLine(int argc, char ** argv,
                  "specified %d", argc-1);
     else
         cmdlineP->inputFilename = argv[1];
+
+    free(option_def);
 }
 
 
diff --git a/converter/other/pamtooctaveimg.c b/converter/other/pamtooctaveimg.c
index b090281d..28bc4cd4 100644
--- a/converter/other/pamtooctaveimg.c
+++ b/converter/other/pamtooctaveimg.c
@@ -75,13 +75,13 @@ findOrAddColor(tuple          const color,
   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 found;
     int colorIndex;
 
     pnm_lookuptuple(&cmapP->pam, cmapP->hash, color, &found, &colorIndex);
 
     if (!found) {
-        bool fits;
+        int fits;
         unsigned int plane;
 
         colorIndex = cmapP->nColors++;
diff --git a/converter/other/pamtopam.c b/converter/other/pamtopam.c
index cae54060..9cb82f7a 100644
--- a/converter/other/pamtopam.c
+++ b/converter/other/pamtopam.c
@@ -17,7 +17,7 @@
 int
 main(int argc, const char * argv[]) {
 
-    bool       eof;     /* no more images in input stream */
+    int        eof;     /* no more images in input stream */
     struct pam inpam;   /* Input PAM image */
     struct pam outpam;  /* Output PAM image */
 
diff --git a/converter/other/pamtopdbimg.c b/converter/other/pamtopdbimg.c
new file mode 100644
index 00000000..6454292e
--- /dev/null
+++ b/converter/other/pamtopdbimg.c
@@ -0,0 +1,810 @@
+/*=============================================================================
+                               pamtopdbimg
+===============================================================================
+
+  Convert Netpbm image to Palm Pilot PDB Image format (for viewing by
+  Pilot Image Viewer).
+
+  Bryan Henderson derived this from Eric Howe's programs named
+  'pgmtoimgv' and 'pbmtoimgv' in September 2010.
+=============================================================================*/
+/*
+ * Copyright (C) 1997 Eric A. Howe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *   Authors:  Eric A. Howe (mu@trends.net)
+ *             Bryan Henderson, September 2010.
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+
+#include "ipdb.h"
+
+enum CompMode {COMPRESSED, MAYBE, UNCOMPRESSED};
+
+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 * title;
+    const char * notefile;  /* NULL if not specified */
+    enum CompMode compMode;
+    unsigned int depth4;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int titleSpec, notefileSpec;
+    unsigned int compressed, maybeCompressed, uncompressed;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "title",               OPT_STRING,    &cmdlineP->title,
+            &titleSpec,               0);
+    OPTENT3(0, "notefile",            OPT_STRING,    &cmdlineP->notefile,
+            &notefileSpec,            0);
+    OPTENT3(0, "compressed",          OPT_FLAG,      NULL,
+            &compressed,              0);
+    OPTENT3(0, "maybecompressed",     OPT_FLAG,      NULL,
+            &maybeCompressed,         0);
+    OPTENT3(0, "uncompressed",        OPT_FLAG,      NULL,
+            &uncompressed,            0);
+    OPTENT3(0, "4depth",              OPT_FLAG,      NULL,
+            &cmdlineP->depth4,        0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = false;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = false;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+
+    if (!titleSpec)
+        cmdlineP->title = "unnamed";
+
+    if (!notefileSpec)
+        cmdlineP->notefile = NULL;
+    
+    if (compressed + uncompressed + maybeCompressed > 1)
+        pm_error("You may specify only one of -compressed, -uncompressed, "
+                 "-maybecompressed");
+    if (compressed)
+        cmdlineP->compMode = COMPRESSED;
+    else if (uncompressed)
+        cmdlineP->compMode = UNCOMPRESSED;
+    else if (maybeCompressed)
+        cmdlineP->compMode = MAYBE;
+    else
+        cmdlineP->compMode = MAYBE;
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFileName = argv[1];
+    else
+        pm_error("Program takes at most one argument:  input file name");
+}
+
+
+
+/*
+ * Pixel setting macros.
+ */
+#define setg16pixel(b,v,o)  ((b) |= ((v) << (4 - 4*(o))))
+#define setgpixel(b,v,o)    ((b) |= ((v) << (6 - 2*(o))))
+#define setmpixelblack(b,o)    ((b) |= (1 << (7 - (o))))
+
+
+
+static int
+pdbheadWrite(PDBHEAD * const pdbheadP,
+             FILE *    const fileP) {
+
+    fwrite(pdbheadP->name, 1, 32, fileP);
+    pm_writebigshort(fileP, pdbheadP->flags);
+    pm_writebigshort(fileP, pdbheadP->version);
+    pm_writebiglong(fileP, pdbheadP->ctime);
+    pm_writebiglong(fileP, pdbheadP->mtime);
+    pm_writebiglong(fileP, pdbheadP->btime);
+    pm_writebiglong(fileP, pdbheadP->mod_num);
+    pm_writebiglong(fileP, pdbheadP->app_info);
+    pm_writebiglong(fileP, pdbheadP->sort_info);
+    fwrite(pdbheadP->type, 1, 4,  fileP);
+    fwrite(pdbheadP->id,   1, 4,  fileP);
+    pm_writebiglong(fileP, pdbheadP->uniq_seed);
+    pm_writebiglong(fileP, pdbheadP->next_rec);
+    pm_writebigshort(fileP, pdbheadP->num_recs);
+
+    return 0;
+}
+
+
+
+static int
+rechdrWrite(RECHDR * const rechdrP,
+            FILE *   const fileP) {
+
+    if (rechdrP) {
+        pm_writebiglong(fileP, rechdrP->offset);
+        fwrite(rechdrP->unknown,   1, 3, fileP);
+        fwrite(&rechdrP->rec_type, 1, 1, fileP);
+
+        if (rechdrP->n_extra != 0)
+            fwrite(rechdrP->extra, 1, rechdrP->n_extra, fileP);
+    }
+    return 0;
+}
+
+
+
+static void
+imageWriteHeader(IMAGE * const imgP,
+                 FILE *  const fileP) {
+
+    fwrite(imgP->name,       1, 32, fileP);
+    fwrite(&imgP->version,   1,  1, fileP);
+    fwrite(&imgP->type,      1,  1, fileP);
+    fwrite(imgP->reserved1,  1,  4, fileP);
+    fwrite(imgP->note,       1,  4, fileP);
+    pm_writebigshort(fileP, imgP->x_last);
+    pm_writebigshort(fileP, imgP->y_last);
+    fwrite(imgP->reserved2,  1,  4, fileP);
+    pm_writebigshort(fileP, imgP->x_anchor);
+    pm_writebigshort(fileP, imgP->y_anchor);
+    pm_writebigshort(fileP, imgP->width);
+    pm_writebigshort(fileP, imgP->height);
+}
+
+
+
+static void
+imageWriteData(IMAGE *         const imgP,
+               const uint8_t * const data,
+               size_t          const dataSize,
+               FILE *          const fileP) {
+
+    fwrite(data, 1,  dataSize, fileP);
+}
+
+
+
+static void
+imageWrite(IMAGE *   const imgP,
+           uint8_t * const data,
+           size_t    const dataSize,
+           FILE *    const fileP) {
+
+    imageWriteHeader(imgP, fileP);
+
+    imageWriteData(imgP, data, dataSize, fileP);
+}
+
+
+
+static int
+textWrite(TEXT * const textP,
+          FILE * const fileP) {
+    
+    if (textP)
+        fwrite(textP->data, 1, strlen(textP->data), fileP);
+
+    return 0;
+}
+
+
+
+typedef struct {
+    unsigned int match;
+    uint8_t      buf[128];
+    int          mode;
+    size_t       len;
+    size_t       used;
+    uint8_t *    p;
+} RLE;
+#define MODE_MATCH  0
+#define MODE_LIT    1
+#define MODE_NONE   2
+
+#define reset(r) {                              \
+        (r)->match = 0xffff;                    \
+        (r)->mode  = MODE_NONE;                 \
+        (r)->len   = 0;                         \
+    }
+
+
+
+static void
+putMatch(RLE *  const rleP,
+          size_t const n) {
+
+    *rleP->p++ = 0x80 + n - 1;
+    *rleP->p++ = rleP->match;
+    rleP->used += 2;
+    reset(rleP);
+}
+
+
+
+static void
+putLit(RLE *  const rleP,
+       size_t const n) {
+
+    *rleP->p++ = n - 1;
+    rleP->p = (uint8_t *)memcpy(rleP->p, rleP->buf, n) + n;
+    rleP->used += n + 1;
+    reset(rleP);
+}
+
+
+
+static size_t
+compress(const uint8_t * const inData,
+         size_t          const n_in,
+         uint8_t *       const out) {
+
+    static void (*put[])(RLE *, size_t) = {putMatch, putLit};
+    RLE rle;
+    size_t  i;
+    const uint8_t * p;
+
+    MEMSZERO(&rle);
+    rle.p = out;
+    reset(&rle);
+
+    for (i = 0, p = &inData[0]; i < n_in; ++i, ++p) {
+        if (*p == rle.match) {
+            if (rle.mode == MODE_LIT && rle.len > 1) {
+                putLit(&rle, rle.len - 1);
+                ++rle.len;
+                rle.match = *p;
+            }
+            rle.mode = MODE_MATCH;
+            ++rle.len;
+        } else {
+            if (rle.mode == MODE_MATCH)
+                putMatch(&rle, rle.len);
+            rle.mode         = MODE_LIT;
+            rle.match        = *p;
+            rle.buf[rle.len++] = *p;
+        }
+        if (rle.len == 128)
+            put[rle.mode](&rle, rle.len);
+    }
+    if (rle.len != 0)
+        put[rle.mode](&rle, rle.len);
+
+    return rle.used;
+}
+
+
+
+static void
+compressIfRequired(IPDB *     const pdbP,
+                   int        const comp,
+                   uint8_t ** const compressedDataP,
+                   size_t *   const compressedSizeP) {
+
+    if (comp == IPDB_NOCOMPRESS) {
+        *compressedDataP = pdbP->i->data;
+        *compressedSizeP = ipdb_img_size(pdbP->i);
+    } else {
+        int const uncompressedSz = ipdb_img_size(pdbP->i);
+        
+        /* Allocate for the worst case. */
+        size_t const allocSz = (3 * uncompressedSz + 2)/2;
+
+        uint8_t * data;
+            
+        data = pdbP->i->data;
+
+        MALLOCARRAY(data, allocSz);
+            
+        if (data == NULL)
+            pm_error("Could not get %lu bytes of memory to decompress",
+                     (unsigned long)allocSz);
+        else {
+            size_t compressedSz;
+            compressedSz = compress(pdbP->i->data, uncompressedSz, data);
+            if (comp == IPDB_COMPMAYBE && compressedSz >= uncompressedSz) {
+                /* Return the uncompressed data */
+                free(data);
+                *compressedDataP = pdbP->i->data;
+                *compressedSizeP = uncompressedSz;
+            } else {
+                pdbP->i->compressed = TRUE;
+                if (pdbP->i->type == IMG_GRAY16)
+                    pdbP->i->version = 9;
+                else
+                    pdbP->i->version = 1;
+                if (pdbP->t != NULL)
+                    pdbP->t->r->offset -= uncompressedSz - compressedSz;
+                *compressedDataP = data;
+                *compressedSizeP = compressedSz;
+            }
+        }
+    }
+}
+
+
+
+static void
+ipdbWrite(IPDB * const pdbP,
+          int    const comp,
+          FILE * const fileP) {
+
+    RECHDR * const trP = pdbP->t == NULL ? NULL : pdbP->t->r;
+    RECHDR * const irP = pdbP->i->r;
+
+    int rc;
+    uint8_t * compressedData;
+        /* This is the image raster, compressed as required.
+           (I.e. if it doesn't have to be compressed, it isn't).
+        */
+    size_t compressedSize;
+
+    assert(pdbP->i);
+
+    compressIfRequired(pdbP, comp, &compressedData, &compressedSize);
+
+    rc = pdbheadWrite(pdbP->p, fileP);
+    if (rc != 0)
+        pm_error("Failed to write PDB header.  %s", ipdb_err(rc));
+            
+    rc = rechdrWrite(irP, fileP);
+    if (rc != 0)
+        pm_error("Failed to write image record header.  %s", ipdb_err(rc));
+
+    rc = rechdrWrite(trP, fileP);
+    if (rc != 0)
+        pm_error("Failed to write text record header.  %s", ipdb_err(rc));
+
+    imageWrite(pdbP->i, compressedData, compressedSize, fileP);
+
+    rc = textWrite(pdbP->t, fileP);
+    if (rc != 0)
+        pm_error("Failed to write text.  %s", ipdb_err(rc));
+
+    /* Oh, gross.  compressIfRequired() might have returned a pointer to
+       storage that was already allocated, or it might have returned a
+       pointer to newly malloc'ed storage.  In the latter case, we have
+       to free the storage.
+    */
+    if (compressedData != pdbP->i->data)
+        free(compressedData);
+}
+
+
+
+static void
+g16pack(tuple *         const tupleRow,
+        struct pam *    const pamP,
+        uint8_t *       const outData,
+        unsigned int    const paddedWidth) {
+/*----------------------------------------------------------------------------
+   Pack a row of 16-level graysacle pixels 'tupleRow', described by *pamP into
+   'outData', padding it to 'paddedWidth' with white.
+
+   We pack 2 input pixels into one output byte.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+    unsigned int off;
+    uint8_t * seg;
+
+    for (col = 0, off = 0, seg = &outData[0]; col < paddedWidth; ++col) {
+        if (col < pamP->width)
+            setg16pixel(*seg, 15 - tupleRow[col][0] * 15 / pamP->maxval, off);
+        else
+            /* Pad on the right with white */
+            setgpixel(*seg, 0, off);
+
+        if (++off == 2) {
+            ++seg;
+            off = 0;
+        }
+    }
+}
+
+
+
+static void
+gpack(tuple *         const tupleRow,
+      struct pam *    const pamP,
+      uint8_t *       const outData,
+      unsigned int    const paddedWidth) {
+/*----------------------------------------------------------------------------
+   Pack a row of 4-level graysacle pixels 'tupleRow', described by *pamP into
+   'outData', padding it to 'paddedWidth' with white.
+
+   We pack 4 input pixels into one output byte.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+    unsigned int off;
+    uint8_t * seg;
+
+    for (col = 0, off = 0, seg = &outData[0]; col < paddedWidth; ++col) {
+        if (col < pamP->width)
+            setgpixel(*seg, 3 - tupleRow[col][0] * 3 / pamP->maxval, off);
+        else
+            /* Pad on the right with white */
+            setgpixel(*seg, 0, off);
+
+        if (++off == 4) {
+            ++seg;
+            off = 0;
+        }
+    }
+}
+
+
+
+static void
+mpack(tuple *         const tupleRow,
+      struct pam *    const pamP,
+      uint8_t *       const outData,
+      unsigned int    const paddedWidth) {
+/*----------------------------------------------------------------------------
+   Pack a row of monochrome pixels 'tupleRow', described by *pamP into
+   'outData', padding it to 'paddedWidth' with white.
+
+   We pack 8 input pixels into one output byte.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+    unsigned int off;
+    uint8_t * seg;
+
+    assert(paddedWidth % 8 == 0);
+
+    /* Initialize row to white, then set necessary pixels black */
+    memset(outData, 0, paddedWidth/8);
+
+    for (col = 0, off = 0, seg = &outData[0]; col < paddedWidth; ++col) {
+        if (col < pamP->width && tupleRow[col][0] == PAM_BLACK)
+            setmpixelblack(*seg, off);
+        if (++off == 8) {
+            ++seg;
+            off = 0;
+        }
+    }
+}
+
+
+
+static int
+adjustDimensions(unsigned int   const w,
+                 unsigned int   const h,
+                 unsigned int * const awP,
+                 unsigned int * const ahP) {
+
+    unsigned int provW, provH;
+
+    provW = w;
+    provH = h;
+    if (provW % 16 != 0)
+        provW += 16 - (provW % 16);
+    if (provW < 160)
+        provW = 160;
+    if (provH < 160)
+        provH = 160;
+
+    *awP = provW;
+    *ahP = provH;
+
+    return w == provW && h == provH;
+}
+
+
+
+/*
+ * You can allocate only 64k chunks of memory on the pilot and that
+ * supplies an image size limit.
+ */
+#define MAX_SIZE(t) ((1 << 16)*((t) == IMG_GRAY ? 4 : 8))
+
+static void
+imageInsertInit(IPDB * const pdbP,
+                int    const uw,
+                int    const uh,
+                int    const type) {
+
+    char * const name = pdbP->p->name;
+    unsigned int adjustedWidth, adjustedHeight;
+
+    if (pdbP->p->num_recs != 0)
+        pm_error("Image record already present, logic error.");
+    else {
+        adjustDimensions(uw, uh, &adjustedWidth, &adjustedHeight);
+        pm_message("Output dimensions: %uw x %uh",
+                   adjustedWidth, adjustedHeight);
+        if (adjustedWidth * adjustedHeight > MAX_SIZE(type))
+            pm_error("Image too large.   Maximum number of pixels allowed "
+                     "for a %s image is %u",
+                     ipdb_typeName(type), MAX_SIZE(type));
+        else {
+            pdbP->i =
+                ipdb_image_alloc(name, type, adjustedWidth, adjustedHeight);
+            if (pdbP->i == NULL)
+                pm_message("Could not get memory for %u x %u image",
+                           adjustedWidth, adjustedHeight);
+            else
+                pdbP->p->num_recs = 1;
+        }
+    }
+}
+
+
+
+static void
+insertG16image(IPDB *          const pdbP,
+               struct pam *    const pamP,
+               tuple **        const tuples) {
+/*----------------------------------------------------------------------------
+   Insert into the PDB an image in 16-level grayscale format.
+
+   The pixels of the image to insert are 'tuples', described by *pamP.
+   Note that the image inserted may be padded up to larger dimensions.
+-----------------------------------------------------------------------------*/
+    imageInsertInit(pdbP, pamP->width, pamP->height, IMG_GRAY16);
+    {
+        int const rowSize = ipdb_width(pdbP)/2;
+            /* The size in bytes of a packed, padded row */
+
+        uint8_t * outP;
+        unsigned int row;
+
+        for (row = 0, outP = &pdbP->i->data[0];
+             row < pamP->height;
+             ++row, outP += rowSize)
+            g16pack(tuples[row], pamP, outP, ipdb_width(pdbP));
+
+        /* Pad with white on the bottom */
+        for (; row < ipdb_height(pdbP); ++row)
+            memset(outP, 0, rowSize);
+    } 
+}
+
+
+
+static void
+insertGimage(IPDB *          const pdbP,
+             struct pam *    const pamP,
+             tuple **        const tuples) {
+/*----------------------------------------------------------------------------
+   Insert into the PDB an image in 4-level grayscale format.
+
+   The pixels of the image to insert are 'tuples', described by *pamP.
+   Note that the image inserted may be padded up to larger dimensions.
+-----------------------------------------------------------------------------*/
+    imageInsertInit(pdbP, pamP->width, pamP->height, IMG_GRAY);
+    {
+        int const rowSize = ipdb_width(pdbP)/4;
+            /* The size in bytes of a packed, padded row */
+
+        uint8_t * outP;
+        unsigned int row;
+
+        for (row = 0, outP = &pdbP->i->data[0];
+             row < pamP->height;
+             ++row, outP += rowSize)
+            gpack(tuples[row], pamP, outP, ipdb_width(pdbP));
+
+        /* Pad with white on the bottom */
+        for (; row < ipdb_height(pdbP); ++row)
+            memset(outP, 0, rowSize);
+    } 
+}
+
+
+
+static void
+insertMimage(IPDB *          const pdbP,
+             struct pam *    const pamP,
+             tuple **        const tuples) {
+/*----------------------------------------------------------------------------
+   Insert into the PDB an image in monochrome format.
+
+   The pixels of the image to insert are 'tuples', described by *pamP.
+   Note that the image inserted may be padded up to larger dimensions.
+-----------------------------------------------------------------------------*/
+    imageInsertInit(pdbP, pamP->width, pamP->height, IMG_MONO);
+    {
+        int const rowSize = ipdb_width(pdbP)/8;
+            /* The size in bytes of a packed, padded row */
+
+        uint8_t * outP;
+        unsigned int row;
+
+        for (row = 0, outP = &pdbP->i->data[0];
+             row < pamP->height;
+             ++row, outP += rowSize)
+            mpack(tuples[row], pamP, outP, ipdb_width(pdbP));
+
+        /* Pad with white on the bottom */
+        for (; row < ipdb_height(pdbP); ++row)
+            memset(outP, 0, rowSize);
+    } 
+}
+
+
+
+static int
+insertText(IPDB *       const pdbP,
+           const char * const s) {
+
+    int retval;
+
+    if (pdbP->i == NULL)
+        retval = E_IMAGENOTTHERE;
+    else if (pdbP->p->num_recs == 2)
+        retval = E_TEXTTHERE;
+    else {
+        pdbP->t = ipdb_text_alloc(s);
+        if (pdbP->t == NULL)
+            retval = ENOMEM;
+        else {
+            pdbP->p->num_recs = 2;
+
+            pdbP->i->r->offset += 8;
+            pdbP->t->r->offset =
+                pdbP->i->r->offset + IMAGESIZE + ipdb_img_size(pdbP->i);
+
+            retval = 0;
+        }
+    }
+    return retval;
+}
+
+
+
+static void
+readimg(IPDB * const pdbP,
+        FILE * const ifP,
+        bool   const depth4) {
+
+     struct pam inpam;
+    tuple ** tuples;
+
+    tuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    if (strneq(inpam.tuple_type, "RGB", 3))
+        pm_error("Input image is color.  Cannot make a Palm color image.");
+
+    if (inpam.maxval == 1)
+        insertMimage(pdbP, &inpam, tuples);
+    else if (depth4)
+        insertG16image(pdbP, &inpam, tuples);
+    else
+        insertGimage(pdbP, &inpam, tuples);
+
+    pnm_freepamarray(tuples, &inpam);
+}
+
+
+
+static void
+readtxt(IPDB *       const pdbP,
+        const char * const noteFileName) {
+
+    struct stat st;
+    char * fileContent;
+    FILE * fP;
+    int n;
+    int rc;
+    size_t bytesRead;
+
+    rc = stat(noteFileName, &st);
+
+    if (rc != 0)
+        pm_error("stat of '%s' failed, errno = %d (%s)",
+                 noteFileName, errno, strerror(errno));
+
+    fP = pm_openr(noteFileName);
+
+    MALLOCARRAY(fileContent, st.st_size + 1);
+
+    if (fileContent == NULL)
+        pm_error("Couldn't get %lu bytes of storage to read in note file",
+                 (unsigned long) st.st_size);
+
+    bytesRead = fread(fileContent, 1, st.st_size, fP);
+
+    if (bytesRead != st.st_size)
+        pm_error("Failed to read note file '%s'.  Errno = %d (%s)",
+                 noteFileName, errno, strerror(errno));
+
+    pm_close(fP);
+
+    /* Chop of trailing newlines */
+    for (n = strlen(fileContent) - 1; n >= 0 && fileContent[n] == '\n'; --n)
+        fileContent[n] = '\0';
+
+    insertText(pdbP, fileContent);
+}
+
+
+
+int
+main(int argc, const char **argv) {
+
+    struct cmdlineInfo cmdline;
+    IPDB * pdbP;
+    FILE * ifP;
+    int comp;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    switch (cmdline.compMode) {
+    case COMPRESSED:   comp = IPDB_COMPRESS;   break;
+    case UNCOMPRESSED: comp = IPDB_NOCOMPRESS; break;
+    case MAYBE:        comp = IPDB_COMPMAYBE;  break;
+    }
+
+    pdbP = ipdb_alloc(cmdline.title);
+
+    if (pdbP == NULL)
+        pm_error("Failed to allocate IPDB structure");
+
+    readimg(pdbP, ifP, cmdline.depth4);
+
+    if (cmdline.notefile)
+        readtxt(pdbP, cmdline.notefile);
+
+    ipdbWrite(pdbP, comp, stdout);
+
+    if (comp == IPDB_COMPMAYBE && !ipdb_compressed(pdbP))
+        pm_message("Image too complex to be compressed.");
+
+    ipdb_free(pdbP);
+
+    pm_close(ifP);
+
+    return EXIT_SUCCESS;
+}
diff --git a/converter/other/pamtopfm.c b/converter/other/pamtopfm.c
index 129b8eee..25a8a0af 100644
--- a/converter/other/pamtopfm.c
+++ b/converter/other/pamtopfm.c
@@ -52,7 +52,7 @@ parseCommandLine(int argc,
    was passed to us as the argv array.  We also trash *argv.
 --------------------------------------------------------------------------*/
     optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-    /* Instructions to optParseOptions3 on how to parse our options. */
+    /* Instructions to pm_optParseOptions3 on how to parse our options. */
     optStruct3 opt;
   
     unsigned int option_def_index;
@@ -67,7 +67,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 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
     /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (endianSpec) {
@@ -150,12 +150,12 @@ floatToPfmSample(float       const input,
    Type converter
 -----------------------------------------------------------------------------*/
     if (machineEndianness == pfmEndianness) {
-        *(float *)outputP->bytes = input;
+        MEMSCPY(&outputP->bytes, &input);
     } else {
         unsigned char reversed[sizeof(pfmSample)];
         unsigned int i, j;
 
-        *(float *)reversed = input;
+        MEMSCPY(&reversed, &input);
         
         for (i = 0, j = sizeof(pfmSample)-1; 
              i < sizeof(pfmSample); 
diff --git a/converter/other/pamtopng.c b/converter/other/pamtopng.c
new file mode 100644
index 00000000..fdeb6582
--- /dev/null
+++ b/converter/other/pamtopng.c
@@ -0,0 +1,825 @@
+/*
+** read a PNM/PAM image and produce a Portable Network Graphics (PNG) file
+**
+** derived from pnmtorast.c by Jef Poskanzer and pamrgbatopng.c by Bryan
+** Henderson <bryanh@giraffe-data.com> and probably some other sources
+**
+** Copyright (C) 1995-1998 by Alexander Lehmann <alex@hal.rhein-main.de>
+**                        and Willem van Schaik <willem@schaik.com>
+** Copyright (C) 1999,2001 by Greg Roelofs <newt@pobox.com>
+** Copyright (C) 2015 by 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.
+*/
+
+/*
+  This Netpbm program pamtopng was derived in 2015 from the Netpbm program
+  Pnmtopng. This was a nearly complete rewrite with the following goals:
+
+  - Add ability to create a PNG alpha channel from the alpha channel in a
+    PAM (format P7) file.
+
+  - Simplify the 20 year old pnmtopng code. Because of the many, many features
+    that program implements and its need for backward compatibility, the code
+    had become rather complex.  This program is roughly 1/3 the size of
+    pnmtopng.c that it replaces.
+
+  - In 1995 bandwith was limited and therefore filesize had to be kept
+    small. The original program tried to optimize for that by applying
+    many "clever tricks". Today that isn't an issue anymore, so gone 
+    are filters, palettes, etc. Also, image conversions were removed,
+    because those should be done with other NetPBM tools.
+
+  - Add ability to create iTXt (international language) chunks.
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <png.h>
+/* setjmp.h needs to be included after png.h */
+#include <setjmp.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+#include "pngx.h"
+#include "pngtxt.h"
+
+
+/* global variable */
+static bool verbose;
+
+
+struct CmdlineInfo {
+    const char * inputFileName;
+    unsigned int verbose;
+    unsigned int transparencySpec;
+    const char * transparency;
+    unsigned int chromaSpec;
+    struct pngx_chroma chroma;
+    unsigned int gammaSpec;
+    float gamma;
+    unsigned int srgbintentSpec;
+    pngx_srgbIntent srgbintent;
+    unsigned int textSpec;
+    const char * text;
+    unsigned int ztxtSpec;
+    const char * ztxt;
+    unsigned int itxtSpec;
+    const char * itxt;
+    unsigned int backgroundSpec;
+    const char * background;
+    unsigned int timeSpec;
+    time_t time;
+};
+
+
+
+static void
+parseChromaOpt(const char *         const chromaOpt,
+               struct pngx_chroma * const chromaP) {
+
+    int count;
+    
+    count = sscanf(chromaOpt, "%f %f %f %f %f %f %f %f",
+                   &chromaP->wx, &chromaP->wy,
+                   &chromaP->rx, &chromaP->ry,
+                   &chromaP->gx, &chromaP->gy,
+                   &chromaP->bx, &chromaP->by);
+
+    if (count != 6)
+        pm_error("Invalid syntax for the -rgb option value '%s'.  "
+                 "Should be 6 floating point number: "
+                 "x and y for each of white, red, green, and blue",
+                 chromaOpt);
+}
+
+
+
+static void
+parseSrgbintentOpt(const char *      const srgbintentOpt,
+                   pngx_srgbIntent * const srgbintentP) {
+    
+    if (streq(srgbintentOpt, "perceptual"))
+        *srgbintentP = PNGX_PERCEPTUAL;
+    else if (streq(srgbintentOpt, "relativecolorimetric"))
+        *srgbintentP = PNGX_RELATIVE_COLORIMETRIC;
+    else if (streq(srgbintentOpt, "saturation"))
+        *srgbintentP = PNGX_SATURATION;
+    else if (streq(srgbintentOpt, "absolutecolorimetric"))
+        *srgbintentP = PNGX_ABSOLUTE_COLORIMETRIC;
+    else
+        pm_error("Unrecognized sRGB intent value '%s'.  We understand "
+                 "only 'perceptual', 'relativecolorimetric', "
+                 "'saturation', and 'absolutecolorimetric'",
+                 srgbintentOpt);
+}
+
+
+
+static void
+parseTimeOpt(const char * const timeOpt,
+             time_t *     const timeP) {
+
+    struct tm brokenTime;
+    int year;
+    int month;
+    int count;
+
+    count = sscanf(timeOpt, "%d-%d-%d %d:%d:%d",
+                   &year,
+                   &month,
+                   &brokenTime.tm_mday,
+                   &brokenTime.tm_hour,
+                   &brokenTime.tm_min,
+                   &brokenTime.tm_sec);
+
+    if (count != 6)
+        pm_error("Invalid value for -time '%s'.   It should have "
+                 "the form [yy]yy-mm-dd hh:mm:ss.", timeOpt);
+    
+    if (year < 0)
+        pm_error("Year is negative in -time value '%s'", timeOpt);
+    if (year > 9999)
+        pm_error("Year is more than 4 digits in -time value '%s'",
+                 timeOpt);
+    if (month < 0)
+        pm_error("Month is negative in -time value '%s'", timeOpt);
+    if (month > 12)
+        pm_error("Month is >12 in -time value '%s'", timeOpt);
+    if (brokenTime.tm_mday < 0)
+        pm_error("Day of month is negative in -time value '%s'",
+                 timeOpt);
+    if (brokenTime.tm_mday > 31)
+        pm_error("Day of month is >31 in -time value '%s'", timeOpt);
+    if (brokenTime.tm_hour < 0)
+        pm_error("Hour is negative in -time value '%s'", timeOpt);
+    if (brokenTime.tm_hour > 23)
+        pm_error("Hour is >23 in -time value '%s'", timeOpt);
+    if (brokenTime.tm_min < 0)
+        pm_error("Minute is negative in -time value '%s'", timeOpt);
+    if (brokenTime.tm_min > 59)
+        pm_error("Minute is >59 in -time value '%s'", timeOpt);
+    if (brokenTime.tm_sec < 0)
+        pm_error("Second is negative in -time value '%s'", timeOpt);
+    if (brokenTime.tm_sec > 59)
+        pm_error("Second is >59 in -time value '%s'", timeOpt);
+
+    brokenTime.tm_mon = month - 1;
+    if (year >= 1900)
+        brokenTime.tm_year = year - 1900;
+    else
+        brokenTime.tm_year = year;
+
+    /* Note that mktime() considers brokeTime to be in local time.
+       This is what we want, since we got it from a user.  User should
+       set his local time zone to UTC if he wants absolute time.
+    */
+    *timeP = mktime(&brokenTime);
+}
+
+
+
+static void
+parseCommandLine (int                  argc,
+                  const char **        argv,
+                  struct CmdlineInfo * const cmdlineP) {
+    
+    optEntry * option_def;
+    optStruct3 opt;
+    unsigned int option_def_index = 0;  /* incremented by OPTENT3 */
+
+    const char * srgbintent;
+    const char * chroma;
+    const char * time;
+
+    MALLOCARRAY(option_def, 100);
+
+    OPTENT3(0,  "verbose",      OPT_FLAG,       NULL,
+            &cmdlineP->verbose,        0);
+    OPTENT3(0,  "transparency", OPT_STRING,     &cmdlineP->transparency,
+            &cmdlineP->transparencySpec, 0);
+    OPTENT3(0,  "chroma",       OPT_STRING,     &chroma,
+            &cmdlineP->chromaSpec,     0);
+    OPTENT3(0,  "gamma",        OPT_FLOAT,      &cmdlineP->gamma,
+            &cmdlineP->gammaSpec,      0);
+    OPTENT3(0,  "srgbintent",   OPT_STRING,     &srgbintent,
+            &cmdlineP->srgbintentSpec, 0);
+    OPTENT3(0,  "text",         OPT_STRING,     &cmdlineP->text,
+            &cmdlineP->textSpec,       0);
+    OPTENT3(0,  "ztxt",         OPT_STRING,     &cmdlineP->ztxt,
+            &cmdlineP->ztxtSpec,       0);
+    OPTENT3(0,  "itxt",         OPT_STRING,     &cmdlineP->itxt,
+            &cmdlineP->itxtSpec,       0);
+    OPTENT3(0,  "background",   OPT_STRING,     &cmdlineP->background,
+            &cmdlineP->backgroundSpec, 0);
+    OPTENT3(0,  "time",         OPT_STRING,        &time,
+            &cmdlineP->timeSpec,       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 */
+
+    /* uses and sets argc, argv, and some of *cmdlineP and others */
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+
+    if (cmdlineP->chromaSpec)
+        parseChromaOpt(chroma, &cmdlineP->chroma);
+
+    if (cmdlineP->srgbintentSpec)
+        parseSrgbintentOpt(srgbintent, &cmdlineP->srgbintent);
+
+    if (cmdlineP->timeSpec)
+        parseTimeOpt(time, &cmdlineP->time);
+    
+    /* get the input-file or stdin pipe */
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFileName = argv[1];
+    else
+        pm_error("Program takes at most one argument: input file name.");
+
+    free(option_def);
+}
+
+
+
+static png_byte
+colorTypeFromInputType(const struct pam * const pamP) {
+/*----------------------------------------------------------------------------
+  Analyse the Netpbm image for color-type and bit-depth
+-----------------------------------------------------------------------------*/
+    png_byte retval;
+
+    if (pamP->depth < 1 && pamP->depth > 4)
+        pm_error ("Number of color planes must be between 1 and 4 inclusive");
+
+    if (pamP->maxval != 1 && pamP->maxval != 3 && pamP->maxval != 15 &&
+        pamP->maxval != 255 && pamP->maxval != 65535)
+        pm_error("The maxval of the input image is %u; "
+                 "it must be 1, 3, 15, 255 or 65535", (unsigned)pamP->maxval);
+
+    if (strneq(pamP->tuple_type, "RGB_ALPHA", 9)) {
+        if (pamP->depth == 4)
+            retval = PNG_COLOR_TYPE_RGB_ALPHA;
+        else
+            pm_error("Input tuple type is RGB_ALPHA, "
+                     "but number of planes is %u instead of 4",
+                pamP->depth);
+    } else if (strneq(pamP->tuple_type, "RGB", 3)) {
+        if (pamP->depth == 3)
+            retval = PNG_COLOR_TYPE_RGB;
+        else
+            pm_error("Input tuple type is RGB, "
+                     "but number of planes is %u instead of 3",
+                     pamP->depth);
+    } else if (strneq(pamP->tuple_type, "GRAYSCALE_ALPHA", 15)) {
+        if (pamP->depth == 2)
+            retval = PNG_COLOR_TYPE_GRAY_ALPHA;
+        else
+            pm_error("Input tupel type is GRAYSCALE_ALPHA, "
+                     "but number of planes is %u instread of 2",
+                     pamP->depth);
+    } else if (strneq(pamP->tuple_type, "GRAYSCALE", 9)) {
+        if (pamP->depth == 1)
+            retval = PNG_COLOR_TYPE_GRAY;
+        else
+            pm_error("Input tuple type is GRAYSCALE, "
+                     "but number of planes is %u instead of 1",
+                     pamP->depth);
+    } else if (strneq(pamP->tuple_type, "BLACKANDWHITE", 3)) {
+        if (pamP->depth != 1)
+            pm_error("Input tuple type is BLACKANDWHITE, "
+                     "but number of planes is %u instead of 1",
+                     pamP->depth);
+        if (pamP->maxval != 1)
+            pm_error("Input tuple type is BLACKANDWHITE, "
+                     "but maxval is %u instead of 1", (unsigned)pamP->maxval);
+
+        retval = PNG_COLOR_TYPE_GRAY;
+    } else
+        pm_error("Unrecognized tuple type: '%s'", pamP->tuple_type);
+
+    return retval;
+}
+
+
+
+/*****************************************************************************
+*  Subroutines that create all the (ancillary) chunks
+*****************************************************************************/
+
+
+
+static png_color_16
+parseAndScaleColor(const char * const colorString,
+                   xelval       const pngMaxval) {
+
+    png_color_16 pngColor;
+
+    if (colorString) {
+        xel const inputColor = ppm_parsecolor(colorString, PNM_OVERALLMAXVAL);
+
+        xel scaledColor;
+
+        /* Scale the color down to the PNG bit depth */
+        PPM_DEPTH(scaledColor, inputColor, PNM_OVERALLMAXVAL, pngMaxval);
+
+        pngColor.red   = PPM_GETR(scaledColor);
+        pngColor.green = PPM_GETG(scaledColor);
+        pngColor.blue  = PPM_GETB(scaledColor);
+        pngColor.gray  = PNM_GET1(scaledColor);
+    }
+
+    return pngColor;
+}
+
+
+
+static png_color_8
+sigBitsFmImgType(unsigned int const pnmBitDepth,
+                 int          const pngColorType) {
+/*----------------------------------------------------------------------------
+   A representation used in PNG of color resolutions in an original image.
+-----------------------------------------------------------------------------*/
+    png_color_8 retval;
+
+    /* Initial values */
+    if (pnmBitDepth < 8) {
+        switch (pngColorType) {
+        case PNG_COLOR_TYPE_RGB:
+            retval.red   = pnmBitDepth;
+            retval.green = pnmBitDepth;
+            retval.blue  = pnmBitDepth;
+            retval.gray  = 0;
+            retval.alpha = 0;
+            break;
+        case PNG_COLOR_TYPE_RGB_ALPHA:
+            retval.red   = pnmBitDepth;
+            retval.green = pnmBitDepth;
+            retval.blue  = pnmBitDepth;
+            retval.gray  = 0;
+            retval.alpha = pnmBitDepth;
+            break;
+        case PNG_COLOR_TYPE_GRAY:
+            /* PNG can (so presumably will) use original bit depth */
+            retval.red   = 0;
+            retval.green = 0;
+            retval.blue  = 0;
+            retval.gray  = 0;
+            retval.alpha = 0;
+            break;
+        case PNG_COLOR_TYPE_GRAY_ALPHA:
+            retval.red   = 0;
+            retval.green = 0;
+            retval.blue  = 0;
+            retval.gray  = pnmBitDepth;
+            retval.alpha = pnmBitDepth;
+            break;
+        }
+    } else {
+        /* PNG can (so presumably will) use original bit depth */
+        retval.red   = 0;
+        retval.green = 0;
+        retval.blue  = 0;
+        retval.gray  = 0;
+        retval.alpha = 0;
+    }
+    return retval;
+}
+
+
+
+static void
+doTrnsChunk(const struct pam * const pamP,
+            struct pngx *      const pngxP,
+            const char *       const trans) {
+
+    if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY_ALPHA || 
+        pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB_ALPHA)
+        pm_error("Both alpha channel and transparency chunk not allowed.");
+    else {
+        xelval const pngMaxval = pm_bitstomaxval(pngx_bitDepth(pngxP));
+        png_color_16 const pngColor = parseAndScaleColor(trans, pngMaxval);
+            /* Transparency color from text format scaled from 16-bit to
+               maxval.
+            */
+
+        pngx_setTrnsValue(pngxP, pngColor);
+
+        if (verbose) {
+            if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY) {
+                pm_message("writing tRNS chunk with color {gray} = {%u}",
+                           pngColor.gray );
+            } else if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB) {
+                pm_message("writing tRNS chunk with color "
+                           "{red, green, blue} = {%u, %u, %u}",
+                           pngColor.red, pngColor.green, pngColor.blue);
+            }
+        }
+    }
+}
+
+
+
+static void
+doChrmChunk(struct pngx *      const pngxP,
+            struct pngx_chroma const chroma) {
+    
+    pngx_setChrm(pngxP, chroma);
+
+    if (verbose) {
+        pm_message("writing cHRM chunk { wx, wy, rx, ry, gx, gy, bx, by } = "
+                   "{ %4.2f, %4.2f, %4.2f, %4.2f, "
+                   "%4.2f, %4.2f, %4.2f, %4.2f }", 
+                   chroma.wx, chroma.wy,
+                   chroma.rx, chroma.ry,
+                   chroma.gx, chroma.gy,
+                   chroma.bx, chroma.by);
+    }
+}
+
+
+
+static void 
+doGamaChunk(struct pngx *  const pngxP,
+            float          const gamma) {
+
+    pngx_setGama(pngxP, gamma);
+
+    if (verbose) {
+        pm_message("writing gAMA chunk with image gamma value %4.2f", gamma);
+    }
+}
+
+
+
+static void
+doSbitChunk(const struct pam * const pamP,
+            struct pngx *      const pngxP,
+            png_color_8        const sigBits) {
+
+    if (sigBits.red + sigBits.green + sigBits.blue +
+        sigBits.gray + sigBits.alpha > 0) {
+        pngx_setSbit(pngxP, sigBits);
+    }
+}
+
+
+
+static void
+doSrgbChunk(struct pngx *   const pngxP,
+            pngx_srgbIntent const srgbIntent) {
+
+    pngx_setSrgb(pngxP, srgbIntent);
+
+    if (verbose) {
+        pm_message("writing sRGB chunk with intent value %s",
+                   pngx_srgbIntentDesc(srgbIntent));
+    }
+}
+
+
+
+static void
+doTextChunkSet(struct pngx * const pngxP,
+               const char *  const textFileName) {
+
+    bool const ztxt = true;
+    bool const itxt = false;
+
+    FILE * tfP;
+
+    tfP = pm_openr(textFileName);
+    
+    pngtxt_addChunk(pngxP, tfP, ztxt, itxt, verbose);
+    
+    pm_close(tfP);
+}
+
+
+
+static void
+doZtxtChunkSet(struct pngx * const pngxP,
+               const char *  const textFileName) {
+
+    bool const ztxt = true;
+    bool const itxt = false;
+
+    FILE * tfP;
+
+    tfP = pm_openr(textFileName);
+    
+    pngtxt_addChunk(pngxP, tfP, ztxt, itxt, verbose);
+    
+    pm_close(tfP);
+}
+
+
+
+
+static void
+doItxtChunkSet(struct pngx * const pngxP,
+               const char *  const textFileName) {
+
+    bool const ztxt = true;
+    bool const itxt = true;
+
+    FILE * tfP;
+
+    tfP = pm_openr(textFileName);
+
+    pngtxt_addChunk(pngxP, tfP, ztxt, itxt, verbose);
+}
+
+
+
+static void
+doBkgdChunk (const struct pam * const pamP,
+             struct pngx *      const pngxP,
+             const char *       const colorName)
+{
+    xelval const pngMaxval = pm_bitstomaxval(pngx_bitDepth(pngxP));
+
+    png_color_16 const pngColor = parseAndScaleColor(colorName, pngMaxval);
+        /* Background color from text format, scaled from 16-bit to maxval */
+
+    pngx_setBkgdRgb(pngxP, pngColor);
+
+    if (verbose) {
+        if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY || 
+            pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY_ALPHA) {
+            pm_message("writing bKGD chunk with gray level = %u",
+                       pngColor.gray);
+        } else if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB || 
+                   pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB_ALPHA) {
+            pm_message("writing bKGD chunk with color {red, green, blue} = "
+                       "{%u, %u, %u}",
+                       pngColor.red, pngColor.green, pngColor.blue);
+        }
+    }
+}
+
+
+
+static void
+doTimeChunk(struct pngx * const pngxP,
+            time_t        const time) {
+
+    pngx_setTime(pngxP, time);
+
+    if (verbose) {
+        struct tm * const brokenTimeP = gmtime(&time);
+
+        char buffer[100];
+
+        strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", brokenTimeP);
+
+        pm_message("Writing tIME chunk specifying datetime %s", buffer);
+    }
+}
+
+
+
+static void
+setShift(struct pngx * const pngxP,
+         png_color_8   const sigBits) {
+
+    if (sigBits.red + sigBits.green + sigBits.blue +
+        sigBits.gray + sigBits.alpha > 0) {
+
+        /* Move the 1, 2, 4 bits to most significant bits */
+        pngx_setShift(pngxP, sigBits);
+    }
+}
+
+
+
+static void
+convertRaster(const struct pam * const pamP,
+              const tuple *      const tuplerow,
+              png_byte *         const pngRow,
+              unsigned int       const bitDepth) {
+
+    unsigned int col;
+
+    /* An image row consists of columns x planes like gray or rgb(a) x 8 or 16
+       bits.
+    */
+    for (col = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane) {
+            if (bitDepth > 8) {
+                /* Copy 2 bytes = 16 bits for one pixel */
+                pngRow[2 * (pamP->depth * col + plane)] =
+                    (tuplerow[col][plane] >> 8) & 0xff ;
+                pngRow[2 * (pamP->depth * col + plane) + 1] =
+                    tuplerow[col][plane] & 0xff ;
+            } else {
+                /* Copy 1 byte for one pixel. Later, a packing of 2, 4 or 8
+                   pixels into a single byte can still happen.
+                */
+                pngRow[pamP->depth * col + plane] = tuplerow[col][plane];
+            }
+        }
+    }
+}
+
+
+
+static void
+writeRaster(const struct pam * const pamP,
+            struct pngx *      const pngxP,
+            int                const bitDepth) {
+
+    tuple * tupleRow;
+    png_byte * pngRow;
+    unsigned int row;
+
+    /* We process row-by-row and do not read the complete image into memory */
+
+    tupleRow = pnm_allocpamrow(pamP);
+
+    MALLOCARRAY(pngRow, pamP->width * 8);
+        /* sufficient to store a 16-bit RGB+A row */
+
+    if (pngRow == NULL)
+        pm_error("Unable to allocate space for PNG pixel row for "
+                 "%u columns", pamP->width);
+    else {
+        for (row = 0; row < pamP->height; ++row) {
+            pnm_readpamrow(pamP, tupleRow);
+
+            convertRaster(pamP, tupleRow, pngRow, bitDepth);
+
+            png_write_row(pngxP->png_ptr, pngRow);
+        }
+        free(pngRow);
+    }
+    pnm_freepamrow(tupleRow);
+}
+
+
+
+static void
+writePng(const struct pam * const pamP,
+         FILE *             const ofP,
+         struct CmdlineInfo const cmdline) {
+
+    unsigned int const pnmBitDepth = pm_maxvaltobits(pamP->maxval);
+    int const pngColorType = colorTypeFromInputType(pamP);
+
+    struct pngx * pngxP;
+    unsigned int pngBitDepth;
+    png_color_8 sBit;
+
+    pngx_create(&pngxP, PNGX_WRITE, NULL);
+
+
+
+    if ((pngColorType == PNG_COLOR_TYPE_RGB ||
+         pngColorType == PNG_COLOR_TYPE_RGB_ALPHA) &&
+        pnmBitDepth < 8) {
+
+        pngBitDepth = 8;
+    } else
+        pngBitDepth = pnmBitDepth;
+
+    png_init_io(pngxP->png_ptr, ofP);
+
+    pngx_setIhdr(pngxP, pamP->width, pamP->height,
+                 pngBitDepth, pngColorType,
+                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
+                 PNG_FILTER_TYPE_BASE);
+
+    sBit = sigBitsFmImgType(pnmBitDepth, pngColorType);
+
+    /* Where requested, add ancillary chunks */
+    if (cmdline.transparencySpec)
+        doTrnsChunk(pamP, pngxP,cmdline.transparency);
+
+    if (cmdline.chromaSpec)
+        doChrmChunk(pngxP, cmdline.chroma);
+
+    if (cmdline.gammaSpec)
+        doGamaChunk(pngxP, cmdline.gamma);
+
+    /* no iccp */
+
+    doSbitChunk(pamP, pngxP, sBit);
+
+    if (cmdline.srgbintentSpec)
+        doSrgbChunk(pngxP, cmdline.srgbintent);
+
+    if (cmdline.textSpec)
+        doTextChunkSet(pngxP, cmdline.text);
+
+    if (cmdline.ztxtSpec)
+        doZtxtChunkSet(pngxP, cmdline.ztxt);
+
+    if (cmdline.itxtSpec)
+        doItxtChunkSet(pngxP, cmdline.itxt);
+
+    if (cmdline.backgroundSpec)
+        doBkgdChunk(pamP, pngxP, cmdline.background);
+
+    /* no hist */
+
+    /* no phys */
+
+    /* no splt */
+
+    if (cmdline.timeSpec)
+        doTimeChunk(pngxP, cmdline.time);
+
+    setShift(pngxP, sBit);
+
+    /* Write the ancillary chunks to PNG file */
+    pngx_writeInfo(pngxP);
+
+    if (pngColorType != PNG_COLOR_TYPE_GRAY && pnmBitDepth < 8) {
+        /* Move the 1, 2, 4 bits to most significant bits */
+        pngx_setShift(pngxP, sBit);
+    }
+    if ((pngColorType == PNG_COLOR_TYPE_GRAY) && (pnmBitDepth < 8)) {
+        /* Pack multiple pixels in a byte */
+        pngx_setPacking(pngxP);
+    }
+
+    writeRaster(pamP, pngxP, pnmBitDepth);
+
+    pngx_writeEnd(pngxP);
+    pngx_destroy(pngxP);
+}
+
+
+
+
+static void
+reportInputFormat(const struct pam * const pamP) {
+
+    const char * formatDesc;
+
+    if (pamP->format == PBM_FORMAT || pamP->format == RPBM_FORMAT)
+        formatDesc = "PBM";
+    else if (pamP->format == PGM_FORMAT || pamP->format == RPGM_FORMAT)
+        formatDesc = "PGM";
+    else if (pamP->format == PPM_FORMAT || pamP->format == RPPM_FORMAT)
+        formatDesc = "PPM";
+    else if (pamP->format == PAM_FORMAT)
+        formatDesc = "PAM";
+    else
+        formatDesc = NULL;
+
+    if (formatDesc)
+        pm_message("Input format = %s", formatDesc);
+    else
+        pm_message("Unrecognized input format, format code = 0x%x",
+                   pamP->format);
+
+    pm_message("Input tuple type = '%s'", pamP->tuple_type);
+    pm_message("Input depth = %u", pamP->depth);
+    pm_message("Input maxval = %u", (unsigned int) pamP->maxval);
+}
+
+
+
+int
+main(int           argc,
+     const char ** argv) {
+
+    FILE * ifP;
+    struct CmdlineInfo cmdline;
+    struct pam pam;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
+
+    if (verbose)
+        reportInputFormat(&pam);
+
+    writePng(&pam, stdout, cmdline);
+
+    pm_close(ifP);
+
+    return 0;
+}
+
+
+
diff --git a/converter/other/pamtopnm.c b/converter/other/pamtopnm.c
index 86f6514c..f043d721 100644
--- a/converter/other/pamtopnm.c
+++ b/converter/other/pamtopnm.c
@@ -17,23 +17,23 @@
 #include "shhopt.h"
 #include "mallocvar.h"
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* Filespecs of input files */
-    unsigned int assume;    /* -assume option */
+    const char * inputFileName;  /* Name of input file */
+    unsigned int assume;
 };
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo *cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def;
+    optEntry * option_def;
         /* Instructions to OptParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
@@ -49,16 +49,18 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 == 0) 
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
                  "specified %d", argc-1);
     else
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
+
+    free(option_def);
 }
 
 
@@ -69,11 +71,11 @@ validateTupleType(struct pam const inpam,
 /*----------------------------------------------------------------------------
    Make sure the image has a tuple type we know how to convert to PNM.
 
-   We're quite liberal, trying to accomodate all sorts of future
+   We're quite liberal, trying to accommodate all sorts of future
    twists on the formats.  If the tuple type _starts with_
    BLACKANDWHITE, GRAYSCALE, or RGB, and has at least as many planes
    as we'd need to convert to PBM, PGM, or PPM, respectively, we
-   accept it.  We thus accomodate variations on these formats that add
+   accept it.  We thus accommodate variations on these formats that add
    planes and add to the right end of the tuple type to explain them.
 
    If Callers specified 'assumeTupleType', we're even more liberal.
@@ -105,19 +107,19 @@ validateTupleType(struct pam const inpam,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char **argv) {
 
-    struct cmdlineInfo cmdline;
-    FILE* ifP;
-    bool eof;   /* no more images in input stream */
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    int eof;   /* no more images in input stream */
     struct pam inpam;   /* Input PAM image */
     struct pam outpam;  /* Output PNM image */
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
     eof = FALSE;
     while (!eof) {
diff --git a/converter/other/pamtosrf.c b/converter/other/pamtosrf.c
new file mode 100644
index 00000000..3800d77c
--- /dev/null
+++ b/converter/other/pamtosrf.c
@@ -0,0 +1,217 @@
+/*
+ * Convert a Netpbm image to SRF (Garmin vehicle)
+ *
+ * Copyright (C) 2011 by Mike Frysinger <vapier@gentoo.org>
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "pam.h"
+#include "srf.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;  /* '-' if stdin */
+    unsigned int  verbose;
+};
+
+static bool verbose;
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+  optEntry *option_def;
+      /* Instructions to pm_optParseOptions3 on how to parse our options.
+       */
+  optStruct3 opt;
+
+  unsigned int option_def_index;
+
+  MALLOCARRAY_NOFAIL(option_def, 100);
+
+  option_def_index = 0;   /* incremented by OPTENT3 */
+  OPTENT3(0, "verbose",          OPT_FLAG,      NULL,
+          &cmdlineP->verbose,    0);
+
+  opt.opt_table = option_def;
+  opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+  opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+  pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+      /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+  if (argc-1 < 1)
+    cmdlineP->inputFileName = "-";
+  else if (argc-1 == 1)
+    cmdlineP->inputFileName = argv[1];
+  else
+    pm_error("Program takes at most one argument:  input file name");
+}
+
+
+
+static uint8_t
+srfScale(sample             const unscaled,
+         const struct pam * const pamP) {
+
+    return pnm_scalesample(unscaled, pamP->maxval, 255);
+}
+
+
+
+static uint16_t
+srfColorFromTuple(tuple              const t,
+                  const struct pam * const pamP) {
+
+    unsigned int redPlane, grnPlane, bluPlane;
+
+    if (pamP->depth >= 3) {
+        redPlane = PAM_RED_PLANE;
+        grnPlane = PAM_GRN_PLANE;
+        bluPlane = PAM_BLU_PLANE;
+    } else {
+        redPlane = 0;
+        grnPlane = 0;
+        bluPlane = 0;
+    }
+    return
+        (((srfScale(t[redPlane], pamP) >> 3) & 0x1f) << 11) |
+        (((srfScale(t[grnPlane], pamP) >> 3) & 0x1f) <<  6) |
+        (((srfScale(t[bluPlane], pamP) >> 3) & 0x1f) <<  0);
+}
+
+
+
+static uint8_t
+srfAlphaFromTuple(tuple              const t,
+                  const struct pam * const pamP) {
+
+    uint8_t retval;
+    int haveOpacity;
+    unsigned int opacityPlane;
+
+    pnm_getopacity(pamP, &haveOpacity, &opacityPlane);
+
+    if (haveOpacity) {
+        uint8_t const scaled = srfScale(t[opacityPlane], pamP);
+
+        retval = scaled == 0xff ? SRF_ALPHA_OPAQUE :  128 - (scaled >> 1);
+    } else
+        retval = SRF_ALPHA_OPAQUE;
+
+    return retval;
+}
+
+
+
+static void
+convertRaster(struct pam *     const pamP,
+              struct srf_img * const imgP) {
+
+    tuple * tuplerow;
+    unsigned int row;
+
+    tuplerow = pnm_allocpamrow(pamP);
+
+    for (row = 0; row < pamP->height; ++row) {
+        uint32_t        const off   = row * pamP->width;
+        uint16_t *      const data  = &imgP->data.data[off];
+        unsigned char * const alpha = &imgP->alpha.data[off];
+
+        unsigned int col;
+
+        pnm_readpamrow(pamP, tuplerow);
+
+        for (col = 0; col < pamP->width; ++col) {
+            alpha[col] = srfAlphaFromTuple(tuplerow[col], pamP);
+            data[col]  = srfColorFromTuple(tuplerow[col], pamP);
+        }
+    }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+convertImage(FILE *       const ifP,
+             struct srf * const srfP) {
+
+    struct pam inpam;
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    if (verbose)
+        pm_message("reading %ux%u image", inpam.width, inpam.height);
+
+    srf_create_img(srfP, inpam.width, inpam.height);
+
+    convertRaster(&inpam, &srfP->imgs[srfP->header.img_cnt-1]);
+}
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+  struct cmdlineInfo cmdline;
+  FILE *             ifP;
+  struct srf         srf;
+  int                eof;   /* No more images in input */
+  unsigned int       imageSeq;
+      /* Sequence of current image in input file.  First = 0 */
+
+  pm_proginit(&argc, argv);
+
+  parseCommandLine(argc, argv, &cmdline);
+
+  verbose = cmdline.verbose;
+
+  ifP = pm_openr(cmdline.inputFileName);
+
+  srf_init(&srf);
+
+  eof = FALSE;
+  for (imageSeq = 0; !eof; ++imageSeq) {
+      if (verbose)
+          pm_message("Converting Image %u", imageSeq);
+
+      convertImage(ifP, &srf);
+
+      pnm_nextimage(ifP, &eof);
+  }
+
+  srf_write(stdout, &srf);
+    
+  srf_term(&srf);
+  pm_closer(ifP);
+
+  return 0;
+}
+
+
+
diff --git a/converter/other/pamtosvg/Makefile b/converter/other/pamtosvg/Makefile
index 8b033020..83f150d0 100644
--- a/converter/other/pamtosvg/Makefile
+++ b/converter/other/pamtosvg/Makefile
@@ -7,26 +7,13 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 include $(BUILDDIR)/config.mk
 
-BINARIES = pamtosvg
+PORTBINARIES = pamtosvg
 
-PAMTOSVG_OBJECTS = \
-	pamtosvg.o \
-	output-svg.o \
-	fit.o \
-	spline.o \
-	curve.o \
-	vector.o \
-	epsilon-equal.o \
-	autotrace.o \
-	pxl-outline.o \
-	bitmap.o \
-	thin-image.o \
-	logreport.o \
-	exception.o \
-	image-proc.o \
+BINARIES = $(PORTBINARIES)
+
+MERGEBINARIES = $(BINARIES)
 
-MERGE_OBJECTS = \
-	pamtosvg.o2 \
+ADDL_OBJECTS = \
 	output-svg.o \
 	fit.o \
 	spline.o \
@@ -41,15 +28,12 @@ MERGE_OBJECTS = \
 	exception.o \
 	image-proc.o \
 
-OBJECTS = $(PAMTOSVG_OBJECTS)
+OBJECTS = pamtosvg.o $(ADDL_OBJECTS)
 
-MERGEBINARIES = $(BINARIES)
+MERGE_OBJECTS = pamtosvg.o2 $(ADDL_OBJECTS)
 
 all: $(BINARIES)
 
 include $(SRCDIR)/common.mk
 
-pamtosvg: $(PAMTOSVG_OBJECTS) $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $(PAMTOSVG_OBJECTS) \
-	  $(shell $(LIBOPT) $(NETPBMLIB)) \
-	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
+pamtosvg: $(ADDL_OBJECTS)
diff --git a/converter/other/pamtosvg/bitmap.c b/converter/other/pamtosvg/bitmap.c
index 1a00e748..84a8a8ae 100644
--- a/converter/other/pamtosvg/bitmap.c
+++ b/converter/other/pamtosvg/bitmap.c
@@ -59,8 +59,8 @@ at_bitmap_init(unsigned char * area,
             if (bitmap.bitmap == NULL)
                 pm_error("Unable to allocate %u x %u x %u bitmap array",
                          width, height, planes);
-            bzero(bitmap.bitmap,
-                  width * height * planes * sizeof(unsigned char));
+            memset(bitmap.bitmap,
+                   0, width * height * planes * sizeof(unsigned char));
         }
     }
     
diff --git a/converter/other/pamtosvg/bitmap.h b/converter/other/pamtosvg/bitmap.h
index 7334f138..b979e0c0 100644
--- a/converter/other/pamtosvg/bitmap.h
+++ b/converter/other/pamtosvg/bitmap.h
@@ -36,7 +36,7 @@ at_bitmap_type * at_bitmap_new(unsigned short width,
 			       unsigned int planes);
 at_bitmap_type * at_bitmap_copy(at_bitmap_type * src);
 
-/* We have to export functions that supports internal datum 
+/* We have to export functions that allows internal datum 
    access. Such functions might be useful for 
    at_bitmap_new user. */
 unsigned short at_bitmap_get_width (at_bitmap_type * bitmap);
diff --git a/converter/other/pamtosvg/image-proc.c b/converter/other/pamtosvg/image-proc.c
index 287e6384..d025ee1e 100644
--- a/converter/other/pamtosvg/image-proc.c
+++ b/converter/other/pamtosvg/image-proc.c
@@ -52,7 +52,7 @@ new_distance_map(bitmap_type bitmap, unsigned char target_value, bool padded, at
         MALLOCARRAY(dist.d[y], w);
         if (dist.d[y] == NULL)
             pm_error("Unable to get memory for distance map");
-        bzero(dist.d[y], w * sizeof(float));
+        memset(dist.d[y], 0, w * sizeof(float));
         
         MALLOCARRAY(dist.weight[y], w);
         if (dist.weight[y] == NULL)
@@ -330,7 +330,7 @@ binarize(bitmap_type *bitmap)
     }
     else
     {
-	    WARNING1("binarize: %u-plane images are not supported", spp);
+	    WARNING1("binarize: don't know how to interpret %u-plane images", spp);
     }
 }
 
diff --git a/converter/other/pamtosvg/message.h b/converter/other/pamtosvg/message.h
index 0a226206..0d0b9db5 100644
--- a/converter/other/pamtosvg/message.h
+++ b/converter/other/pamtosvg/message.h
@@ -10,10 +10,6 @@
 
 /* Define common sorts of messages.  */
 
-/* This should be called only after a system call fails.  */
-#define FATAL_PERROR(s) do { perror (s); exit (errno); } while (0)
-
-
 #define START_FATAL() do { fputs ("fatal: ", stderr); LOG("fatal: ")
 #define END_FATAL() fputs (".\n", stderr); exit (1); } while (0)
 
@@ -21,12 +17,6 @@
   START_FATAL (); fprintf (stderr, "%s", s); LOG (s); END_FATAL ()
 #define FATAL1(s, e1)							\
   START_FATAL (); fprintf (stderr, s, e1); LOG1 (s, e1); END_FATAL ()
-#define FATAL2(s, e1, e2)						\
-  START_FATAL (); fprintf (stderr, s, e1, e2); LOG2 (s, e1, e2); END_FATAL ()
-#define FATAL3(s, e1, e2, e3)						\
-  START_FATAL (); fprintf (stderr, s, e1, e2, e3); LOG3 (s, e1, e2, e3); END_FATAL ()
-#define FATAL4(s, e1, e2, e3, e4)					\
-  START_FATAL (); fprintf (stderr, s, e1, e2, e3, e4); LOG4 (s, e1, e2, e3, e4); END_FATAL ()
 
 
 #define START_WARNING() do { fputs ("warning: ", stderr); LOG ("warning: ")
@@ -36,12 +26,6 @@
   START_WARNING (); fprintf (stderr, "%s", s); LOG (s); END_WARNING ()
 #define WARNING1(s, e1)							\
   START_WARNING (); fprintf (stderr, s, e1); LOG1 (s, e1); END_WARNING ()
-#define WARNING2(s, e1, e2)						\
-  START_WARNING (); fprintf (stderr, s, e1, e2); LOG2 (s, e1, e2); END_WARNING ()
-#define WARNING3(s, e1, e2, e3)						\
-  START_WARNING (); fprintf (stderr, s, e1, e2, e3); LOG3 (s, e1, e2, e3); END_WARNING ()
-#define WARNING4(s, e1, e2, e3, e4)					\
-  START_WARNING (); fprintf (stderr, s, e1, e2, e3, e4); LOG4 (s, e1, e2, e3, e4); END_WARNING ()
 
 #endif /* not MESSAGE_H */
 
diff --git a/converter/other/pamtosvg/pamtosvg.c b/converter/other/pamtosvg/pamtosvg.c
index dbe67c74..adf76801 100644
--- a/converter/other/pamtosvg/pamtosvg.c
+++ b/converter/other/pamtosvg/pamtosvg.c
@@ -134,8 +134,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 pm_optParseOptions3 on how to parse our options. */
     optStruct3 opt;
 
     const char * background_colorOpt;
@@ -197,7 +197,7 @@ parseCommandLine(int argc,
     cmdlineP->tangent_surround         = 3;
     cmdlineP->width_weight_factor      = 6.0;
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
     /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (cmdlineP->backgroundSpec)
@@ -334,7 +334,7 @@ openLogFile(FILE **      const logFileP,
     const char * logfileName;
 
     if (streq(inputFileArg, "-"))
-        asprintfN(&logfileName, "pamtosvg.log");
+        pm_asprintf(&logfileName, "pamtosvg.log");
     else {
         const char * inputRootName;
 
@@ -343,14 +343,14 @@ openLogFile(FILE **      const logFileP,
             pm_error("Can't find the root portion of file name '%s'",
                      inputFileArg);
     
-        asprintfN(&logfileName, "%s.log", inputRootName);
+        pm_asprintf(&logfileName, "%s.log", inputRootName);
 
-        strfree(inputRootName);
+        pm_strfree(inputRootName);
     }
 
     *logFileP = pm_openw(logfileName);
 
-    strfree(logfileName);
+    pm_strfree(logfileName);
 }
     
 
diff --git a/converter/other/pamtosvg/pxl-outline.c b/converter/other/pamtosvg/pxl-outline.c
index a1fa5299..456f41e1 100644
--- a/converter/other/pamtosvg/pxl-outline.c
+++ b/converter/other/pamtosvg/pxl-outline.c
@@ -9,7 +9,6 @@
 
 #include "message.h"
 #include "bitmap.h"
-#include "bitmap.h"
 #include "logreport.h"
 #include "pxl-outline.h"
 
diff --git a/converter/other/pamtosvg/thin-image.c b/converter/other/pamtosvg/thin-image.c
index 40ced794..364f67cc 100644
--- a/converter/other/pamtosvg/thin-image.c
+++ b/converter/other/pamtosvg/thin-image.c
@@ -171,7 +171,7 @@ thin_image(bitmap_type *image, bool bgSpec, pixel bg,
 	    if (PPM_ISGRAY(background))
             bg_color = PPM_GETR(background);
 	    else
-            bg_color = PPM_LUMIN(background);
+            bg_color = ppm_luminosity(background);
 
 	    for (n = num_pixels - 1; n >= 0L; --n)
 	    {
@@ -189,7 +189,7 @@ thin_image(bitmap_type *image, bool bgSpec, pixel bg,
 
 	default:
 	{
-	  LOG1 ("thin_image: %u-plane images are not supported", spp);
+	  LOG1 ("thin_image: Don't know how to interpret %u-plane images", spp);
 	  at_exception_fatal(exp, "thin_image: wrong plane images are passed");
 	  goto cleanup;
 	}
@@ -306,7 +306,7 @@ void thin1(bitmap_type *image, unsigned char colour)
       if (PPM_ISGRAY(background))
           bg_color = PPM_GETR(background);
       else
-          bg_color = PPM_LUMIN(background);
+          bg_color = ppm_luminosity(background);
 
       LOG (" Thinning image.....\n "); 
       xsize = image->width;
diff --git a/converter/other/pamtosvg/vector.c b/converter/other/pamtosvg/vector.c
index 7678f73a..0a5ef3a9 100644
--- a/converter/other/pamtosvg/vector.c
+++ b/converter/other/pamtosvg/vector.c
@@ -1,5 +1,6 @@
 /* vector.c: vector/point operations. */
 
+#define _XOPEN_SOURCE   /* Make sure M_PI is in <math.h> */
 #include <math.h>
 #include <errno.h>
 #include <assert.h>
diff --git a/converter/other/pamtotga.c b/converter/other/pamtotga.c
index c23b92b1..aca93015 100644
--- a/converter/other/pamtotga.c
+++ b/converter/other/pamtotga.c
@@ -74,7 +74,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (cmap + mono + rgb > 1)
@@ -463,7 +463,7 @@ static void
 releaseTgaHeader(struct ImageHeader const tgaHeader) {
 
     if (tgaHeader.IdLength > 0)
-        strfree(tgaHeader.Id);
+        pm_strfree(tgaHeader.Id);
 }
 
 
@@ -567,7 +567,7 @@ main(int argc, char *argv[]) {
         pnm_freetupletable(&pam, chv);
 
     releaseTgaHeader(tgaHeader);
-    strfree(outName);
+    pm_strfree(outName);
     pnm_freepamarray(tuples, &pam);
 
     return 0;
diff --git a/converter/other/pamtotiff.c b/converter/other/pamtotiff.c
index 1b31c65b..7b645b23 100644
--- a/converter/other/pamtotiff.c
+++ b/converter/other/pamtotiff.c
@@ -27,12 +27,6 @@
 #include <unistd.h>
 #include <stdio.h>
 #include <fcntl.h>
-#ifdef VMS
-#ifdef SYSV
-#undef SYSV
-#endif
-#include <tiffioP.h>
-#endif
 /* tiffio.h has a weird problem on AIX.  tiffio.h wrongly typedefs
    "int32".  That's wrong because such a name is likely to be used in
    other parts of the program that includes tiffio.h.  And in fact, on
@@ -59,16 +53,19 @@
 #define COMPRESSION_ADOBE_DEFLATE 8
 #endif
 
-struct sizeset {
+typedef struct {
     bool b1, b2, b4, b8;
-};
+} SizeSet;
+
+typedef enum { TMPFILE, DIRECT_CREATE, DIRECT_APPEND } WriteMethod;
 
+typedef enum { MUST_EXIST, MAY_CREATE } CreatePolicy;
 
-struct cmdlineInfo {
+typedef struct {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *input_filespec;  /* Filespecs of input files */
+    const char * inputFileName;
     int compression;
         /* COMPRESSION Tiff tag value, that corresponds to the compression
            option the user specified, or -1 if he didn't specify any.
@@ -84,14 +81,15 @@ struct cmdlineInfo {
     unsigned int color;        /* logical: assume not grayscale */
     float xresolution; /* XRESOLUTION Tiff tag value or -1 for none */
     float yresolution; /* YRESOLUTION Tiff tag value or -1 for none */
-    int resolutionUnit;  /* RESOLUTIONUNIT Tiff tag value */
-    struct sizeset indexsizeAllowed;
+    int resolutionunit;  /* RESOLUTIONUNIT Tiff tag value */
+    SizeSet indexsizeAllowed;
     /* Which bit widths are allowable in a raster of palette indices */
     unsigned int verbose;
-    unsigned int append;
+    WriteMethod writeMethod;  /* Output mode */
+    const char * output; /* -output option value.  NULL if none. */
     float resolution;  /* X and Y resolution */
     struct optNameValue * taglist;
-};
+} CmdlineInfo;
 
 
 
@@ -102,7 +100,7 @@ validateTagList(struct optNameValue const taglist[]) {
     for (i = 0; taglist[i].name; ++i) {
         const char * const tagName = taglist[i].name;
         const tagDefinition * tagDefP = tagDefFind(tagName);
-        
+
         if (!tagDefP)
             pm_error("Unknown tag name '%s'", tagName);
         else {
@@ -137,9 +135,9 @@ validateTagList(struct optNameValue const taglist[]) {
 
 
 static void
-parseCommandLine(int                        argc,
-                 char **              const argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int                 argc,
+                 const char ** const argv,
+                 CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
@@ -151,10 +149,11 @@ parseCommandLine(int                        argc,
     unsigned int none, packbits, lzw, g3, g4, msb2lsb, lsb2msb, opt_2d, fill;
     unsigned int flate, adobeflate;
     char * indexbits;
-    char * resolutionUnit;
+    char * resolutionunit;
 
-    unsigned int predictorSpec, rowsperstripSpec, xresolutionSpec,
-        yresolutionSpec, indexbitsSpec, resolutionUnitSpec, tagSpec;
+    unsigned int appendSpec, outputSpec, predictorSpec, rowsperstripSpec,
+                 xresolutionSpec, yresolutionSpec, indexbitsSpec,
+      resolutionunitSpec, tagSpec;
 
     unsigned int option_def_index;
 
@@ -162,7 +161,6 @@ parseCommandLine(int                        argc,
 
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0, "verbose",      OPT_FLAG,   NULL, &cmdlineP->verbose,       0);
-    OPTENT3(0, "append",       OPT_FLAG,   NULL, &cmdlineP->append,        0);
     OPTENT3(0, "none",         OPT_FLAG,   NULL, &none,                    0);
     OPTENT3(0, "packbits",     OPT_FLAG,   NULL, &packbits,                0);
     OPTENT3(0, "lzw",          OPT_FLAG,   NULL, &lzw,                     0);
@@ -180,17 +178,20 @@ parseCommandLine(int                        argc,
     OPTENT3(0, "mw",           OPT_FLAG,   NULL, &cmdlineP->miniswhite,    0);
     OPTENT3(0, "truecolor",    OPT_FLAG,   NULL, &cmdlineP->truecolor,     0);
     OPTENT3(0, "color",        OPT_FLAG,   NULL, &cmdlineP->color,         0);
-    OPTENT3(0, "predictor",    OPT_UINT,   &cmdlineP->predictor,    
+    OPTENT3(0, "append",       OPT_FLAG,   NULL, &appendSpec,       0);
+    OPTENT3(0, "output",       OPT_STRING, &cmdlineP->output,
+            &outputSpec,       0);
+    OPTENT3(0, "predictor",    OPT_UINT,   &cmdlineP->predictor,
             &predictorSpec,    0);
-    OPTENT3(0, "rowsperstrip", OPT_UINT,   &cmdlineP->rowsperstrip, 
+    OPTENT3(0, "rowsperstrip", OPT_UINT,   &cmdlineP->rowsperstrip,
             &rowsperstripSpec, 0);
-    OPTENT3(0, "xresolution",  OPT_FLOAT,  &cmdlineP->xresolution,  
+    OPTENT3(0, "xresolution",  OPT_FLOAT,  &cmdlineP->xresolution,
             &xresolutionSpec,  0);
-    OPTENT3(0, "yresolution",  OPT_FLOAT,  &cmdlineP->yresolution,  
+    OPTENT3(0, "yresolution",  OPT_FLOAT,  &cmdlineP->yresolution,
             &yresolutionSpec,  0);
-    OPTENT3(0, "resolutionunit", OPT_STRING, &resolutionUnit,
-            &resolutionUnitSpec,    0);
-    OPTENT3(0, "indexbits",    OPT_STRING,   &indexbits, 
+    OPTENT3(0, "resolutionunit", OPT_STRING, &resolutionunit,
+            &resolutionunitSpec,    0);
+    OPTENT3(0, "indexbits",    OPT_STRING,   &indexbits,
             &indexbitsSpec,    0);
     OPTENT3(0, "tag",          OPT_NAMELIST, &cmdlineP->taglist, &tagSpec, 0);
 
@@ -198,14 +199,14 @@ 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);
+    pm_optParseOptions3(&argc, (char**)argv, opt, sizeof(opt), 0);
     /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (none + packbits + lzw + g3 + g4 + flate + adobeflate > 1)
         pm_error("You specified more than one compression option.  "
                  "Only one of -none, -packbits, -lze, -g3, and -g4 "
                  "is allowed.");
-    
+
     if (none)
         cmdlineP->compression = COMPRESSION_NONE;
     else if (packbits)
@@ -222,7 +223,7 @@ parseCommandLine(int                        argc,
         cmdlineP->compression = COMPRESSION_DEFLATE;
     else
         cmdlineP->compression = COMPRESSION_NONE;
-    
+
     if (msb2lsb + lsb2msb > 1)
         pm_error("You specified both -msb2lsb and -lsb2msb.  "
                  "These are conflicting options.");
@@ -231,9 +232,9 @@ parseCommandLine(int                        argc,
         cmdlineP->fillorder = FILLORDER_MSB2LSB;
     else if (lsb2msb)
         cmdlineP->fillorder = FILLORDER_LSB2MSB;
-    else 
+    else
         cmdlineP->fillorder = FILLORDER_MSB2LSB;
-    
+
 
     if (cmdlineP->miniswhite && cmdlineP->minisblack)
         pm_error("You cannot specify both -miniswhite and -minisblack");
@@ -244,9 +245,17 @@ parseCommandLine(int                        argc,
     if (fill)
         cmdlineP->g3options |= GROUP3OPT_FILLBITS;
 
+    if (outputSpec) {
+        if (appendSpec)
+            cmdlineP->writeMethod = DIRECT_APPEND;
+        else
+            cmdlineP->writeMethod = DIRECT_CREATE;
+    } else
+        cmdlineP->writeMethod = TMPFILE;
+
     if (predictorSpec) {
         if (cmdlineP->predictor != 1 && cmdlineP->predictor != 2)
-            pm_error("-predictor may be only 1 or 2.  You specified %d.", 
+            pm_error("-predictor may be only 1 or 2.  You specified %d.",
                      cmdlineP->predictor);
     } else
         cmdlineP->predictor = -1;
@@ -272,25 +281,25 @@ parseCommandLine(int                        argc,
     } else
         cmdlineP->yresolution = -1;
 
-    if (resolutionUnitSpec) {
-        if (streq(resolutionUnit, "inch"))
-            cmdlineP->resolutionUnit = RESUNIT_INCH;
-        else if (streq(resolutionUnit, "in"))
-            cmdlineP->resolutionUnit = RESUNIT_INCH;
-        else if (streq(resolutionUnit, "centimeter"))
-            cmdlineP->resolutionUnit = RESUNIT_CENTIMETER;
-        else if (streq(resolutionUnit, "cm"))
-            cmdlineP->resolutionUnit = RESUNIT_CENTIMETER;
-        else if (streq(resolutionUnit, "none"))
-            cmdlineP->resolutionUnit = RESUNIT_NONE;
-        else if (streq(resolutionUnit, "no"))
-            cmdlineP->resolutionUnit = RESUNIT_NONE;
+    if (resolutionunitSpec) {
+        if (streq(resolutionunit, "inch"))
+            cmdlineP->resolutionunit = RESUNIT_INCH;
+        else if (streq(resolutionunit, "in"))
+            cmdlineP->resolutionunit = RESUNIT_INCH;
+        else if (streq(resolutionunit, "centimeter"))
+            cmdlineP->resolutionunit = RESUNIT_CENTIMETER;
+        else if (streq(resolutionunit, "cm"))
+            cmdlineP->resolutionunit = RESUNIT_CENTIMETER;
+        else if (streq(resolutionunit, "none"))
+            cmdlineP->resolutionunit = RESUNIT_NONE;
+        else if (streq(resolutionunit, "no"))
+            cmdlineP->resolutionunit = RESUNIT_NONE;
         else
             pm_error("The only acceptable values for -resolutionunit are "
                      "inch, centimeter, none, in, cm, and no.  "
-                     "You specified '%s'.", resolutionUnit);
+                     "You specified '%s'.", resolutionunit);
     } else
-        cmdlineP->resolutionUnit = RESUNIT_INCH;
+        cmdlineP->resolutionunit = RESUNIT_INCH;
 
     if (indexbitsSpec) {
         if (strstr(indexbits, "1"))
@@ -324,46 +333,18 @@ parseCommandLine(int                        argc,
         cmdlineP->taglist[0].value = NULL;
     }
 
-    if (argc-1 == 0) 
-        cmdlineP->input_filespec = "-";
+    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->input_filespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
 }
 
 
 
 static void
-putSample(sample           const s,
-          sample           const maxval,
-          sample           const tiff_maxval,
-          unsigned int     const bitspersample,
-          unsigned char ** const tPP) {
-
-    xelval s2;
-
-    s2 = s;
-    if (maxval != tiff_maxval)
-        s2 = s * tiff_maxval / maxval;
-    if (bitspersample > 8) {
-        *((unsigned short *)(*tPP)) = s2;
-        (*tPP) += sizeof(short);
-    } else
-        *(*tPP)++ = s2 & 0xff;
-}
-
-
-
-
-/* Note: PUTSAMPLE doesn't work if bitspersample is 1-4. */
-
-#define PUTSAMPLE putSample(s, maxval, tiff_maxval, bitspersample, &tP);
-
-
-
-static void
 fillRowOfSubBytePixels(struct pam *    const pamP,
                        const tuple *   const tuplerow,
                        unsigned char * const buf,
@@ -380,9 +361,9 @@ fillRowOfSubBytePixels(struct pam *    const pamP,
     int bitshift;
         /* The number of bits we have to shift a pixel value left to line
            it up with where the current pixel goes in the current byte of
-           the output buffer.  
+           the output buffer.
         */
-    int const firstbitshift = 
+    int const firstbitshift =
         (fillorder == FILLORDER_MSB2LSB) ? 8 - bitspersample : 0;
         /* The value of 'bitshift' for the first pixel into a
            byte of the output buffer.  (MSB2LSB is normal).
@@ -397,7 +378,7 @@ fillRowOfSubBytePixels(struct pam *    const pamP,
         /* The under-construction value of the byte pointed to by
            tP, above.
         */
-                
+
     bitshift = firstbitshift;
     byte = 0;
     for (col = 0, tP = buf; col < pamP->width; ++col) {
@@ -406,7 +387,7 @@ fillRowOfSubBytePixels(struct pam *    const pamP,
             s = tuplerow[col][0];
             if (pamP->maxval != tiff_maxval )
                 s = (long) s * tiff_maxval / pamP->maxval;
- 
+
             if (photometric == PHOTOMETRIC_MINISWHITE)
                 s = tiff_maxval - s;
         } else {
@@ -440,6 +421,36 @@ fillRowOfSubBytePixels(struct pam *    const pamP,
 
 
 static void
+putSample(sample           const s,
+          sample           const maxval,
+          sample           const tiffMaxval,
+          unsigned int     const bitspersample,
+          bool             const minIsWhite,
+          unsigned char ** const tPP) {
+
+    /* Until release 10.48 (September 2009), we ignored the min-is-white
+       photometric (i.e. treated it like min-is-black).  Nobody has ever
+       complained, but it seems clear to me that that was wrong, so I
+       changed it.  We have always respected it for sub-byte samples,
+       and have always respected it going the other direction, in
+       Tifftopnm.
+       - Bryan.
+    */
+
+    xelval const s2 = maxval == tiffMaxval ? s : s * tiffMaxval / maxval;
+
+    xelval const s3 = minIsWhite ? tiffMaxval - s2 : s2;
+
+    if (bitspersample > 8) {
+        *((unsigned short *)(*tPP)) = s3;
+        (*tPP) += sizeof(short);
+    } else
+        *(*tPP)++ = s3 & 0xff;
+}
+
+
+
+static void
 fillRowOfWholeBytePixels(struct pam *    const pamP,
                          tuple *         const tuplerow,
                          unsigned char * const buf,
@@ -447,10 +458,12 @@ fillRowOfWholeBytePixels(struct pam *    const pamP,
                          unsigned short  const tiffMaxval,
                          unsigned int    const bitsPerSample) {
 
+    bool const minIsWhite = (photometric == PHOTOMETRIC_MINISWHITE);
+
     unsigned int col;
     unsigned char * tP;
     unsigned int planes;
-    
+
     if (photometric == PHOTOMETRIC_RGB)
         planes = pamP->depth;
     else
@@ -463,22 +476,22 @@ fillRowOfWholeBytePixels(struct pam *    const pamP,
         unsigned int plane;
         for (plane = 0; plane < planes; ++plane) {
             putSample(tuplerow[col][plane], pamP->maxval,
-                      tiffMaxval, bitsPerSample, &tP);
+                      tiffMaxval, bitsPerSample, minIsWhite, &tP);
             /* Advances tP */
         }
     }
-} 
+}
 
 
 
 static void
 writeScanLines(struct pam *   const pamP,
-               TIFF *         const tif, 
+               TIFF *         const tif,
                tuplehash      const cht,
                unsigned short const tiffMaxval,
-               unsigned short const bitspersample, 
+               unsigned short const bitspersample,
                unsigned short const photometric,
-               int            const bytesperrow, 
+               int            const bytesperrow,
                int            const fillorder) {
 /*----------------------------------------------------------------------------
    Write out the raster for the input image described by 'pamP', whose
@@ -492,15 +505,19 @@ writeScanLines(struct pam *   const pamP,
        it's 'buf' parameter, but here it is: Its format depends on the
        bits per pixel of the TIFF image.  If it's 16, 'buf' is an
        array of short (16 bit) integers, one per raster column.  If
-       it's 8, 'buf' is an array of characters (8 bit integers), one
-       per image column.  If it's less than 8, it's an array of characters,
-       each of which represents 1-8 raster columns, packed
+       it's 8, 'buf' is an array of 8 bit unsigned integers, one
+       per pixel sample.  If it's less than 8, it's an array of bytes,
+       each of which represents 1-8 pixel samples, packed
        into it in the order specified by the TIFF image's fill order,
        with don't-care bits on the right such that each byte contains only
-       whole pixels.
+       whole samples.
 
        In all cases, the array elements are in order left to right going
        from low array indices to high array indices.
+
+       The samples form pixel values according to the pixel format indicated
+       by the TIFF photometric.  E.g. if it is MINISWHITE, then a pixel is
+       one sample and a value of 0 for that sample means white.
     */
     MALLOCARRAY(buf, bytesperrow);
 
@@ -508,7 +525,7 @@ writeScanLines(struct pam *   const pamP,
         pm_error("can't allocate memory for row buffer");
 
     tuplerow = pnm_allocpamrow(pamP);
-    
+
     for (row = 0; row < pamP->height; ++row) {
         int col;
 
@@ -530,9 +547,9 @@ writeScanLines(struct pam *   const pamP,
                 for (col = 0; col < pamP->width; ++col) {
                     int si;
                     int found;
-                    
+
                     pnm_lookuptuple(pamP, cht, tuplerow[col], &found, &si);
-                    
+
                     if (!found)
                         pm_error("INTERNAL ERROR.  We made a color map, and a "
                                  "color map we need is not in it!  "
@@ -556,12 +573,12 @@ writeScanLines(struct pam *   const pamP,
 
 
 static void
-analyzeColorsInRgbInput(struct pam *        const pamP,
-                        struct cmdlineInfo  const cmdline,
-                        int                 const maxcolors, 
-                        tupletable *        const chvP, 
-                        unsigned int *      const colorsP, 
-                        bool *              const grayscaleP) {
+analyzeColorsInRgbInput(struct pam *   const pamP,
+                        CmdlineInfo    const cmdline,
+                        int            const maxcolors,
+                        tupletable *   const chvP,
+                        unsigned int * const colorsP,
+                        bool *         const grayscaleP) {
 /*----------------------------------------------------------------------------
    Same as analyzeColors(), except assuming input image has R/G/B tuples.
 -----------------------------------------------------------------------------*/
@@ -580,7 +597,7 @@ analyzeColorsInRgbInput(struct pam *        const pamP,
             grayscale = FALSE;
         } else {
             unsigned int i;
-            pm_message("%u color%s found", 
+            pm_message("%u color%s found",
                        *colorsP, *colorsP == 1 ? "" : "s");
             grayscale = TRUE;  /* initial assumption */
             for (i = 0; i < *colorsP && grayscale; ++i) {
@@ -613,15 +630,15 @@ analyzeColorsInRgbInput(struct pam *        const pamP,
 
 
 static void
-analyzeColors(struct pam *        const pamP,
-              struct cmdlineInfo  const cmdline,
-              int                 const maxcolors, 
-              tupletable *        const chvP, 
-              unsigned int *      const colorsP, 
-              bool *              const grayscaleP) {
+analyzeColors(struct pam *   const pamP,
+              CmdlineInfo    const cmdline,
+              int            const maxcolors,
+              tupletable *   const chvP,
+              unsigned int * const colorsP,
+              bool *         const grayscaleP) {
 /*----------------------------------------------------------------------------
    Analyze the colors in the input image described by 'pamP', whose file
-   is positioned to the raster. 
+   is positioned to the raster.
 
    If the colors, combined with command line options 'cmdline', indicate
    a colormapped TIFF should be generated, return as *chvP the address
@@ -650,13 +667,13 @@ analyzeColors(struct pam *        const pamP,
 
 static void
 computeRasterParm(struct pam *     const pamP,
-                  tupletable       const chv, 
-                  int              const colors, 
+                  tupletable       const chv,
+                  int              const colors,
                   bool             const grayscale,
                   int              const compression,
                   bool             const minisblack,
                   bool             const miniswhite,
-                  struct sizeset   const indexsizeAllowed,
+                  SizeSet          const indexsizeAllowed,
                   unsigned short * const samplesperpixelP,
                   unsigned short * const bitspersampleP,
                   unsigned short * const photometricP,
@@ -677,7 +694,7 @@ computeRasterParm(struct pam *     const pamP,
        option.  It is not clear why we don't use bits per pixel < 8
        for RGB images.  Note that code to handle maxvals <= 255 was
        written long before maxval > 255 was possible and there are
-       backward compatibility requirements.  
+       backward compatibility requirements.
     */
 
     if (pamP->depth == 1 && pamP->maxval == 1) {
@@ -691,7 +708,7 @@ computeRasterParm(struct pam *     const pamP,
     } else {
         if (chv) {
             *samplesperpixelP = 1;  /* Pixel is just the one index value */
-            *bitspersampleP = 
+            *bitspersampleP =
                 colors <=   2 && indexsizeAllowed.b1 ? 1 :
                 colors <=   4 && indexsizeAllowed.b2 ? 2 :
                 colors <=  16 && indexsizeAllowed.b4 ? 4 :
@@ -744,18 +761,53 @@ computeRasterParm(struct pam *     const pamP,
 
 
 
-static void
-validateSeekableOutputFile(int          const ofd,
-                           const char * const outFileName) {
 /*----------------------------------------------------------------------------
-   Validate that the file attached to file descriptor 'ofd' is capable
-   of seeking.  If not, fail the program.
+  WRITE MODES
+  -----------
+  
+  The Tiff library does all output.  There are several issues:
+  
+    1) The manner of output is opaque to the library client.  I.e.  we cannot
+       see or control it.
+
+    2) The output file must be random-access.
+
+    3) The output file must be writable and readable for multiple-image
+       streams.  (This includes append operations.)
+
+    4) The Tiff library produces unhelpful error messages when the above
+       conditions are not met.
+  
+  We provide two modes for output:
+  
+  1. Tmpfile mode (default)
+  
+     We have the Tiff library direct output to an unnamed temporary file we
+     create which is seekable and readable.  When output is complete, we copy
+     the file's contents to Standard Output.
+  
+  2. Direct mode (specified with -output)
+  
+     We have the Tiff library write output to the specified file.  As the Tiff
+     library requires taht it be be seekable and readable, we fail the program
+     rather than ask the Tiff library to use the file if it does not meet
+     these requirements.
+  
+     Direct mode is further divided into append and create.  They are the same
+     except that in append mode, we insist that the file already exist,
+     whereas with create mode, we create it if necessary.  In either case, if
+     the file already exists, he Tiff library appends the output to it.
+-----------------------------------------------------------------------------*/
+
 
-   This is useful because the TIFF library requires seekable output and
-   fails with an unhelpful error message about a file I/O error if it is
-   not.  We, on the other hand, give a helpful error message.
 
-   We leave the file positioned to the beginning.
+static bool
+fileIsSeekable(int          const ofd,
+               const char * const outFileName) {
+/*----------------------------------------------------------------------------
+  The file represented by 'ofd' iscapable of seeking.
+
+  As a side effect, we position the file to the beginning.
 -----------------------------------------------------------------------------*/
     int rc;
 
@@ -769,44 +821,170 @@ validateSeekableOutputFile(int          const ofd,
     */
     lseek(ofd, 1, SEEK_SET);
     rc = lseek(ofd, 0, SEEK_SET);
-            
-    if (rc < 0)
-        pm_error("Output file (%s) is not seekable.  lseek() returned "
-                 "errno %d (%s).  "
-                 "The TIFF library can write only to "
-                 "a seekable file.", 
-                 outFileName, errno, strerror(errno));
+
+    return rc >= 0;
+
+}
+
+
+
+static void
+validateReadableOutputFile(int const ofd) {
+/*----------------------------------------------------------------------------
+  Validate that file 'ofd' is readable and fail the program if it isn't.
+
+  This is useful because there are situations in which the TIFF library must
+  read the output file and if it can't, it fails with an unhelpful error
+  message about a file I/O error.  We, on the other hand, produce a helpful
+  error message.
+-----------------------------------------------------------------------------*/
+#if !MSVCRT
+
+    int flags;
+
+    flags = fcntl(ofd, F_GETFL);
+
+    if (flags < 0) {
+        /* We couldn't get the flags.  So just assume the file's OK */
+    } else {
+        if ((flags & O_RDONLY) || (flags & O_RDWR)) {
+            /* File is readable.  All is well. */
+        } else
+            pm_error("Output is not opened for reading.  "
+                     "In order to create a multi-image TIFF stream, "
+                     "output must be both readable and writable.");
+    }
+#endif
 }
 
 
 
 static void
-createTiffGenerator(int          const ofd, 
-                    const char * const outFileName,
-                    bool         const append,
-                    TIFF **      const tifPP) {
+createTiffGeneratorDirect(const char * const outputFileName,
+                          CreatePolicy const createPolicy,
+                          TIFF **      const tifPP,
+                          int  *       const ofdP) {
+/*----------------------------------------------------------------------------
+  Create a TIFF generator that writes its output to the specified file.
 
-    const char * option;
+  If the file doesn't already exist and 'createPolicy' is MayCreate,
+  create the file; otherwise fail the program.
 
-    validateSeekableOutputFile(ofd, outFileName);
+  Fail the program if the specified file is not seekable and readable.
 
-    if (append)
-        option = "a";
+  Return the handle of the TIFF generator as *tifPP.  Also return the
+  file descriptor for the output file as *ofdP.
+-----------------------------------------------------------------------------*/
+    int fd;
+
+    if (createPolicy == MUST_EXIST)
+        fd = open(outputFileName, O_RDWR);
     else
-        option = "w";
+        fd = open(outputFileName, (O_RDWR | O_CREAT), 00644);
+
+    if (fd == -1) {
+        if (errno == ENOENT) /* Possible only if MustExist */
+            pm_error ("Cannot open file : '%s'.  File does not exist.",
+                      outputFileName);
+        else
+            pm_error ("Cannot open file : '%s'.  open() failed with "
+                      "errno %d (%s).  ",
+                      outputFileName, errno, strerror(errno));
+    }
 
-    *tifPP = TIFFFdOpen(ofd, outFileName, option);
+    if (!fileIsSeekable(fd, outputFileName))
+        pm_error("Output file (%s) is not seekable.  "
+                 "lseek() returned errno %d (%s).  "
+                 "The TIFF library can write only to "
+                 "a seekable file.",
+                 outputFileName, errno, strerror(errno));
+
+    *tifPP = TIFFFdOpen(fd, outputFileName, "a");
     if (*tifPP == NULL)
-        pm_error("error opening standard output as TIFF file.  "
+        pm_error("error opening file %s as TIFF file.  "
+                 "TIFFFdOpen() failed.", outputFileName);
+
+    *ofdP = fd;
+}
+
+
+
+static void
+createTiffGeneratorTmpfile(TIFF ** const tifPP,
+                            int  * const ofdP) {
+/*----------------------------------------------------------------------------
+  Create a TIFF generator that writes its output to an unnnamed temporary file
+  we create.
+
+  Return the handle of the TIFF generator as *tifPP.  Also return the file
+  descriptor for the temporary file as *ofdP.
+
+  The TIFF generator has a file name attribute, but it is just for messages;
+  it is not the name of a file.  We use "Internal Temporary File".
+-----------------------------------------------------------------------------*/
+    int fd;
+
+    fd = pm_tmpfile_fd();
+
+    *tifPP = TIFFFdOpen(fd, "Internal Temporary File", "w");
+
+    if (*tifPP == NULL)
+        pm_error("error opening temporary file as TIFF file.  "
                  "TIFFFdOpen() failed.");
+
+    *ofdP = fd;
+}
+
+
+
+static void
+copyBufferToStdout(int const tmpfileFd) {
+
+    FILE * tmpfileP;
+
+    tmpfileP = fdopen(tmpfileFd, "rb");
+
+    fseek(tmpfileP, 0, SEEK_SET);
+
+    while (!feof(tmpfileP) && !ferror(tmpfileP) && !ferror(stdout)) {
+        char buffer[4096];
+        size_t bytesReadCt;
+
+        bytesReadCt = fread(buffer, 1, sizeof(buffer), tmpfileP);
+
+        if (ferror(tmpfileP))
+            pm_error("Error reading from temporary file.  "
+                     "Incomplete output.  "
+                     "Errno = %s (%d)", strerror(errno), errno);
+        else
+            fwrite(buffer, 1, bytesReadCt, stdout);
+    }
+
+    /* POSIX lets us create a FILE from an existing file descriptor, but
+       does not provide a way to destroy the FILE and keep the file
+       descriptor.  The following fclose() closes the file.  Caller
+       must not access the file again, and if he attempts to close it,
+       must ignore the failure of close
+    */
+    fclose(tmpfileP);
 }
 
 
 
 static void
-destroyTiffGenerator(TIFF * const tifP) {
+destroyTiffGenerator(WriteMethod const writeMethod,
+                     TIFF *      const tifP,
+                     int         const ofd) {
 
     TIFFFlushData(tifP);
+
+    if (writeMethod == TMPFILE)
+        copyBufferToStdout(ofd);
+
+    /* If we copied the buffer above, the buffer file is already closed
+       (copyBufferToStdout closes it), TIFFClose appears to tolerate that -
+       all it does is a close() and doesn't mind that it fails.
+    */
     TIFFClose(tifP);
 }
 
@@ -823,11 +1001,11 @@ createTiffColorMap(struct pam *       const pamP,
     unsigned short ** tiffColorMap;
     unsigned int plane;
     unsigned int i;
-    
+
     MALLOCARRAY_NOFAIL(tiffColorMap, pamP->depth);
     for (plane = 0; plane < pamP->depth; ++plane)
         MALLOCARRAY_NOFAIL(tiffColorMap[plane], colorMapSize);
-    
+
     for (i = 0; i < colorMapSize; ++i) {
         unsigned int plane;
         for (plane = 0; plane < pamP->depth; ++plane) {
@@ -840,7 +1018,7 @@ createTiffColorMap(struct pam *       const pamP,
     }
     *tiffColorMapP = tiffColorMap;
 }
-        
+
 
 
 static void
@@ -865,7 +1043,7 @@ setTagListFields(const struct optNameValue * const taglist,
 
     for (i = 0; taglist[i].name; ++i) {
         const tagDefinition * const tagDefP = tagDefFind(taglist[i].name);
-        
+
         if (tagDefP->put)
             tagDefP->put(tifP, tagDefP->tagnum, taglist[i].value,
                          tagDefP->choices);
@@ -876,7 +1054,7 @@ setTagListFields(const struct optNameValue * const taglist,
 
 static void
 setTiffFields(TIFF *              const tifP,
-              struct cmdlineInfo  const cmdline,
+              CmdlineInfo         const cmdline,
               struct pam *        const pamP,
               unsigned short      const bitspersample,
               unsigned short      const photometric,
@@ -911,12 +1089,17 @@ setTiffFields(TIFF *              const tifP,
     else
         TIFFSetField(tifP, TIFFTAG_ROWSPERSTRIP,
                      TIFFDefaultStripSize(tifP, 0));
+    /* Since Netpbm 10.31, we prefer that the user use -tags to specify
+       RESOLUTIONUNIT, XRESOLUTION, and YRESOLUTION, but retain
+       -xresolution, -yresolution, and -resolutionunit for backward
+       compatibility
+    */
     if (cmdline.xresolution != -1 ||
         cmdline.yresolution != -1 ||
-        cmdline.resolutionUnit != -1) {
+        cmdline.resolutionunit != -1) {
         TIFFSetField(tifP, TIFFTAG_RESOLUTIONUNIT,
-                     cmdline.resolutionUnit != -1 ?
-                     cmdline.resolutionUnit : RESUNIT_NONE);
+                     cmdline.resolutionunit != -1 ?
+                     cmdline.resolutionunit : RESUNIT_NONE);
     }
     if (cmdline.xresolution > 0)
         TIFFSetField(tifP, TIFFTAG_XRESOLUTION, cmdline.xresolution);
@@ -933,7 +1116,7 @@ setTiffFields(TIFF *              const tifP,
 
     TIFFSetField(tifP, TIFFTAG_DOCUMENTNAME,     inputFileDescription);
     TIFFSetField(tifP, TIFFTAG_IMAGEDESCRIPTION, "converted PNM file");
- 
+
     /* Some of taglist[] overrides defaults we set above.  But taglist[]
        is defined not to specify any tag types that are not purely user
        choice.
@@ -944,10 +1127,10 @@ setTiffFields(TIFF *              const tifP,
 
 
 static void
-convertImage(FILE *             const ifP,
-             TIFF *             const tifP,
-             const char *       const inputFileDescription,
-             struct cmdlineInfo const cmdline) {
+convertImage(FILE *       const ifP,
+             TIFF *       const tifP,
+             const char * const inputFileDescription,
+             CmdlineInfo  const cmdline) {
 
     tupletable chv;
     tuplehash cht;
@@ -959,7 +1142,7 @@ convertImage(FILE *             const ifP,
     unsigned short samplesperpixel;
     unsigned short bitspersample;
     unsigned short tiff_maxval;
-    /* This is the maxval of the samples in the tiff file.  It is 
+    /* This is the maxval of the samples in the tiff file.  It is
        determined solely by the bits per sample ('bitspersample').
        */
     int bytesperrow;
@@ -972,11 +1155,11 @@ convertImage(FILE *             const ifP,
     analyzeColors(&pam, cmdline, MAXCOLORS, &chv, &colors, &grayscale);
 
     /* Go back to beginning of raster */
-    pm_seek2(ifP, &rasterPos, sizeof(rasterPos));  
+    pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
 
     /* Figure out TIFF parameters. */
 
-    computeRasterParm(&pam, chv, colors, grayscale, 
+    computeRasterParm(&pam, chv, colors, grayscale,
                       cmdline.compression,
                       cmdline.minisblack, cmdline.miniswhite,
                       cmdline.indexsizeAllowed,
@@ -1001,7 +1184,7 @@ convertImage(FILE *             const ifP,
                   cmdline.taglist);
 
     writeScanLines(&pam, tifP, cht,
-                   tiff_maxval, bitspersample, photometric, bytesperrow, 
+                   tiff_maxval, bitspersample, photometric, bytesperrow,
                    cmdline.fillorder);
 
     if (tiffColorMap)
@@ -1010,63 +1193,41 @@ convertImage(FILE *             const ifP,
 
 
 
-static void
-validateReadableStdout(void) {
-/*----------------------------------------------------------------------------
-  We validate that Standard Output is readable and fail the program if
-  it isn't.
-
-  This is useful because there are situations in which the TIFF library
-  must read the output file and if it can't, it fails with an unhelpful
-  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);
-
-    if (flags < 0) {
-        /* We couldn't get the flags.  So just assume the file's OK */
-    } else {
-        if ((flags & O_RDONLY) || (flags & O_RDWR)) {
-            /* File is readable.  All is well. */
-        } else
-            pm_error("Standard Output is not opened for reading.  "
-                     "In order to create a multi-image TIFF stream, "
-                     "Standard Output must be both readable and writable.");
-    }
-#endif
-}
 
 
 
 int
-main(int argc, char *argv[]) {
-    struct cmdlineInfo cmdline;
+main(int argc, const char *argv[]) {
+    CmdlineInfo cmdline;
     const char * inputFileDescription;
-    FILE* ifP;
-    TIFF* tifP;
-    bool eof;
+    FILE * ifP;
+    TIFF * tifP;
+    int ofd;
+    int eof;
     unsigned int imageSeq;
-
-    pnm_init(&argc, argv);
-
-    parseCommandLine(argc, argv, &cmdline);
     
-    ifP = pm_openr_seekable(cmdline.input_filespec);
+    pm_proginit(&argc, argv);
 
-    if (streq(cmdline.input_filespec, "-"))
-        inputFileDescription = "Standard Input";
-    else 
-        inputFileDescription = cmdline.input_filespec;
+    parseCommandLine(argc, argv, &cmdline);
 
-    if (cmdline.append)
-        validateReadableStdout();
+    ifP = pm_openr_seekable(cmdline.inputFileName);
 
-    createTiffGenerator(STDOUT_FILENO, "Standard Output", cmdline.append,
-                        &tifP);
+    if (streq(cmdline.inputFileName, "-"))
+        inputFileDescription = "Standard Input";
+    else
+        inputFileDescription = cmdline.inputFileName;
+
+    switch (cmdline.writeMethod) {
+    case DIRECT_APPEND:
+        createTiffGeneratorDirect(cmdline.output, MUST_EXIST,  &tifP, &ofd);
+        break;
+    case DIRECT_CREATE:
+        createTiffGeneratorDirect(cmdline.output, MAY_CREATE,  &tifP, &ofd);
+        break;
+    case TMPFILE:
+        createTiffGeneratorTmpfile(&tifP, &ofd);
+        break;
+    }
 
     eof = FALSE;  /* initial assumption */
     imageSeq = 0;
@@ -1074,17 +1235,17 @@ main(int argc, char *argv[]) {
     while (!eof) {
         bool success;
 
-        if (cmdline.verbose)
-            pm_message("Converting Image %u", imageSeq);
-
         pnm_nextimage(ifP, &eof);
 
         if (!eof) {
             if (imageSeq > 0)
-                validateReadableStdout();
+                validateReadableOutputFile(ofd);
+
+            if (cmdline.verbose)
+                pm_message("Converting Image %u", imageSeq);
 
             convertImage(ifP, tifP, inputFileDescription, cmdline);
-            
+
             success = TIFFWriteDirectory(tifP);
             if (!success)
                 pm_error("Unable to write TIFF image %u to file.  "
@@ -1093,8 +1254,10 @@ main(int argc, char *argv[]) {
         }
     }
 
-    destroyTiffGenerator(tifP);
+    destroyTiffGenerator(cmdline.writeMethod, tifP, ofd);
     pm_close(ifP);
 
     return 0;
 }
+
+
diff --git a/converter/other/pamtouil.c b/converter/other/pamtouil.c
index 1a53be0f..ee7f5ae6 100644
--- a/converter/other/pamtouil.c
+++ b/converter/other/pamtouil.c
@@ -61,7 +61,7 @@ parseCommandLine(int argc, char ** argv,
    malloc'ed storage.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -79,7 +79,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 == 0)
@@ -374,9 +374,9 @@ freeCmap(cixel_map    const cmap[],
     for (i = 0; i < ncolors; ++i) {
         cixel_map const cmapEntry = cmap[i];
         if (cmapEntry.uilname)
-            strfree(cmapEntry.uilname);
+            pm_strfree(cmapEntry.uilname);
         if (cmapEntry.rgbname)
-            strfree(cmapEntry.rgbname);
+            pm_strfree(cmapEntry.rgbname);
     }
 }
 
diff --git a/converter/other/pamtowinicon.c b/converter/other/pamtowinicon.c
new file mode 100644
index 00000000..7e2c9e86
--- /dev/null
+++ b/converter/other/pamtowinicon.c
@@ -0,0 +1,1177 @@
+#define _POSIX_SOURCE   /* Make sure fdopen() is in <stdio.h> */
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "netpbm/pm_config.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+#include "netpbm/shhopt.h"
+#include "netpbm/pm_system.h"
+#include "netpbm/pam.h"
+#include "winicon.h"
+
+
+
+struct CmdlineInfo {
+    const char * inputFileName;
+    unsigned int verbose;
+    int          pngthreshold;
+    unsigned int truetransparent;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char **argv,
+                 struct CmdlineInfo * const cmdlineP) {
+
+    optEntry *   option_def;
+    unsigned int option_def_index;
+    optStruct3   opt3;
+    unsigned int pngthresholdSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;
+
+    OPTENT3(0, "verbose",         OPT_FLAG, NULL,
+            &cmdlineP->verbose,         0);
+    OPTENT3(0, "pngthreshold",    OPT_UINT, &cmdlineP->pngthreshold,
+            &pngthresholdSpec,          0);
+    OPTENT3(0, "truetransparent", OPT_FLAG, NULL,
+            &cmdlineP->truetransparent, 0);
+
+    opt3.opt_table     = option_def;
+    opt3.short_allowed = false;
+    opt3.allowNegNum   = false;
+
+    pm_optParseOptions3(&argc, (char **)argv, opt3, sizeof(opt3), 0);
+
+    if (pngthresholdSpec) {
+        if (UINT_MAX / cmdlineP->pngthreshold < cmdlineP->pngthreshold)
+            pm_error("-pngthreshold is too large: %u", cmdlineP->pngthreshold);
+    } else
+        cmdlineP->pngthreshold = 128;
+
+    if (argc-1 > 0) {
+        cmdlineP->inputFileName = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("Too many arguments: %u.  The only non-option "
+                     "argument is the optional input file name", argc-1);
+    } else
+        cmdlineP->inputFileName = "-";
+
+    free(option_def);
+}
+
+
+
+static bool verbose;
+
+static unsigned char const pngHeader[] = PNG_HEADER;
+
+
+
+struct Palette {
+    sample color[256][3];
+    unsigned int colorCt;
+        /* Number of colors in color[][] */
+    bool tooManyColors;
+        /* There are too many colors for a BMP palette (more than 256); only
+           the first 256 are in color[][]
+        */
+};
+
+
+
+static struct IconDir *
+newIconDir() {
+
+    struct IconDir * dirP;
+
+    MALLOCVAR_NOFAIL(dirP);
+
+    dirP->zero           = 0;
+    dirP->type           = ICONDIR_TYPE_ICO;
+    dirP->count          = 0;
+
+    dirP->entriesAllocCt = 0;
+    dirP->entries        = NULL;
+
+    return dirP;
+}
+
+
+
+static void
+freeIconDir(struct IconDir * const dirP) {
+    if (dirP->entries)
+        free(dirP->entries);
+
+    free(dirP);
+}
+
+
+
+static void
+addToDirectory(struct IconDirEntry * const dirEntryP,
+               struct IconDir *      const dirP) {
+/*----------------------------------------------------------------------------
+  Add an icon to the icon directory.
+-----------------------------------------------------------------------------*/
+    if (dirP->count + 1 > dirP->entriesAllocCt) {
+        /* Out of space in dirP->entries[].  Expand. */
+
+        dirP->entriesAllocCt += 8;
+
+        REALLOCARRAY(dirP->entries, dirP->entriesAllocCt);
+
+        if (!dirP->entries)
+            pm_error("Unable to get memory for %u entries "
+                     "in the Icon directory.", dirP->entriesAllocCt);
+    }
+
+    dirP->entries[dirP->count++] = *dirEntryP;
+}
+
+
+
+typedef void (GetPixelFn) (tuple **     const tuples,
+                           unsigned int const col,
+                           unsigned int const row,
+                           sample *     const pixel);
+
+
+
+static GetPixelFn get_grayscalePixel;
+
+static void
+get_grayscalePixel(tuple **     const tuples,
+                   unsigned int const col,
+                   unsigned int const row,
+                   sample *     const pixel) {
+/*----------------------------------------------------------------------------
+   Get a pixel from a grayscale PAM
+-----------------------------------------------------------------------------*/
+    pixel[0] = tuples[row][col][0];
+    pixel[1] = tuples[row][col][0];
+    pixel[2] = tuples[row][col][0];
+}
+
+
+
+static GetPixelFn get_rgbPixel;
+
+static void
+get_rgbPixel(tuple **     const tuples,
+             unsigned int const col,
+             unsigned int const row,
+             sample *     const pixel) {
+/*----------------------------------------------------------------------------
+   Get a pixel from an RGB PAM
+-----------------------------------------------------------------------------*/
+    pixel [0] = tuples [row][col][0];
+    pixel [1] = tuples [row][col][1];
+    pixel [2] = tuples [row][col][2];
+}
+
+
+static bool
+andMakesOpaque(const struct pam * const pamP,
+               tuple **           const tuples,
+               unsigned int       const row,
+               unsigned int       const col,
+               bool               const haveAlpha,
+               unsigned int       const alphaPlane,
+               bool               const haveAnd,
+               unsigned int       const andPlane) {
+/*----------------------------------------------------------------------------
+   The AND mask makes a pixel opaque
+-----------------------------------------------------------------------------*/
+    if (haveAnd)
+        return (pamP->maxval <= tuples[row][col][andPlane]);
+    else if (haveAlpha)
+        return (pamP->maxval <= tuples[row][col][alphaPlane]);
+    else
+        /* neither alpha channel nor AND mask: full opacity */
+        return true;
+}
+
+
+
+static void
+getPalette(struct pam *     const pamP,
+           tuple **         const tuples,
+           GetPixelFn *     const getPixel,
+           struct Palette * const paletteP) {
+/*----------------------------------------------------------------------------
+  Create the palette for all the colors in 'tuples'.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    paletteP->colorCt = 0;  /* initial value */
+    paletteP->tooManyColors = false;  /* initial value */
+
+    for (row = 0; pamP->height > row && !paletteP->tooManyColors; ++row) {
+        unsigned int col;
+        for (col = 0; pamP->width > col && !paletteP->tooManyColors; ++col) {
+            sample pixel[3];
+            unsigned int i;
+
+            getPixel(tuples, col, row, pixel);
+
+            for (i = 0; i < paletteP->colorCt; ++i) {
+                if ((paletteP->color[i][0] == pixel[0])
+                    && (paletteP->color[i][1] == pixel[1])
+                    && (paletteP->color[i][2] == pixel[2]))
+                    break;
+            }
+            if (i == paletteP->colorCt) {
+                /* We didn't find the color. */
+                if (paletteP->colorCt >= 256) {
+                    /* Image exceeds the palette capacity */
+                    paletteP->tooManyColors = true;
+                } else {
+                    /* Add the color to the palette */
+                    paletteP->color[paletteP->colorCt][0] = pixel[0];
+                    paletteP->color[paletteP->colorCt][1] = pixel[1];
+                    paletteP->color[paletteP->colorCt][2] = pixel[2];
+
+                    ++paletteP->colorCt;
+                }
+            }
+        }
+    }
+}
+
+
+
+static bool
+realAlphaNeeded(const struct pam * const pamP,
+                tuple **           const tuples,
+                unsigned int       const alphaPlane) {
+/*----------------------------------------------------------------------------
+  A real alpha channel (in contrast to an AND mask) is needed to represent the
+  image in 'tuples', given that 'alphaPlane' is the alpha plane.
+
+  A real alpha channel is needed if any pixel is translucent (neither opaque
+  nor transparent).
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    for (row = 0; row < pamP->height; ++row) {
+        unsigned int col;
+        for (col = 0; col < pamP->width; ++col) {
+            sample const opacity = tuples[row][col][alphaPlane];
+
+            if (opacity != 0 && opacity != pamP->maxval)
+                return true;
+        }
+    }
+    return false;
+}
+
+
+
+static void
+writeBmpImageHeader(unsigned int const width,
+                    unsigned int const height,
+                    unsigned int const bpp,
+                    unsigned int const rasterSize,
+                    FILE *       const ofP) {
+/*----------------------------------------------------------------------------
+
+  Write BMP image header
+    
+  Note: bm_height is sum of rows in XOR mask and AND mask, while
+  image_size is the size of the AND mask only.
+    
+  image_size does not include the sizes of the (optional) palette
+  and the (mandatory) AND mask.
+-----------------------------------------------------------------------------*/
+    pm_writelittlelongu  (ofP, 40);              /* header_size          */
+    pm_writelittlelongu  (ofP, width);           /* bm_width             */
+    pm_writelittlelongu  (ofP, height *2);       /* bm_height            */
+    pm_writelittleshortu (ofP, 1);               /* color_planes         */
+    pm_writelittleshortu (ofP, bpp);             /* bits_per_pixel       */
+    pm_writelittlelongu  (ofP, BI_RGB);          /* compression_method   */
+    pm_writelittlelongu  (ofP, rasterSize);      /* image_size           */
+    pm_writelittlelongu  (ofP, 0);               /* horizontal_resolution*/
+    pm_writelittlelongu  (ofP, 0);               /* vertical_resolution  */
+    pm_writelittlelongu  (ofP, 0);               /* colors_in_palette    */
+    pm_writelittlelongu  (ofP, 0);               /* important_colors     */
+}
+
+
+
+static void
+write32BitBmp(const struct pam *   const pamP,
+              tuple     **         const tuples,
+              GetPixelFn *         const getPixel,
+              bool                 const haveAlpha,
+              unsigned int         const alphaPlane,
+              FILE *               const ofP,
+              uint32_t *           const sizeP) {
+/*----------------------------------------------------------------------------
+  Write a 32-bit BMP encoded image to file *ofP.
+-----------------------------------------------------------------------------*/
+    int row;
+    
+    writeBmpImageHeader(pamP->width, pamP->height, 32, 
+                        pamP->width * 4 * pamP->height, ofP);
+
+    /*  write "XOR mask" */
+    for (row = pamP->height - 1; row >= 0; --row) {
+        unsigned int col;
+        for (col = 0; col < pamP->width; ++col) {
+            sample   pixel[3];
+            uint32_t val;
+
+            getPixel(tuples, col, row, pixel);
+
+            val = ((uint32_t) pixel[PAM_RED_PLANE] << 16)
+                + ((uint32_t) pixel[PAM_GRN_PLANE] <<  8)
+                + ((uint32_t) pixel[PAM_BLU_PLANE] <<  0)
+                ;
+            
+            if (haveAlpha)
+                val += (uint32_t) tuples[row][col][alphaPlane] << 24;
+
+            pm_writelittlelongu(ofP, val);
+       }
+    }
+    *sizeP = 40 + pamP->height * pamP->width * 4;
+}
+
+
+
+static void
+writeBmpPalette(const struct Palette * const paletteP,
+                unsigned int           const maxColors,
+                FILE *                 const ofP) {
+/*----------------------------------------------------------------------------
+   Write the palette of a BMP image.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+
+    for (i = 0; i < paletteP->colorCt; ++i)
+        pm_writelittlelongu(ofP, 0
+                            +(paletteP->color[i][PAM_RED_PLANE] << 16)
+                            +(paletteP->color[i][PAM_GRN_PLANE] <<  8)
+                            +(paletteP->color[i][PAM_BLU_PLANE] <<  0));
+    
+    for (; i < maxColors; ++i)
+        pm_writelittlelongu(ofP, 0);
+}
+
+
+
+static void
+writeXorMask(const struct pam *     const pamP,
+             tuple **               const tuples,
+             GetPixelFn *           const getPixel,
+             const struct Palette * const paletteP,
+             unsigned int           const bpp,
+             FILE *                 const ofP) {
+/*----------------------------------------------------------------------------
+   Write the "XOR mask" part of a BMP image.
+
+   This is what one normally thinks of as the foreground image raster.
+-----------------------------------------------------------------------------*/
+    unsigned int const maxCol = ((pamP->width * bpp + 31) & ~31) / bpp;
+
+    int row;
+                 
+    for (row = pamP->height - 1; row >= 0; --row) {
+        uint8_t  val;
+        uint16_t mask;
+        unsigned int col;
+
+        mask = 0x1;
+        val  = 0x0;
+
+        for (col = 0; col < pamP->width; ++col) {
+            sample pixel[3];
+            unsigned int i;
+
+            mask <<= bpp;
+            val  <<= bpp;
+
+            getPixel(tuples, col, row, pixel);
+
+            for (i = 0; i < paletteP->colorCt; ++i)
+                if (true
+                    && (pixel[0] == paletteP->color[i][0])
+                    && (pixel[1] == paletteP->color[i][1])
+                    && (pixel[2] == paletteP->color[i][2]))
+                    break;
+
+            assert(i < paletteP->colorCt);
+
+            val |= i;
+
+            if (mask > 0xFF) {
+                pm_writecharu(ofP, val);
+                mask = 0x1;
+                val  = 0x0;
+            }
+        }
+        for (; col < maxCol; ++col) {
+            mask <<= bpp;
+            val  <<= bpp;
+
+            if (mask > 0xFF) {
+                pm_writecharu(ofP, val);
+                mask = 0x1;
+            }
+        }
+    }
+}
+
+
+
+static void
+writePaletteBmp(unsigned int           const bpp,
+                const struct pam   *   const pamP,
+                tuple **               const tuples,
+                GetPixelFn *           const getPixel,
+                const struct Palette * const paletteP,
+                FILE *                 const ofP,
+                uint32_t *             const sizeP) {
+/*----------------------------------------------------------------------------
+  Write a `BMP with palette' encoded image to file *ofP.
+
+  Unless it would be smaller as a 32-bit direct image, in which case
+  write that instead.
+-----------------------------------------------------------------------------*/
+    unsigned int const maxColors = 1 << bpp;
+
+    unsigned int const rasterSize =
+        pamP->height *((pamP->width * bpp + 31) & ~31) / 8;
+
+    if (pamP->height * pamP->width * 4 <= maxColors * 4 + rasterSize)
+        write32BitBmp(pamP, tuples, getPixel, false /*haveAlpha*/, 0,
+                      ofP, sizeP);
+    else {
+        unsigned int const headerSize = 40;
+        unsigned int const paletteSize = maxColors * 4;
+
+        writeBmpImageHeader(pamP->width, pamP->height, bpp, rasterSize, ofP);
+
+        writeBmpPalette(paletteP, maxColors, ofP);
+
+        writeXorMask(pamP, tuples, getPixel, paletteP, bpp, ofP);
+
+        *sizeP = headerSize + paletteSize + rasterSize;
+    }
+}
+
+
+
+static void
+writeAndMask(const struct pam * const pamP,
+             tuple     **       const tuples,
+             bool               const haveAlpha,
+             unsigned int       const alphaPlane,
+             bool               const haveAnd,
+             unsigned int       const andPlane,
+             FILE *             const ofP,
+             uint32_t *         const sizeP) {
+/*----------------------------------------------------------------------------
+  Write the AND mask to file *ofP.
+-----------------------------------------------------------------------------*/
+    unsigned int const maxCol =((pamP->width * 1 + 31) & ~31) / 1;
+
+    int row;
+    unsigned int sizeSoFar;
+
+    sizeSoFar = 0;
+
+    for (row = pamP->height - 1; row >= 0; --row) {
+        uint8_t  val;
+        uint16_t mask;
+        unsigned int col;
+
+        mask = 0x1;
+        val  = 0x0;
+
+        for (col = 0; col < pamP->width; ++col) {
+            mask <<= 1;
+            val  <<= 1;
+
+            if (!andMakesOpaque(pamP, tuples, row, col, 
+                                haveAlpha, alphaPlane, haveAnd, andPlane))
+                val |= 0x1;
+
+            if (mask > 0xFF) {
+                pm_writecharu(ofP, val);
+                sizeSoFar += 1;
+                mask = 0x1;
+                val  = 0x0;
+            }
+        }
+        for (; col < maxCol; ++col) {
+            mask <<= 1;
+            val  <<= 1;
+
+            if (mask > 0xFF){
+                pm_writecharu(ofP, val);
+                sizeSoFar += 1;
+                mask = 0x1;
+            }
+        }
+    }
+    *sizeP = sizeSoFar;
+}
+
+
+
+static void
+makeAlphaFile(const struct pam * const imagePamP,
+              tuple **           const imageTuples,
+              unsigned int       const alphaPlane,
+              const char **      const alphaFileNameP) {
+
+    FILE * alphaFileP;
+    struct pam alphaPam;
+    tuple ** alphaTuples;
+    unsigned int row;
+    
+    pm_make_tmpfile(&alphaFileP, alphaFileNameP);
+
+    alphaPam.size   = sizeof(alphaPam);
+    alphaPam.len    = PAM_STRUCT_SIZE(tuple_type);
+    alphaPam.file   = alphaFileP;
+    alphaPam.format = PAM_FORMAT;
+    alphaPam.width  = imagePamP->width;
+    alphaPam.height = imagePamP->height;
+    alphaPam.depth  = 1;
+    alphaPam.maxval = imagePamP->maxval;
+    strcpy(alphaPam.tuple_type, PAM_PGM_TUPLETYPE);
+
+    alphaTuples = pnm_allocpamarray(&alphaPam);
+
+    assert(alphaPlane < imagePamP->depth);
+
+    for (row = 0; row < alphaPam.height; ++row) {
+        unsigned int col;
+        for (col = 0; col < alphaPam.width; ++col)
+            alphaTuples[row][col][0] = imageTuples[row][col][alphaPlane];
+    }
+
+    pnm_writepam(&alphaPam, alphaTuples);
+
+    pnm_freepamarray(alphaTuples, &alphaPam);
+
+    pm_close(alphaFileP);
+}
+
+
+
+struct AcceptToFileParm {
+    FILE *   ofP;
+    size_t * writeCtP;
+};
+
+static void
+acceptToFile(int    const pipeToSuckFd,
+             void * const accepterParm) {
+
+    struct AcceptToFileParm * const parmP = accepterParm;
+
+    FILE * const inFileP = fdopen(pipeToSuckFd, "r");
+
+    bool eof;
+    size_t copyCt;
+
+    for (eof = false, copyCt = 0; !eof; ) {
+        size_t readCt;
+        unsigned char buffer[1024];
+
+        readCt = fread(buffer, 1, sizeof(buffer), inFileP);
+
+        if (readCt == 0)
+            eof = true;
+        else {
+            size_t writeCt;
+
+            writeCt = fwrite(buffer, 1, readCt, parmP->ofP);
+
+            if (writeCt != readCt)
+                pm_error("Write to images file failed.  errno=%d (%s)",
+                         errno, strerror(errno));
+
+            copyCt += writeCt;
+        }
+    }
+    *parmP->writeCtP = copyCt;
+}
+
+
+
+static void
+writePng(const struct pam * const pamP,
+         tuple **           const tuples,
+         bool               const haveAlpha,
+         unsigned int       const alphaPlane,
+         bool               const haveAnd,
+         unsigned int       const andPlane,
+         uint32_t *         const sizeP,
+         FILE *             const ofP) {
+
+    struct pamtuples pamTuples;
+    size_t pngSize;
+    struct AcceptToFileParm acceptParm;
+    struct pam pam;
+
+    pam = *pamP;
+    pam.depth = pamP->depth - (haveAlpha ? 1 : 0) - (haveAnd ? 1 : 0);
+
+    pamTuples.pamP    = &pam;
+    pamTuples.tuplesP = (tuple ***)&tuples;
+
+    /* We're going to fork a process to add stuff to *ofP, so we flush
+       out this process' previous writes to that file first:
+    */
+    fflush(ofP);
+
+    acceptParm.ofP = ofP;
+    acceptParm.writeCtP = &pngSize;
+    
+    if (haveAlpha || haveAnd) {
+        const char * alphaFileName;
+        const char * command;
+
+        if (haveAlpha)
+            makeAlphaFile(pamP, tuples, alphaPlane, &alphaFileName);
+        else
+            makeAlphaFile(pamP, tuples, andPlane, &alphaFileName);
+
+        strcpy (pam.tuple_type,
+                pam.depth == 3 ? PAM_PPM_TUPLETYPE: PAM_PGM_TUPLETYPE);
+        
+        pm_asprintf(&command, "pnmtopng -alpha=\"%s\"", alphaFileName);
+
+        pm_system(pm_feed_from_pamtuples, &pamTuples,
+                  acceptToFile, &acceptParm,
+                  command);
+    
+        pm_strfree(command);
+    
+        unlink(alphaFileName);
+    } else {
+        pm_system(pm_feed_from_pamtuples, &pamTuples,
+                  acceptToFile, &acceptParm,
+                  "pnmtopng");
+    }
+
+    *sizeP = pngSize;
+}
+
+
+
+static void
+blackenXor(const struct pam * const pamP,
+           tuple     **       const tuples,
+           bool               const haveAlpha,
+           unsigned int       const alphaPlane,
+           bool               const haveAnd,
+           unsigned int       const andPlane) {
+/*----------------------------------------------------------------------------
+  Set all pixels marked as transparent in AND mask to black.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    for (row = 0; row < pamP->height; ++row) {
+        unsigned int col;
+        for (col = 0; col < pamP->width; ++col) {
+            if (!andMakesOpaque(pamP, tuples, row, col,
+                                haveAlpha, alphaPlane, haveAnd, andPlane)) {
+                tuples[row][col][0] = PAM_BLACK;
+
+                if (pamP->depth >= 3) {
+                    tuples[row][col][1] = PAM_BLACK;
+                    tuples[row][col][2] = PAM_BLACK;
+                }
+            }
+        }
+    }
+}
+
+
+
+
+static void
+readAndScalePam(struct pam * const pamP,
+                bool         const doingPng,
+                tuple **     const tuples) {
+
+    if (doingPng) {
+        /* Read the image with its native maxval */
+        unsigned int row;
+        for (row = 0; row < pamP->height; ++row)
+            pnm_readpamrow(pamP, tuples[row]);
+    } else {
+        /* Read the image and scale to maxval 255 */
+        tuple * tuplerow;
+        unsigned int row;
+        tuplerow = pnm_allocpamrow(pamP);
+
+        for (row = 0; row < pamP->height; ++row) {
+            pnm_readpamrow(pamP, tuplerow);
+            pnm_scaletuplerow(pamP, tuples[row], tuplerow, 255);
+        }
+        pnm_freepamrow(tuplerow);
+        pamP->maxval = 255;
+    }
+}
+
+
+
+static void
+determineImageType(const struct pam * const pamP,
+                   tuple **           const tuples,
+                   GetPixelFn **      const getPixelP,
+                   bool *             const haveAlphaP,
+                   unsigned int *     const alphaPlaneP,
+                   bool *             const haveAndP,
+                   unsigned int *     const andPlaneP) {
+
+    /*  PAM input channels:
+     *
+     *  1-channel PAM: Grayscale
+     *  2-channel PAM: Grayscale +Alpha
+     *  3-channel PAM: RGB
+     *  4-channel PAM: RGB +Alpha
+     *  5-channel PAM: RGB +Alpha +AND mask
+     */
+    switch (pamP->depth) {
+    case 1:
+        *getPixelP  = get_grayscalePixel;
+        *haveAlphaP = false;
+        *haveAndP   = false;
+        break;
+
+    case 2:
+        *getPixelP = get_grayscalePixel;
+        if (realAlphaNeeded(pamP, tuples, 1)) {
+            *haveAlphaP  = true;
+            *alphaPlaneP = 1;
+            *haveAndP    = false;
+        } else {
+            *haveAlphaP = false;
+            *haveAndP   = true;
+            *andPlaneP  = 1;
+        }
+        break;
+
+    case 3:
+        *getPixelP  = get_rgbPixel;
+        *haveAlphaP = false;
+        *haveAndP   = false;
+        break;
+
+    case 4:
+        *getPixelP = get_rgbPixel;
+        if (realAlphaNeeded(pamP, tuples, 3)) {
+            *haveAlphaP  = true;
+            *alphaPlaneP = 3;
+            *haveAndP    = false;
+        } else {
+            *haveAlphaP = false;
+            *haveAndP    = true;
+            *andPlaneP  = 3;
+        }
+        break;
+
+    case 5:
+        *getPixelP   = get_rgbPixel;
+        *haveAlphaP  = true;
+        *alphaPlaneP = 3;
+        *haveAndP    = true;
+        *andPlaneP   = 4;
+        break;
+
+    default:
+        pm_error("unexpected PAM depth %u.  "
+                 "We understand depths 1-5", pamP->depth);
+        break;
+    }
+}
+
+
+
+static void
+reportImageInfo(unsigned int           const imageNum,
+                const struct pam *     const pamP,
+                const struct Palette * const paletteP,
+                bool                   const haveAlpha,
+                bool                   const haveAnd) {
+
+    const char * colorCt;
+
+    if (paletteP->tooManyColors)
+        pm_asprintf(&colorCt, "> 256");
+    else
+        pm_asprintf(&colorCt, "%u", paletteP->colorCt);
+
+    pm_message("Image %2u:"
+               " %3u x %3u x %u, %s colors%s%s",
+               imageNum,
+               pamP->width, pamP->height, pamP->depth,
+               colorCt,
+               haveAlpha ? ", alpha channel": "",
+               haveAnd ? ", AND mask": "");
+
+    pm_strfree(colorCt);
+}
+
+
+
+static void
+writeIconAndCreateDirEntry(const struct pam *     const pamP,
+                           tuple **               const tuples,
+                           GetPixelFn *           const getPixel,
+                           bool                   const doingPng,
+                           bool                   const haveAlpha,
+                           unsigned int           const alphaPlane,
+                           bool                   const haveAnd,
+                           unsigned int           const andPlane,
+                           bool                   const mustBlackenXor,
+                           const struct Palette * const paletteP,
+                           FILE *                 const ofP,
+                           struct IconDirEntry *  const dirEntryP) {
+/*----------------------------------------------------------------------------
+   Write to *ofP the icon image for the image represented by *pamP and
+   'tuples'.
+
+   Generate the information for an icon directory entry for this image
+   and return it as *dirEntryP.  ==>BUT: the 'offset' member of this
+   structure will not be meaningful. <==
+
+   Make a PNG image if 'doingPng' is true; BMP otherwise.
+
+   'haveAlpha' means that there is an alpha plane in 'tuples' and it is
+   Plane 'alphaPlane'.
+
+   'haveAnd' means that there is an AND plane in 'tuples' and it is Plane
+   'andPlane'.
+
+   *paletteP is the color palette for the icon; it contains an entry for each
+   color in 'tuples'.  Except: it may simply indicate that there are too many
+   colors in 'tuples' to have a palette.
+
+   The 'bits_per_pixel' member of the directory entry is supposed to tell the
+   color resolution of the image so the user can decide which of many versions
+   of the icon in the file to use.  But we just call it 32 bits in every case
+   except paletted BMP, where it actually relates to how many colors are in
+   the image.
+-----------------------------------------------------------------------------*/
+    dirEntryP->width          = pamP->width;
+    dirEntryP->height         = pamP->height;
+    dirEntryP->color_planes   = 1;
+    dirEntryP->zero           = 0;
+
+    if (doingPng) {
+        dirEntryP->color_count    = 0;
+        dirEntryP->bits_per_pixel = 32;
+
+        writePng(pamP, tuples, haveAlpha, alphaPlane, haveAnd, andPlane,
+                 &dirEntryP->size, ofP);
+    } else {
+        uint32_t bmpSize;
+        uint32_t andMaskSize;
+
+        if (mustBlackenXor)
+            blackenXor(pamP, tuples,
+                       haveAlpha, alphaPlane, haveAnd, andPlane);
+
+        if (haveAlpha) {
+            dirEntryP->color_count    = 0;
+            dirEntryP->bits_per_pixel = 32;
+
+            write32BitBmp(pamP, tuples, getPixel, haveAlpha, alphaPlane,
+                          ofP, &bmpSize);
+        } else if (paletteP->tooManyColors) {
+            /* Do a truecolor image */
+            dirEntryP->color_count    = 0;
+            dirEntryP->bits_per_pixel = 32;
+
+            write32BitBmp(pamP, tuples, getPixel, false /*haveAlpha*/, 0,
+                          ofP, &bmpSize);
+        } else {
+            /* Do a paletted image */
+
+            if (paletteP->colorCt <= 2) {
+                dirEntryP->color_count    = paletteP->colorCt;
+                dirEntryP->bits_per_pixel = 1;
+                    
+                writePaletteBmp(1, pamP, tuples, getPixel, paletteP,
+                                ofP, &bmpSize);
+            } else if (paletteP->colorCt <= 16) {
+                dirEntryP->color_count    = paletteP->colorCt;
+                dirEntryP->bits_per_pixel = 4;
+                    
+                writePaletteBmp(4, pamP, tuples, getPixel,paletteP,
+                                ofP, &bmpSize);
+            } else {
+                dirEntryP->color_count    = 0;
+                dirEntryP->bits_per_pixel = 8;
+                    
+                writePaletteBmp(8, pamP, tuples, getPixel, paletteP,
+                                ofP, &bmpSize);
+            }
+        }
+        writeAndMask(pamP, tuples, haveAlpha, alphaPlane, haveAnd, andPlane,
+                     ofP, &andMaskSize);
+
+        dirEntryP->size = bmpSize + andMaskSize;
+    }
+}
+
+
+
+static void
+convertOneImage(unsigned int     const imageNum,
+                FILE *           const ifP,
+                unsigned int     const pngThreshold,
+                bool             const mustBlackenXor,
+                FILE *           const ofP,
+                struct IconDir * const dirP) {
+
+    struct IconDirEntry dirEntry;
+    struct pam          pam;
+    tuple **            tuples;
+    bool                haveAlpha;
+    unsigned int        alphaPlane;
+    bool                haveAnd;
+    unsigned int        andPlane;
+    GetPixelFn *        getPixel;
+    struct Palette      palette;
+    bool                doingPng;
+        
+    /*  Output:
+     *
+     *  threshold^2 pixels or more:
+     *      no alpha channel:        PNG (RGB)
+     *      alpha channel:           PNG (RGBA)
+     *      alpha channel +AND mask: PNG (RGBA), AND mask dropped
+     *  alpha values other than 0 and maxval: 32bit +alpha BMP
+     *  no more than   2 colors:  1bit or 32bit BMP
+     *  no more than  16 colors:  4bit or 32bit BMP
+     *  no more than 256 colors:  8bit or 32bit BMP
+     *  more than    256 colors: 32bit BMP
+     */
+    pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
+
+    if (pam.width > 256 || pam.height > 256)
+        pm_error("Image %u: too large as a windows icon (%u x %u).  "
+                 "Maximum allowed dimension is 256",
+                 imageNum, pam.width, pam.height);
+
+    tuples = pnm_allocpamarray(&pam);
+
+    doingPng = pam.width * pam.height >= pngThreshold;
+
+    readAndScalePam(&pam, doingPng, tuples);
+
+    determineImageType(&pam, tuples, &getPixel,
+                       &haveAlpha, &alphaPlane, &haveAnd, &andPlane);
+
+    getPalette(&pam, tuples, getPixel, &palette);
+
+    if (verbose)
+        reportImageInfo(imageNum, &pam, &palette, haveAlpha, haveAnd);
+
+    writeIconAndCreateDirEntry(&pam, tuples, getPixel, doingPng,
+                               haveAlpha, alphaPlane,
+                               haveAnd, andPlane,
+                               mustBlackenXor,
+                               &palette,
+                               ofP, &dirEntry);
+
+    if (verbose)
+        pm_message("Image %2u: %u bytes", imageNum, dirEntry.size);
+
+    pnm_freepamarray(tuples, &pam);
+
+    addToDirectory(&dirEntry, dirP);
+}
+
+
+
+static void
+convert(FILE *           const ifP,
+        unsigned int     const pngThreshold,
+        bool             const mustBlackenXor,
+        struct IconDir * const dirP,
+        FILE *           const ofP) {
+/*----------------------------------------------------------------------------
+  Read a (multi-image) PAM file from *ifP and convert the individual images
+  to the proper format for a Windows icon file and write those to *ofP.
+
+  Where the number of pixels in an image is at least 'pngThreshold', use
+  a PNG image.  Otherwise, use a BMP.
+-----------------------------------------------------------------------------*/
+    unsigned int imageNum;
+    int eof;
+    
+    for (imageNum = 0, eof = false; !eof; ++imageNum) {
+        convertOneImage(imageNum, ifP, pngThreshold, mustBlackenXor,
+                        ofP, dirP);
+
+        pnm_nextimage(ifP, &eof);
+    }
+}
+
+
+
+static void
+writeIconDirEntry(const struct IconDirEntry * const dirEntryP,
+                  FILE *                      const ofP) {
+        
+    pm_writecharu        (ofP, dirEntryP->width);
+    pm_writecharu        (ofP, dirEntryP->height);
+    pm_writecharu        (ofP, dirEntryP->color_count);
+    pm_writecharu        (ofP, dirEntryP->zero);
+    pm_writelittleshortu (ofP, dirEntryP->color_planes);
+    pm_writelittleshortu (ofP, dirEntryP->bits_per_pixel);
+    pm_writelittlelongu  (ofP, dirEntryP->size); 
+    pm_writelittlelongu  (ofP, dirEntryP->offset);
+}
+
+
+
+static void
+writeIconDirectory(const struct IconDir * const dirP,
+                   FILE *                 const ofP) {
+/*----------------------------------------------------------------------------
+  Write to file *ofP the icon directory described by *dirP.
+
+  *dirP's image offset members are meaningless as input.  We fill them in.
+-----------------------------------------------------------------------------*/
+    uint32_t const hsize = 6 + dirP->count * 16;
+
+    unsigned int imageNum;
+    unsigned int imageOffset;
+
+    pm_writelittleshortu(ofP, dirP->zero);
+    pm_writelittleshortu(ofP, dirP->type);
+    pm_writelittleshortu(ofP, dirP->count);
+
+    for (imageNum = 0, imageOffset = hsize;
+         imageNum < dirP->count;
+         ++imageNum) {
+
+        struct IconDirEntry * const dirEntryP = &dirP->entries[imageNum];
+        
+        pm_message("image %2u: %3u x %3u x %2u",
+                   imageNum,
+                   dirEntryP->width,
+                   dirEntryP->height,
+                   dirEntryP->bits_per_pixel);
+
+        dirEntryP->offset = imageOffset;
+
+        writeIconDirEntry(dirEntryP, ofP);
+
+        imageOffset += dirEntryP->size;
+    }
+}
+
+
+
+static void
+copyFile(FILE * const ifP,
+         FILE * const ofP) {
+
+    bool eof;
+    
+    for (eof = false; !eof; ) {
+        unsigned char buffer[1024];
+        size_t bytesRead;
+
+        bytesRead = fread(buffer, 1, sizeof(buffer), ifP);
+
+        if (bytesRead == 0)
+            eof = true;
+        else {
+            size_t bytesWritten;
+
+            bytesWritten = fwrite(buffer, 1, bytesRead, ofP);
+
+            if (bytesWritten < bytesRead)
+                pm_error("Error writing to output file.");
+        }
+    }
+}
+
+
+
+static void
+writeIconFile(const struct IconDir * const dirP,
+              FILE *                 const imagesFileP,
+              FILE *                 const ofP) {
+/*----------------------------------------------------------------------------
+  Write a windows icon file.
+
+  *dirP is the icon directory to put in it.
+
+  *imagesFileP contains all the text of the icon images.  The contents of
+  this file go verbatim into the output.
+-----------------------------------------------------------------------------*/
+    writeIconDirectory(dirP, ofP);
+
+    copyFile(imagesFileP, ofP);
+}
+
+
+
+int
+main(int argc, const char *argv []) {
+
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    FILE * imagesFileP;
+        /* This is the file in which we collect the individual icon
+           images to be copied later to the output.
+        */
+    struct IconDir * dirP;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+
+    /* The output icon file has directory information at the top that we
+       can't know until we have looked at the input images.  So as we pass
+       through the input, we collect the directory information and generate
+       the individual icon images and store them in *imageFileP.  When we've
+       been through all of the input, we write out the directory and then
+       copy the images from *imageFileP to the output.
+    */
+
+    dirP = newIconDir();
+
+    imagesFileP = pm_tmpfile();
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    convert(ifP, SQR(cmdline.pngthreshold), cmdline.truetransparent,
+            dirP, imagesFileP);
+
+    rewind(imagesFileP);
+
+    writeIconFile(dirP, imagesFileP, stdout);
+
+    freeIconDir(dirP);
+
+    return 0;
+}
+
+
+
diff --git a/converter/other/pamtoxvmini.c b/converter/other/pamtoxvmini.c
index e1aa9b52..b57bcc74 100644
--- a/converter/other/pamtoxvmini.c
+++ b/converter/other/pamtoxvmini.c
@@ -152,14 +152,14 @@ getPaletteIndexThroughCache(struct pam *      const pamP,
    If the tuple-index association is in *paletteIndexP, use it.  If not,
    find it the hard way and add it to *palettedIndexP for the next guy.
 -----------------------------------------------------------------------------*/
-    bool found;
+    int found;
     int paletteIndex;
 
     pnm_lookuptuple(pamP, paletteHash, tuple, &found, &paletteIndex);
     if (found)
         *paletteIndexP = paletteIndex;
     else {
-        bool fits;
+        int fits;
         findClosestColor(pamP, tuple, xvPaletteP, paletteIndexP);
         
         pnm_addtotuplehash(pamP, paletteHash, tuple, *paletteIndexP, &fits);
diff --git a/converter/other/pdbimgtopam.c b/converter/other/pdbimgtopam.c
new file mode 100644
index 00000000..3cb4129a
--- /dev/null
+++ b/converter/other/pdbimgtopam.c
@@ -0,0 +1,774 @@
+/*=============================================================================
+                               pamtopdbimg
+===============================================================================
+
+  Convert Palm Pilot PDB Image format (for viewing by
+  Pilot Image Viewer) to Netpbm image.
+
+  Bryan Henderson derived this from Eric Howe's program named
+  'imgvtopnm', in September 2010.
+=============================================================================*/
+/*
+ * Copyright (C) 1997 Eric A. Howe
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *   Authors:  Eric A. Howe (mu@trends.net)
+ *             Bryan Henderson
+ */
+#include <stdlib.h>
+#include <assert.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+
+#include "ipdb.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;  /* '-' if stdin */
+    const char * notefile;  /* NULL if not specified */
+    unsigned int verbose;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int notefileSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "notefile",            OPT_STRING,    &cmdlineP->notefile,
+            &notefileSpec,            0);
+    OPTENT3(0, "verbose",             OPT_FLAG,    NULL,
+            &cmdlineP->verbose,       0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = false;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = false;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!notefileSpec)
+        cmdlineP->notefile = NULL;
+    
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFileName = argv[1];
+    else
+        pm_error("Program takes at most one argument:  input file name");
+}
+
+
+
+#define getg16pixel(b,o)    (((b) >> (4 - 4*(o))) & 0x0f)
+#define getgpixel(b,o)      (((b) >> (6 - 2*(o))) & 0x03)
+#define getmpixel(b,o)      (((b) >> (7 - (o))) & 0x01)
+
+
+static void
+abortShort() {
+    pm_error("Invalid image.  Compression algorithm runs out of "
+             "compressed data before generating the expected "
+             "amount of image data");
+}
+
+
+
+static void
+abortOverrun() {
+    pm_error("Invalid image.  Compression algorithm finds the end of "
+             "the image in the middle of a run");
+}
+
+
+
+static void
+decompress(const uint8_t * const compressed,
+           size_t          const compressedSize,
+           size_t          const imageSize,
+           uint8_t **      const uncompressedP) {
+/*----------------------------------------------------------------------------
+   Decompress the data 'compressed', which is 'compressedSize' bytes long.
+   Return the decompressed data in newly malloced storage as
+   *decompressedP.  Decompression should yield exactly 'imageSize' bytes.
+-----------------------------------------------------------------------------*/
+    /*
+     * The compression scheme used is a simple RLE; the control codes,
+     * CODE, are one byte and have the following meanings:
+     *
+     *  CODE >  0x80    Insert (CODE + 1 - 0x80) copies of the next byte.
+     *  CODE <= 0x80    Insert the next (CODE + 1) literal bytes.
+     *
+     * Compressed pieces can (and do) cross row boundaries.
+     */
+    uint8_t * uncompressed;
+
+    MALLOCARRAY(uncompressed, imageSize);
+
+    if (uncompressed) {
+        const uint8_t * inP;
+        uint8_t *       outP;
+        size_t          bytesLeft;
+        
+        for (bytesLeft = imageSize,
+                 inP  = &compressed[0], outP = &uncompressed[0];
+             bytesLeft > 0;
+            ) {
+
+            int got, put;
+
+            if (inP > compressed + compressedSize)
+                abortShort();
+
+            if (*inP > 0x80) {
+                put = *inP++ + 1 - 0x80;
+                if (outP + put > uncompressed + imageSize)
+                    abortOverrun();
+                memset(outP, *inP, put);
+                got = 1;
+            } else {
+                put = *inP++ + 1;
+                if (inP + put > compressed + compressedSize)
+                    abortShort();
+                if (outP + put > uncompressed + imageSize)
+                    abortOverrun();
+                memcpy(outP, inP, put);
+                got = put;
+            }
+            inP       += got;
+            outP      += put;
+            assert(bytesLeft >= put);
+            bytesLeft -= put;
+        }
+    }
+    *uncompressedP = uncompressed;
+}
+
+
+
+#define UNKNOWN_OFFSET  (uint32_t)-1
+
+static void
+readCompressed(IMAGE *    const imgP,
+               uint32_t   const end_offset,
+               FILE *     const fP,
+               size_t *   const dataSizeP,
+               uint8_t ** const dataP,
+               int *      const retvalP) {
+/*----------------------------------------------------------------------------
+   Read the compressed data from file *fP (actually, if the image isn't
+   compressed, then it's just the regular data).
+
+   Return the data in newly malloced storage as *dataP, which is
+   *dataSizeP bytes long.
+-----------------------------------------------------------------------------*/
+    int retval;
+    uint8_t * buffer;
+    size_t dataSize;
+
+    dataSize = 0;  /* initial value */
+
+    if (end_offset == UNKNOWN_OFFSET) {
+        /*
+         * Read until EOF. Some of them have an extra zero byte
+         * dangling off the end.  I originally thought this was
+         * an empty note record (even though there was no record
+         * header for it); however, the release notes for Image
+         * Compression Manager 1.1 on http://www.pilotgear.com
+         * note this extra byte as a bug in Image Compression
+         * Manager 1.0 which 1.1 fixes.  We'll just blindly read
+         * this extra byte and ignore it by paying attention to
+         * the image dimensions.
+         */
+        MALLOCARRAY(buffer, ipdb_img_size(imgP));
+
+        if (buffer == NULL)
+            retval = ENOMEM;
+        else {
+            dataSize = fread(buffer, 1, ipdb_img_size(imgP), fP);
+            if (dataSize <= 0)
+                retval = EIO;
+            else
+                retval = 0;
+
+            if (retval != 0)
+                free(buffer);
+        }
+    } else {
+        /*
+         * Read to the indicated offset.
+         */
+        dataSize = end_offset - ftell(fP) + 1;
+        
+        MALLOCARRAY(buffer, dataSize);
+
+        if (buffer == NULL)
+            retval = ENOMEM;
+        else {
+            ssize_t rc;
+            rc = fread(buffer, 1, dataSize, fP);
+            if (rc != dataSize)
+                retval = EIO;
+            else
+                retval = 0;
+
+            if (retval != 0)
+                free(buffer);
+        }
+    }
+    *dataSizeP = dataSize;
+    *dataP = buffer;
+    *retvalP = retval;
+}
+
+
+
+static void
+imageReadHeader(FILE *  const fileP,
+                IMAGE * const imgP,
+                bool    const dump) {
+
+    fread(&imgP->name, 1, 32, fileP);
+    pm_readcharu(fileP, &imgP->version);
+    pm_readcharu(fileP, &imgP->type);
+    fread(&imgP->reserved1, 1, 4, fileP);
+    fread(&imgP->note, 1, 4, fileP);
+    pm_readbigshortu(fileP, &imgP->x_last);
+    pm_readbigshortu(fileP, &imgP->y_last);
+    fread(&imgP->reserved2, 1, 4, fileP);
+    pm_readbigshortu(fileP, &imgP->x_anchor);
+    pm_readbigshortu(fileP, &imgP->y_anchor);
+    pm_readbigshortu(fileP, &imgP->width);
+    pm_readbigshortu(fileP, &imgP->height);
+
+    if (dump) {
+        pm_message("PDB IMAGE header:");
+        pm_message("  Name: '%.*s'", (int)sizeof(imgP->name), imgP->name);
+        pm_message("  Version: %02x", imgP->version);
+        pm_message("  Type: %s", ipdb_typeName(imgP->type));
+        pm_message("  Note: %02x %02x %02x %02x",
+                   imgP->note[0], imgP->note[1], imgP->note[2], imgP->note[3]);
+        pm_message("  X_last: %u", imgP->x_last);
+        pm_message("  Y_last: %u", imgP->y_last);
+        pm_message("  X_anchor: %u", imgP->x_anchor);
+        pm_message("  Y_anchor: %u", imgP->y_anchor);
+        pm_message("  Width: %u", imgP->width);
+        pm_message("  Height: %u", imgP->height);
+        pm_message("Pixels per byte: %u", ipdb_img_ppb(imgP));
+        pm_message("Image size: %lu bytes",
+                   (unsigned long)ipdb_img_size(imgP));
+    }
+}
+
+
+static int
+imageReadData(FILE *   const fileP,
+              IMAGE *  const imgP,
+              uint32_t const end_offset) {
+
+    int retval;
+    size_t dataSize;
+    uint8_t * buffer;
+
+    readCompressed(imgP, end_offset, fileP, &dataSize, &buffer, &retval);
+
+    if (retval == 0) {
+        /*
+         * Compressed data can cross row boundaries so we decompress
+         * the data here to avoid messiness in the row access functions.
+         */
+        if (dataSize != ipdb_img_size(imgP)) {
+            decompress(buffer, dataSize, ipdb_img_size(imgP), &imgP->data);
+            if (imgP->data == NULL)
+                retval = ENOMEM;
+            else
+                imgP->compressed = true;
+            free(buffer);
+        } else {
+            imgP->compressed = false;
+            imgP->data       = buffer;
+            /* Storage at 'buffer' now belongs to *imgP */
+        }
+    }
+    return retval;
+}
+
+
+
+static int
+imageRead(IMAGE *  const imgP,
+          uint32_t const end_offset,
+          FILE *   const fileP,
+          bool     const verbose) {
+
+    if (imgP) {
+        imgP->r->offset = (uint32_t)ftell(fileP);
+
+        imageReadHeader(fileP, imgP, verbose);
+
+        imageReadData(fileP, imgP, end_offset);
+    }
+    return 0;
+}
+
+
+
+static int
+textRead(TEXT * const textP,
+         FILE * const fileP) {
+
+    int retval;
+    char    * s;
+    char    buf[128];
+    int used, alloced, len;
+
+    if (textP == NULL)
+        return 0;
+
+    textP->r->offset = (uint32_t)ftell(fileP);
+    
+    /*
+     * What a pain in the ass!  Why the hell isn't there a length
+     * attached to the text record?  I suppose the designer wasn't
+     * concerned about non-seekable (i.e. pipes) input streams.
+     * Perhaps I'm being a little harsh, the lack of a length probably
+     * isn't much of an issue on the Pilot.
+     */
+    used    = 0;
+    alloced = 0;
+    s       = NULL;
+    retval = 0;  /* initial value */
+    while ((len = fread(buf, 1, sizeof(buf), fileP)) != 0 && retval == 0) {
+        if (buf[len - 1] == '\0')
+            --len;
+        if (used + len > alloced) {
+            alloced += 2 * sizeof(buf);
+            REALLOCARRAY(s, alloced);
+
+            if (s == NULL)
+                retval = ENOMEM;
+        }
+        if (retval == 0) {
+            memcpy(s + used, buf, len);
+            used += len;
+        }
+    }
+    if (retval == 0) {
+        textP->data = calloc(1, used + 1);
+        if (textP->data == NULL)
+            retval = ENOMEM;
+        else
+            memcpy(textP->data, s, used);
+    }
+    if (s)
+        free(s);
+
+    return retval;
+}
+
+
+
+static int
+pdbheadRead(PDBHEAD * const pdbHeadP,
+            FILE *    const fileP) {
+
+    int retval;
+
+    fread(pdbHeadP->name, 1, 32, fileP);
+    pm_readbigshortu(fileP, &pdbHeadP->flags);
+    pm_readbigshortu(fileP, &pdbHeadP->version);
+    pm_readbiglongu2(fileP, &pdbHeadP->ctime);
+    pm_readbiglongu2(fileP, &pdbHeadP->mtime);
+    pm_readbiglongu2(fileP, &pdbHeadP->btime);
+    pm_readbiglongu2(fileP, &pdbHeadP->mod_num);
+    pm_readbiglongu2(fileP, &pdbHeadP->app_info);
+    pm_readbiglongu2(fileP, &pdbHeadP->sort_info);
+    fread(pdbHeadP->type, 1, 4,  fileP);
+    fread(pdbHeadP->id,   1, 4,  fileP);
+    pm_readbiglongu2(fileP, &pdbHeadP->uniq_seed);
+    pm_readbiglongu2(fileP, &pdbHeadP->next_rec);
+    pm_readbigshortu(fileP, &pdbHeadP->num_recs);
+
+    if (!memeq(pdbHeadP->type, IPDB_vIMG, 4) 
+        || !memeq(pdbHeadP->id, IPDB_View, 4))
+        retval = E_NOTIMAGE;
+    else
+        retval = 0;
+
+    return retval;
+}
+
+
+
+static int
+rechdrRead(RECHDR * const rechdrP,
+           FILE *   const fileP) {
+
+    int retval;
+    off_t   len;
+
+    pm_readbiglongu2(fileP, &rechdrP->offset);
+
+    len = (off_t)rechdrP->offset - ftell(fileP);
+    switch(len) {
+    case 4:
+    case 12:
+        /*
+         * Version zero (eight bytes of record header) or version
+         * two with a note (two chunks of eight record header bytes).
+         */
+        fread(&rechdrP->unknown[0], 1, 3, fileP);
+        fread(&rechdrP->rec_type,   1, 1, fileP);
+        rechdrP->n_extra = 0;
+        rechdrP->extra   = NULL;
+        retval = 0;
+        break;
+    case 6:
+        /*
+         * Version one (ten bytes of record header).
+         */
+        fread(&rechdrP->unknown[0], 1, 3, fileP);
+        fread(&rechdrP->rec_type,   1, 1, fileP);
+        rechdrP->n_extra = 2;
+        MALLOCARRAY(rechdrP->extra, rechdrP->n_extra);
+        if (rechdrP->extra == NULL)
+            retval = ENOMEM;
+        else {
+            fread(rechdrP->extra, 1, rechdrP->n_extra, fileP);
+            retval = 0;
+        }
+        break;
+    default:
+        /*
+         * hmmm.... I'll assume this is the record header
+         * for a text record.
+         */
+        fread(&rechdrP->unknown[0], 1, 3, fileP);
+        fread(&rechdrP->rec_type,   1, 1, fileP);
+        rechdrP->n_extra = 0;
+        rechdrP->extra   = NULL;
+        retval = 0;
+        break;
+    }
+    if (retval == 0) {
+        if ((rechdrP->rec_type != IMG_REC && rechdrP->rec_type != TEXT_REC)
+            || !memeq(rechdrP->unknown, IPDB_MYST, 3))
+            retval = E_NOTRECHDR;
+    }
+    return retval;
+}
+
+
+
+static int
+ipdbRead(IPDB * const pdbP,
+         FILE * const fileP,
+         bool   const verbose) {
+
+    int retval;
+
+    ipdb_clear(pdbP);
+
+    pdbP->p = ipdb_pdbhead_alloc(NULL);
+
+    if (pdbP->p == NULL)
+        retval = ENOMEM;
+    else {
+        int status;
+
+        status = pdbheadRead(pdbP->p, fileP);
+
+        if (status != 0)
+            retval = status;
+        else {
+            pdbP->i = ipdb_image_alloc(pdbP->p->name, IMG_GRAY, 0, 0);
+            if (pdbP->i == NULL)
+                retval = ENOMEM;
+            else {
+                int status;
+                status = rechdrRead(pdbP->i->r, fileP);
+                if (status != 0)
+                    retval = status;
+                else {
+                    if (pdbP->p->num_recs > 1) {
+                        pdbP->t = ipdb_text_alloc(NULL);
+                        if (pdbP->t == NULL)
+                            retval = ENOMEM;
+                        else {
+                            int status;
+                            status = rechdrRead(pdbP->t->r, fileP);
+                            if (status != 0)
+                                retval = status;
+                            else
+                                retval = 0;
+                        }
+                    } else
+                        retval = 0;
+                    
+                    if (retval == 0) {
+                        uint32_t const offset =
+                            pdbP->t == NULL ?
+                            UNKNOWN_OFFSET : pdbP->t->r->offset - 1;
+
+                        int status;
+
+                        status = imageRead(pdbP->i, offset, fileP, verbose);
+                        if (status != 0)
+                            retval = status;
+                        else {
+                            if (pdbP->t != NULL) {
+                                int status;
+                                
+                                status = textRead(pdbP->t, fileP);
+                                if (status != 0)
+                                    retval = status;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return retval;
+}
+
+
+
+static void
+g16unpack(const uint8_t * const p,
+          uint8_t *       const g,
+          int             const w) {
+
+    static const uint8_t pal[] =
+        {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
+         0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00};
+    const uint8_t * seg;
+    unsigned int i;
+
+    for (i = 0, seg = p; i < w; i += 2, ++seg) {
+        g[i + 0] = pal[getg16pixel(*seg, 0)];
+        g[i + 1] = pal[getg16pixel(*seg, 1)];
+    }
+}
+
+
+
+static void
+gunpack(const uint8_t * const p,
+        uint8_t *       const g,
+        int             const w) {
+
+    static const uint8_t pal[] = {0xff, 0xaa, 0x55, 0x00};
+    const uint8_t * seg;
+    unsigned int i;
+
+    for (i = 0, seg = p; i < w; i += 4, ++seg) {
+        g[i + 0] = pal[getgpixel(*seg, 0)];
+        g[i + 1] = pal[getgpixel(*seg, 1)];
+        g[i + 2] = pal[getgpixel(*seg, 2)];
+        g[i + 3] = pal[getgpixel(*seg, 3)];
+    }
+}
+
+
+
+static void
+munpack(const uint8_t * const p,
+        uint8_t *       const b,
+        int             const w) {
+
+    static const uint8_t pal[] = {PAM_BW_WHITE, PAM_BLACK};
+    const uint8_t * seg;
+    unsigned int i;
+
+    for (i = 0, seg = p; i < w; i += 8, ++seg) {
+        b[i + 0] = pal[getmpixel(*seg, 0)];
+        b[i + 1] = pal[getmpixel(*seg, 1)];
+        b[i + 2] = pal[getmpixel(*seg, 2)];
+        b[i + 3] = pal[getmpixel(*seg, 3)];
+        b[i + 4] = pal[getmpixel(*seg, 4)];
+        b[i + 5] = pal[getmpixel(*seg, 5)];
+        b[i + 6] = pal[getmpixel(*seg, 6)];
+        b[i + 7] = pal[getmpixel(*seg, 7)];
+    }
+}
+
+
+
+static void
+g16row(IPDB *       const pdbP,
+       unsigned int const row,
+       uint8_t *    const buffer) {
+    
+    g16unpack(ipdb_img_row(pdbP->i, row), buffer, ipdb_width(pdbP));
+}
+
+
+
+static void
+grow(IPDB *       const pdbP,
+     unsigned int const row,
+     uint8_t *    const buffer) {
+
+    gunpack(ipdb_img_row(pdbP->i, row), buffer, ipdb_width(pdbP));
+}
+
+
+
+static void
+mrow(IPDB *       const pdbP,
+     unsigned int const row,
+     uint8_t *    const buffer) {
+
+    munpack(ipdb_img_row(pdbP->i, row), buffer, ipdb_width(pdbP));
+}
+
+
+
+static void
+writeImgPam(IPDB * const pdbP,
+            FILE * const ofP) {
+
+    struct pam pam;
+    tuple * tupleRow;
+    unsigned int row;
+    uint8_t * imgRow;
+
+    MALLOCARRAY(imgRow, ipdb_width(pdbP));
+
+    pam.size             = sizeof(pam);
+    pam.len              = PAM_STRUCT_SIZE(tuple_type);
+    pam.file             = ofP;
+    pam.plainformat      = 0;
+    pam.width            = ipdb_width(pdbP);
+    pam.height           = ipdb_height(pdbP);
+    pam.depth            = 1;
+    pam.maxval           = ipdb_type(pdbP) == IMG_MONO ? 1 : 255;
+    pam.bytes_per_sample = pnm_bytespersample(pam.maxval);
+    pam.format           = PAM_FORMAT;
+    strcpy(pam.tuple_type,
+           ipdb_type(pdbP) == IMG_MONO ?
+           PAM_PBM_TUPLETYPE : PAM_PGM_TUPLETYPE);
+
+    pnm_writepaminit(&pam);
+    
+    tupleRow = pnm_allocpamrow(&pam);
+
+    for (row = 0; row < pam.height; ++row) {
+        unsigned int col;
+
+
+        if (ipdb_type(pdbP) == IMG_MONO)
+            mrow(pdbP, row, imgRow);
+        else if (ipdb_type(pdbP) == IMG_GRAY)
+            grow(pdbP, row, imgRow);
+        else
+            g16row(pdbP, row, imgRow);
+
+        for (col = 0; col < pam.width; ++col)
+            tupleRow[col][0] = imgRow[col];
+        
+        pnm_writepamrow(&pam, tupleRow);
+    }
+    pnm_freepamrow(tupleRow);
+
+    free(imgRow);
+}
+
+
+
+static void
+writeText(IPDB *       const pdbP,
+          const char * const name) {
+
+    const char * const note = ipdb_text(pdbP);
+
+    FILE * fP;
+
+    if (name == NULL || note == NULL) {
+    } else {
+        fP = pm_openw(name);
+        if (fP == NULL)
+            pm_error("Could not open note file '%s' for output", name);
+        
+        fprintf(fP, "%s\n", note);
+
+        pm_close(fP);
+    }
+}
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    IPDB * pdbP;
+    int status;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pdbP = ipdb_alloc(NULL);
+    if (pdbP == NULL)
+        pm_error("Could not allocate IPDB structure.");
+
+    status = ipdbRead(pdbP, ifP, cmdline.verbose);
+    if (status != 0)
+        pm_error("Image header read error: %s.", ipdb_err(status));
+
+    writeImgPam(pdbP, stdout);
+
+    writeText(pdbP, cmdline.notefile);
+
+    ipdb_free(pdbP);
+
+    pm_close(ifP);
+
+    return EXIT_SUCCESS;
+}
diff --git a/converter/other/pfmtopam.c b/converter/other/pfmtopam.c
index 1617ed31..080ec7ff 100644
--- a/converter/other/pfmtopam.c
+++ b/converter/other/pfmtopam.c
@@ -45,7 +45,7 @@ parseCommandLine(int argc,
    was passed to us as the argv array.  We also trash *argv.
 --------------------------------------------------------------------------*/
     optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-    /* Instructions to optParseOptions3 on how to parse our options. */
+    /* Instructions to pm_optParseOptions3 on how to parse our options. */
     optStruct3 opt;
   
     unsigned int option_def_index;
@@ -59,7 +59,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 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
     /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (!maxvalSpec)
diff --git a/converter/other/pgmtopbm.c b/converter/other/pgmtopbm.c
index f828b716..67cac468 100644
--- a/converter/other/pgmtopbm.c
+++ b/converter/other/pgmtopbm.c
@@ -44,7 +44,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -77,7 +77,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (floydOpt + thresholdOpt + hilbertOpt + dither8Opt + 
@@ -585,6 +585,7 @@ createDither8Converter(unsigned int const cols,
     converter.convertRow = &dither8ConvertRow;
     converter.destroy = dither8Destroy;
     converter.stateP = stateP;
+    converter.maxval = maxval;
 
     /* Scale dither matrix. */
     for (row = 0; row < 16; ++row) {
@@ -660,6 +661,7 @@ createClusterConverter(unsigned int const radius,
     unsigned int row;
 
     converter.cols = cols;
+    converter.maxval = maxval;
     converter.convertRow = &clusterConvertRow;
     converter.destroy = &clusterDestroy;
 
diff --git a/converter/other/pgmtoppm.c b/converter/other/pgmtoppm.c
index 7194db49..f8a69424 100644
--- a/converter/other/pgmtoppm.c
+++ b/converter/other/pgmtoppm.c
@@ -10,6 +10,7 @@
 ** implied warranty.
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <string.h>
 
@@ -44,7 +45,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -62,7 +63,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!mapSpec)
diff --git a/converter/other/pngtopam.c b/converter/other/pngtopam.c
index 89ac100a..8743174e 100644
--- a/converter/other/pngtopam.c
+++ b/converter/other/pngtopam.c
@@ -21,65 +21,52 @@
 #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 <png.h>
+/* Becaues of a design error in png.h, you must not #include <setjmp.h> before
+   <png.h>.  If you do, png.h won't compile.
+*/
+#include <setjmp.h>
+#include <zlib.h>
+
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
 #include "pam.h"
+#include "pngx.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 AlphaHandling {ALPHA_NONE, ALPHA_ONLY, ALPHA_MIX, ALPHA_IN};
 
-enum alpha_handling {ALPHA_NONE, ALPHA_ONLY, ALPHA_MIX, ALPHA_IN};
+typedef struct {
+    bool needCorrection;
+    double gamma;
+} GammaCorrection;
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* '-' if stdin */
+    const char * inputFileName;  /* '-' if stdin */
     unsigned int verbose;
-    enum alpha_handling alpha;
+    enum AlphaHandling alpha;
     const char * background;
-    float gamma;  /* -1.0 means unspecified */
+    unsigned int gammaSpec;
+    float gamma;
     const char * text;
     unsigned int time;
+    unsigned int byrow;
 };
 
 
-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 ) {
+                 struct CmdlineInfo * cmdlineP ) {
 /*----------------------------------------------------------------------------
    Parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -91,14 +78,14 @@ parseCommandLine(int                  argc,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
     unsigned int option_def_index;
 
     unsigned int alphaSpec, alphapamSpec, mixSpec,
-        backgroundSpec, gammaSpec, textSpec;
+        backgroundSpec, textSpec;
 
     MALLOCARRAY(option_def, 100);
 
@@ -114,17 +101,19 @@ parseCommandLine(int                  argc,
     OPTENT3(0, "background",  OPT_STRING, &cmdlineP->background,
             &backgroundSpec,          0);
     OPTENT3(0, "gamma",       OPT_FLOAT,  &cmdlineP->gamma,
-            &gammaSpec,               0);
+            &cmdlineP->gammaSpec,     0);
     OPTENT3(0, "text",        OPT_STRING, &cmdlineP->text,
             &textSpec,                0);
     OPTENT3(0, "time",        OPT_FLAG,   NULL,                  
             &cmdlineP->time,          0);
+    OPTENT3(0, "byrow",       OPT_FLAG,   NULL,                  
+            &cmdlineP->byrow,         0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
@@ -145,16 +134,13 @@ parseCommandLine(int                  argc,
     if (!backgroundSpec)
         cmdlineP->background = NULL;
 
-    if (!gammaSpec)
-        cmdlineP->gamma = -1.0;
-
     if (!textSpec)
         cmdlineP->text = 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("Program takes at most one argument: input file name.  "
             "you specified %d", argc-1);
@@ -162,18 +148,299 @@ parseCommandLine(int                  argc,
 
 
 
+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 pngcolor
+pngcolorFrom16(png_color_16 const arg) {
+
+    pngcolor retval;
+
+    retval.r = arg.red;
+    retval.g = arg.green;
+    retval.b = arg.blue;
+
+    return retval;
+}
+
+
+
+static pngcolor
+pngcolorFromByte(png_color const arg) {
+
+    pngcolor retval;
+
+    retval.r = arg.red;
+    retval.g = arg.green;
+    retval.b = arg.blue;
+
+    return retval;
+}
+
+
+
+static bool
+pngColorEqual(pngcolor const comparand,
+              pngcolor const comparator) {
+
+    return (comparand.r == comparator.r
+            && comparand.g == comparator.g
+            && comparand.b == comparator.b);
+}
+
+
+
+static png_uint_16
+gammaCorrect(png_uint_16     const uncorrected,
+             GammaCorrection const gamma,
+             png_uint_16     const maxval) {
+
+    if (gamma.needCorrection) {
+        double const uncorrectedN = (double) uncorrected / maxval;
+        return (png_uint_16)
+            ROUNDU(pow(uncorrectedN, (1.0 / gamma.gamma)) * maxval);
+    } else
+        return uncorrected;
+}
+
+
+
+static pngcolor
+gammaCorrectColor(pngcolor        const color,
+                  GammaCorrection const gamma,
+                  png_uint_16     const maxval) {
+
+    pngcolor retval;
+
+    retval.r = gammaCorrect(color.r, gamma, maxval);
+
+    return retval;
+}
+
+
+
+static unsigned int
+computePngLineSize(struct pngx * const pngxP) {
+
+    unsigned int const bytesPerSample =
+        pngx_bitDepth(pngxP) == 16 ? 2 : 1;
+
+    unsigned int samplesPerPixel;
+
+    switch (pngx_colorType(pngxP)) {
+    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 < pngx_imageWidth(pngxP))
+        pm_error("Width %u of PNG is uncomputably large",
+                 pngx_imageWidth(pngxP));
+       
+    return pngx_imageWidth(pngxP) * 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, pngx_imageHeight(pngxP));
+
+    if (pngImage == NULL)
+        pm_error("couldn't allocate index space for %u PNG raster rows.  "
+                 "Try -byrow, which needs only 1 row of buffer space.  ",
+                 pngx_imageHeight(pngxP));
+
+    for (row = 0; row < pngx_imageHeight(pngxP); ++row) {
+        MALLOCARRAY(pngImage[row], lineSize);
+        if (pngImage[row] == NULL)
+            pm_error("couldn't allocate space for %uth row of PNG raster.  "
+                     "Try -byrow, which needs only 1 row of buffer space.  ",
+                     row);
+    }
+    *pngImageP = pngImage;
+}
+
+
+
+static void
+freePngRaster(png_byte **   const pngRaster,
+              struct pngx * const pngxP) {
+
+    unsigned int row;
+
+    for (row = 0; row < pngx_imageHeight(pngxP); ++row)
+        free(pngRaster[row]);
+
+    free(pngRaster);
+}
+
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is an object for reading the raster of the PNG, a row at a time.
+-----------------------------------------------------------------------------*/
+    struct pngx * pngxP;
+    png_byte **   pngRaster;
+        /* The entire raster of the PNG.  Null if this is a
+           row-at-a-time object.  Constant.
+
+           We give a pointer into this to the user.
+        */
+    png_byte * rowBuf;
+        /* The buffer in which we put the most recently read row.
+           Null if this is an all-at-once object.  Constant.
+
+           We give a pointer into this to the user.
+        */
+    unsigned int  nextRowNum;
+        /* The number of the next row to be read from this object. */
+
+} Reader;
+
+
+
+static Reader *
+reader_createAllAtOnce(struct pngx * const pngxP,
+                       FILE *        const ifP) {
+/*----------------------------------------------------------------------------
+   Create a Reader object that uses libpng's all-at-once raster reading
+   interface (libpng calls this the "high level" interface).
+
+   The Reader object reads the PNG at construction time, stores the entire
+   raster, and hands it out as you call reader_read().
+
+   It is essential that *pngxP be already fully set up to read the image
+   (all options set).
+-----------------------------------------------------------------------------*/
+    Reader * readerP;
+
+    MALLOCVAR_NOFAIL(readerP);
+
+    readerP->pngxP = pngxP;
+
+    allocPngRaster(pngxP, &readerP->pngRaster);
+
+    readerP->rowBuf = NULL;
+
+    pngx_readImage(pngxP, readerP->pngRaster);
+
+    readerP->nextRowNum = 0;
+
+    return readerP;
+}
+
+
+
+static Reader *
+reader_createRowByRow(struct pngx * const pngxP,
+                      FILE *        const ifP) {
+/*----------------------------------------------------------------------------
+   Create a Reader object that uses libpng's one-row-at-a-time raster reading
+   interface (libpng calls this the "low level" interface).
+
+   The Reader object reads from the PNG file, via libpng, as its client
+   requests the rows.
+-----------------------------------------------------------------------------*/
+    Reader * readerP;
+
+    MALLOCVAR_NOFAIL(readerP);
+
+    readerP->pngxP = pngxP;
+
+    readerP->pngRaster = NULL;
+
+    MALLOCARRAY(readerP->rowBuf, computePngLineSize(pngxP)); 
+
+    if (!readerP->rowBuf)
+        pm_error("Could not allocate %u bytes for a PNG row buffer",
+                 computePngLineSize(pngxP));
+
+    readerP->nextRowNum = 0;
+
+    if (pngx_interlaceType(pngxP) != PNG_INTERLACE_NONE)
+        pm_message("WARNING: this is an interlaced PNG.  The PAM output "
+                   "will be interlaced.  To get proper output, "
+                   "don't use -byrow");
+
+    return readerP;
+}
+
+
+
+static void
+reader_destroy(Reader * const readerP) {
+
+    if (readerP->pngRaster)
+        freePngRaster(readerP->pngRaster, readerP->pngxP);
+   
+    if (readerP->rowBuf)
+        free(readerP->rowBuf);
+
+    free(readerP);
+}
+
+
+
+static png_byte *
+reader_read(Reader * const readerP) {
+/*----------------------------------------------------------------------------
+   Return a pointer to the next row of the raster.
+
+   The pointer is into storage owned by this object.  It is good until
+   the next read from the object, while the object exists.
+-----------------------------------------------------------------------------*/
+    png_byte * retval;
+
+    if (readerP->pngRaster) {
+        if (readerP->nextRowNum >= pngx_imageHeight(readerP->pngxP))
+            retval = NULL;
+        else
+            retval = readerP->pngRaster[readerP->nextRowNum];
+    } else {
+        pngx_readRow(readerP->pngxP, readerP->rowBuf, NULL);
+        retval = readerP->rowBuf;
+    }
+
+    ++readerP->nextRowNum;
+
+    return retval;
+}
+
+
+
 static png_uint_16
-get_png_val(const png_byte ** const pp,
-            int               const bit_depth) {
+getPngVal(const png_byte ** const pp,
+          int               const bitDepth) {
 
     png_uint_16 c;
     
-    if (bit_depth == 16)
-        c = (*((*pp)++)) << 8;
+    if (bitDepth == 16)
+        c = *(*pp)++ << 8;
     else
         c = 0;
 
-    c |= (*((*pp)++));
+    c |= *(*pp)++;
     
     return c;
 }
@@ -207,13 +474,14 @@ setTuple(const struct pam *  const pamP,
          tuple               const tuple,
          pngcolor            const foreground,
          pngcolor            const background,
-         enum alpha_handling const alphaHandling,
+         enum AlphaHandling  const alphaHandling,
+         const struct pngx * const pngxP,
          png_uint_16         const alpha) {
 
     if (alphaHandling == ALPHA_ONLY)
         tuple[0] = alpha;
     else if (alphaHandling == ALPHA_NONE ||
-             (alphaHandling == ALPHA_MIX && alpha == maxval)) {
+             (alphaHandling == ALPHA_MIX && alpha == pngxP->maxval)) {
         if (pamP->depth < 3)
             tuple[0] = foreground.r;
         else {
@@ -236,227 +504,228 @@ setTuple(const struct pam *  const pamP,
 
         if (pamP->depth < 3)
             tuple[0] =
-                alphaMix(foreground.r, background.r, alpha, maxval);
+                alphaMix(foreground.r, background.r, alpha, pngxP->maxval);
         else {
             tuple[PAM_RED_PLANE] =
-                alphaMix(foreground.r, background.r, alpha, maxval);
+                alphaMix(foreground.r, background.r, alpha, pngxP->maxval);
             tuple[PAM_GRN_PLANE] =
-                alphaMix(foreground.g, background.g, alpha, maxval);
+                alphaMix(foreground.g, background.g, alpha, pngxP->maxval);
             tuple[PAM_BLU_PLANE] =
-                alphaMix(foreground.b, background.b, alpha, maxval);
+                alphaMix(foreground.b, background.b, alpha, pngxP->maxval);
         }
     }
 }
 
 
 
-static png_uint_16
-gamma_correct(png_uint_16 const v,
-              float       const g) {
+static bool
+isColor(png_color const c) {
 
-    if (g != -1.0)
-        return (png_uint_16) ROUNDU(pow((double) v / maxval, (1.0 / g)) *
-                                    maxval);
-    else
-        return v;
+    return c.red != c.green || c.green != c.blue;
 }
 
 
 
-static int iscolor (png_color c)
-{
-  return c.red != c.green || c.green != c.blue;
-}
+static void
+saveText(struct pngx * const pngxP,
+         FILE *        const tfP) {
 
-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 */
+    struct pngx_text const text = pngx_text(pngxP);
+
+    unsigned int i;
+
+    for (i = 0 ; i < text.size; ++i) {
+        unsigned int j;
+        j = 0;
+
+        while (text.line[i].key[j] != '\0' &&
+               text.line[i].key[j] != ' ')
+            ++j;    
+
+        if (text.line[i].key[j] != ' ') {
+            fprintf(tfP, "%s", text.line[i].key);
+            for (j = strlen (text.line[i].key); j < 15; ++j)
+                putc(' ', tfP);
+        } else {
+            fprintf(tfP, "\"%s\"", text.line[i].key);
+            for (j = strlen (text.line[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 < text.line[i].text_length; ++j) {
+            putc(text.line[i].text[j], tfP);
+            if (text.line[i].text[j] == '\n') {
+                unsigned int k;
+                for (k = 0; k < 16; ++k)
+                    putc(' ', tfP);
+            }
+        }
+        putc('\n', 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. */
+static void
+showTime(struct pngx * const pngxP) {
 
-  pm_message("fatal libpng error: %s", msg);
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_tIME)) {
+        png_time const modTime = pngx_time(pngxP);
 
-  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.");
-  }
+        static const char * const month[] = {
+            "", "January", "February", "March", "April", "May", "June",
+            "July", "August", "September", "October", "November", "December"
+        };
 
-  longjmp(jmpbuf_ptr->jmpbuf, 1);
+        if (modTime.month < 1 || modTime.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", modTime.month);
+        } else {
+            pm_message("modification time: %02d %s %d %02d:%02d:%02d",
+                       modTime.day,
+                       month[modTime.month],
+                       modTime.year,
+                       modTime.hour,
+                       modTime.minute,
+                       modTime.second);
+        }
+    }
 }
 
 
 
 static void
-dump_png_info(png_info *info_ptr) {
+dumpTypeAndFilter(struct pngx * const pngxP) {
 
-    const char *type_string;
-    const char *filter_string;
+    const char * typeString;
+    const char * filterString;
 
-    switch (info_ptr->color_type) {
-      case PNG_COLOR_TYPE_GRAY:
-        type_string = "gray";
+    switch (pngx_colorType(pngxP)) {
+    case PNG_COLOR_TYPE_GRAY:
+        typeString = "gray";
         break;
-
-      case PNG_COLOR_TYPE_GRAY_ALPHA:
-        type_string = "gray+alpha";
+        
+    case PNG_COLOR_TYPE_GRAY_ALPHA:
+        typeString = "gray+alpha";
         break;
-
-      case PNG_COLOR_TYPE_PALETTE:
-        type_string = "palette";
+        
+    case PNG_COLOR_TYPE_PALETTE:
+        typeString = "palette";
         break;
 
-      case PNG_COLOR_TYPE_RGB:
-        type_string = "truecolor";
+    case PNG_COLOR_TYPE_RGB:
+        typeString = "truecolor";
         break;
 
-      case PNG_COLOR_TYPE_RGB_ALPHA:
-        type_string = "truecolor+alpha";
+    case PNG_COLOR_TYPE_RGB_ALPHA:
+        typeString = "truecolor+alpha";
         break;
     }
 
-    switch (info_ptr->filter_type) {
+    switch (pngx_filterType(pngxP)) {
     case PNG_FILTER_TYPE_BASE:
-        asprintfN(&filter_string, "base filter");
+        pm_asprintf(&filterString, "base filter");
         break;
     default:
-        asprintfN(&filter_string, "unknown filter type %d", 
-                  info_ptr->filter_type);
+        pm_asprintf(&filterString, "unknown filter type %d", 
+                    pngx_filterType(pngxP));
     }
 
-    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 ? 
+               typeString,
+               pngx_interlaceType(pngxP) ? 
                "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)
+               filterString);
+
+    pm_strfree(filterString);
+}
+
+
+
+static void
+dumpPngInfo(struct pngx * const pngxP) {
+
+    pm_message("reading a %u x %u image, %u bit%s",
+               pngx_imageWidth(pngxP),
+               pngx_imageHeight(pngxP),
+               pngx_bitDepth(pngxP),
+               pngx_bitDepth(pngxP) > 1 ? "s" : "");
+
+    dumpTypeAndFilter(pngxP);
+
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_bKGD)) {
+        png_color_16 const background = pngx_bkgd(pngxP);
+
+        pm_message("background {index, gray, red, green, blue} = "
+                   "{%d, %d, %d, %d, %d}",
+                   background.index,
+                   background.gray,
+                   background.red,
+                   background.green,
+                   background.blue);
+    }
+
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS))
         pm_message("tRNS chunk (transparency): %u entries",
-                   info_ptr->num_trans);
+                   pngx_trns(pngxP).numTrans);
     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);
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_gAMA))
+        pm_message("gAMA chunk (image gamma): gamma = %4.2f",
+                   pngx_gama(pngxP));
     else
         pm_message("gAMA chunk (image gamma): not present");
-
-    if (info_ptr->valid & PNG_INFO_sBIT)
+    
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_sBIT))
         pm_message("sBIT chunk: present");
     else
         pm_message("sBIT chunk: not present");
 
-    if (info_ptr->valid & PNG_INFO_cHRM)
+    if (pngx_chunkIsPresent(pngxP, 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);
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_PLTE))
+        pm_message("PLTE chunk: %d entries", pngx_plte(pngxP).size);
     else
         pm_message("PLTE chunk: not present");
 
-    if (info_ptr->valid & PNG_INFO_bKGD)
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_bKGD))
         pm_message("bKGD chunk: present");
     else
         pm_message("bKGD chunk: not present");
 
-    if (info_ptr->valid & PNG_INFO_hIST)
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_hIST))
         pm_message("hIST chunk: present");
     else
         pm_message("hIST chunk: not present");
 
-    if (info_ptr->valid & PNG_INFO_pHYs)
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_pHYs))
         pm_message("pHYs chunk: present");
     else
         pm_message("pHYs chunk: not present");
 
-    if (info_ptr->valid & PNG_INFO_oFFs)
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_oFFs))
         pm_message("oFFs chunk: present");
     else
         pm_message("oFFs chunk: not present");
 
-    if (info_ptr->valid & PNG_INFO_tIME)
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_tIME))
         pm_message("tIME chunk: present");
     else
         pm_message("tIME chunk: not present");
 
-    if (info_ptr->valid & PNG_INFO_pCAL)
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_pCAL))
         pm_message("pCAL chunk: present");
     else
         pm_message("pCAL chunk: not present");
 
-    if (info_ptr->valid & PNG_INFO_sRGB)
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_sRGB))
         pm_message("sRGB chunk: present");
     else
         pm_message("sRGB chunk: not present");
@@ -464,83 +733,34 @@ dump_png_info(png_info *info_ptr) {
 
 
 
-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;
+static const png_color_16
+transColor(struct pngx * const pngxP) {
 
-    for (row = 0; row < pngInfoP->height; ++row)
-        free(pngRaster[row]);
+    struct pngx_trns const trans = pngx_trns(pngxP);
 
-    free(pngRaster);
+    assert(pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS));
+    
+    return trans.transColor;
 }
 
 
 
 static bool
-isTransparentColor(pngcolor   const color,
-                   png_info * const pngInfoP,
-                   double     const totalgamma) {
+isTransparentColor(pngcolor        const color,
+                   struct pngx *   const pngxP,
+                   GammaCorrection const gamma) {
 /*----------------------------------------------------------------------------
    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.
+   'gamma' indicats what gamma correction has been applied to 'color' (we need
+   to know that because *pngxP identifies the color that is supposed to be
+   transparent in _not_ gamma-corrected form!).
 -----------------------------------------------------------------------------*/
     bool retval;
 
-    if (pngInfoP->valid & PNG_INFO_tRNS) {
-        const png_color_16 * const transColorP = &pngInfoP->trans_values;
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) {
+        png_color_16 const transColor16 = transColor(pngxP);
 
         /* It seems odd that libpng lets you get gamma-corrected pixel
            values, but not gamma-corrected transparency or background
@@ -556,15 +776,17 @@ isTransparentColor(pngcolor   const color,
            pixels, and just do it ourselves.
         */
     
-        switch (pngInfoP->color_type) {
+        switch (pngx_colorType(pngxP)) {
         case PNG_COLOR_TYPE_GRAY:
-            retval = color.r == gamma_correct(transColorP->gray, totalgamma);
+            retval = color.r == gammaCorrect(transColor16.gray, gamma,
+                                             pngxP->maxval);
             break;
-        default:
-            retval = 
-                color.r == gamma_correct(transColorP->red,   totalgamma) &&
-                color.g == gamma_correct(transColorP->green, totalgamma) &&
-                color.b == gamma_correct(transColorP->blue,  totalgamma);
+        default: {
+            pngcolor const transColor = pngcolorFrom16(transColor16);
+            retval = pngColorEqual(color,
+                                   gammaCorrectColor(transColor, gamma,
+                                                     pngxP->maxval));
+        }
         }
     } else 
         retval = FALSE;
@@ -574,60 +796,63 @@ 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");
+setupGammaCorrection(struct pngx *     const pngxP,
+                     bool              const screenGammaIsKnown,
+                     float             const screenGamma,
+                     GammaCorrection * const gammaCorrectionP) {
+/*----------------------------------------------------------------------------
+   Set up to have values from the PNG gamma-corrected.
 
-    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");
-}
+   Return as *gammaCorrectionP the correction necessary and tell the
+   libpng image reader *pngxP to do that same correction - on the pixels
+   only, as it can't do it on anything else (hence, Caller will have to
+   use *gammaCorrectionP to do it).
 
+   'screenGammaIsKnown' means we know what the screen gamma is, and it is
+   'screenGamma'.  If we don't know what the screen gamma is, gamma 
+   correction is not possible, so we set up for no gamma correction.
 
+   The gamma correction we ordain is a combination of the image gamma,
+   recorded in the PNG input and represented by *pngxP, and the screen gamma.
 
-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;
+   Note that "screen gamma" is a characteristic of both the display and the
+   viewing conditions.  There are also "display gamma" and "viewing gamma,"
+   respectively, but we don't care about the breakdown.  In a dimly lit room,
+   viewing gamma is 1 and screen gamma is the same as the display gamma.
+-----------------------------------------------------------------------------*/
+    if (!screenGammaIsKnown)
+        gammaCorrectionP->needCorrection = false;
     else {
         float imageGamma;
-        if (info_ptr->valid & PNG_INFO_gAMA)
-            imageGamma = info_ptr->gamma;
+        if (pngx_chunkIsPresent(pngxP, PNG_INFO_gAMA))
+            imageGamma = pngx_gama(pngxP);
         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 (fabs(screenGamma * imageGamma - 1.0) < .01) {
+            gammaCorrectionP->needCorrection = false;
             if (verbose)
                 pm_message("image gamma %4.2f matches "
-                           "display gamma %4.2f.  No conversion.",
-                           imageGamma, displaygamma);
+                           "screen gamma %4.2f.  No conversion.",
+                           imageGamma, screenGamma);
         } else {
-            png_set_gamma(png_ptr, displaygamma, imageGamma);
-            *totalgammaP = imageGamma * displaygamma;
-            /* in case of gamma-corrections, sBIT's as in the
+            pngx_setGamma(pngxP, screenGamma, imageGamma);
+
+            gammaCorrectionP->needCorrection = true;
+            gammaCorrectionP->gamma = imageGamma * screenGamma;
+            /* In case of gamma-corrections, sBIT's as in the
                PNG-file are not valid anymore 
             */
-            info_ptr->valid &= ~PNG_INFO_sBIT;
+            pngx_removeChunk(pngxP, PNG_INFO_sBIT);
             if (verbose)
                 pm_message("image gamma is %4.2f, "
-                           "converted for display gamma of %4.2f",
-                           imageGamma, displaygamma);
+                           "display gamma is %4.2f; "
+                           "combined gamma is %4.2f",
+                           imageGamma, screenGamma, gammaCorrectionP->gamma);
         }
     }
 }
@@ -635,20 +860,21 @@ setupGammaCorrection(png_struct * const png_ptr,
 
 
 static bool
-paletteHasPartialTransparency(png_info * const info_ptr) {
+paletteHasPartialTransparency(struct pngx * const pngxP) {
 
     bool retval;
 
-    if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
-        if (info_ptr->valid & PNG_INFO_tRNS) {
+    if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_PALETTE) {
+        if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) {
+            struct pngx_trns const trans = pngx_trns(pngxP);
+
             bool foundGray;
             unsigned int i;
             
             for (i = 0, foundGray = FALSE;
-                 i < info_ptr->num_trans && !foundGray;
+                 i < trans.numTrans && !foundGray;
                  ++i) {
-                if (info_ptr->TRANS_ALPHA[i] != 0 &&
-                    info_ptr->TRANS_ALPHA[i] != maxval) {
+                if (trans.trans[i] != 0 && trans.trans[i] != pngxP->maxval) {
                     foundGray = TRUE;
                 }
             }
@@ -664,61 +890,68 @@ paletteHasPartialTransparency(png_info * const info_ptr) {
 
 
 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) {
+getComponentSbitFg(struct pngx * const pngxP,
+                   png_byte *    const fgSbitP,
+                   bool *        const notUniformP) {
+
+    png_color_8 const sigBit = pngx_sbit(pngxP);
+
+    assert(pngx_chunkIsPresent(pngxP, PNG_INFO_sBIT));
+
+    if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB ||
+        pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB_ALPHA ||
+        pngx_colorType(pngxP) == PNG_COLOR_TYPE_PALETTE) {
+
+        if (sigBit.red == sigBit.blue &&
+            sigBit.red == sigBit.green) {
             *notUniformP = false;
-            *fgSbitP     = pngInfoP->sig_bit.red;
+            *fgSbitP     = sigBit.red;
         } else
             *notUniformP = true;
     } else {
         /* It has only a gray channel so it's obviously uniform */
         *notUniformP = false;
-        *fgSbitP     = pngInfoP->sig_bit.gray;
+        *fgSbitP     = sigBit.gray;
     }
 }
 
 
 
 static void
-getComponentSbit(png_info *          const pngInfoP,
-                 enum alpha_handling const alphaHandling,
+getComponentSbit(struct pngx *       const pngxP,
+                 enum AlphaHandling  const alphaHandling,
                  png_byte *          const componentSbitP,
                  bool *              const notUniformP) {
 
+    assert(pngx_chunkIsPresent(pngxP, PNG_INFO_sBIT));
+
     switch (alphaHandling) {
 
-    case ALPHA_ONLY:
+    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;
+        *componentSbitP = pngx_sbit(pngxP).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);
+        getComponentSbitFg(pngxP, componentSbitP, notUniformP);
         break;
     case ALPHA_IN: {
         /* We care about both the foreground and the alpha */
         bool fgNotUniform;
         png_byte fgSbit;
         
-        getComponentSbitFg(pngInfoP, &fgSbit, &fgNotUniform);
+        getComponentSbitFg(pngxP, &fgSbit, &fgNotUniform);
 
         if (fgNotUniform)
             *notUniformP = true;
         else {
-            if (fgSbit == pngInfoP->sig_bit.alpha) {
+            if (fgSbit == pngx_sbit(pngxP).alpha) {
                 *notUniformP    = false;
                 *componentSbitP = fgSbit;
             } else
@@ -731,8 +964,8 @@ getComponentSbit(png_info *          const pngInfoP,
                  
 
 static void
-shiftPalette(png_info *   const pngInfoP,
-             unsigned int const shift) {
+shiftPalette(struct pngx * const pngxP,
+             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.
@@ -743,12 +976,14 @@ shiftPalette(png_info *   const pngInfoP,
                  "but sBIT chunk says %u bits",
                  shift);
     else {
+        struct pngx_plte const palette = pngx_plte(pngxP);
+        
         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);
+        for (i = 0; i < palette.size; ++i) {
+            palette.palette[i].red   >>= (8 - shift);
+            palette.palette[i].green >>= (8 - shift);
+            palette.palette[i].blue  >>= (8 - shift);
         }
     }
 }
@@ -756,12 +991,11 @@ shiftPalette(png_info *   const pngInfoP,
 
 
 static void
-computeMaxvalFromSbit(png_struct *        const pngP,
-                      png_info *          const pngInfoP,
-                      enum alpha_handling const alphaHandling,
+computeMaxvalFromSbit(struct pngx *       const pngxP,
+                      enum AlphaHandling  const alphaHandling,
                       png_uint_16 *       const maxvalP,
                       bool *              const succeededP,
-                      int *               const errorlevelP) {
+                      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,
@@ -782,37 +1016,38 @@ computeMaxvalFromSbit(png_struct *        const pngP,
            Meaningless if they aren't all the same (i.e. 'notUniform')
         */
 
-    getComponentSbit(pngInfoP, alphaHandling, &componentSigBit, &notUniform);
+    getComponentSbit(pngxP, 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);
+        pm_message("writing file with %u bit resolution",
+                   pngx_bitDepth(pngxP));
         *succeededP = false;
-        *errorlevelP = PNMTOPNG_WARNING_LEVEL;
+        *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;
+        *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)))
+            (pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB_ALPHA ||
+             pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY_ALPHA ||
+             paletteHasPartialTransparency(pngxP)))
             *succeededP = false;
         else {
-            if (componentSigBit < pngInfoP->bit_depth) {
+            if (componentSigBit < pngx_bitDepth(pngxP)) {
                 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);
+                
+                if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_PALETTE)
+                    shiftPalette(pngxP, componentSigBit);
                 else
-                    png_set_shift(pngP, &pngInfoP->sig_bit);
+                    pngx_setShift(pngxP, pngx_sbit(pngxP));
             } else
                 *succeededP = false;
         }
@@ -822,50 +1057,48 @@ computeMaxvalFromSbit(png_struct *        const pngP,
 
 
 static void
-setupSignificantBits(png_struct *        const pngP,
-                     png_info *          const pngInfoP,
-                     enum alpha_handling const alphaHandling,
-                     png_uint_16 *       const maxvalP,
-                     int *               const errorlevelP) {
+setupSignificantBits(struct pngx *       const pngxP,
+                     enum AlphaHandling  const alphaHandling,
+                     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).
+  Figure out what maxval is used in the PNG described by *pngxP, with 'alpha'
+  telling which information in the PNG we care about (image or alpha mask).
+  Update *pngxP with that information.
 
   Return the result as *maxvalP.
 
-  Also set up *pngP for the corresponding significant bits.
+  Also set up *pngxP for the corresponding significant bits.
 -----------------------------------------------------------------------------*/
     bool gotItFromSbit;
     
-    if (pngInfoP->valid & PNG_INFO_sBIT)
-        computeMaxvalFromSbit(pngP, pngInfoP, alphaHandling,
-                              maxvalP, &gotItFromSbit, errorlevelP);
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_sBIT))
+        computeMaxvalFromSbit(pngxP, alphaHandling,
+                              &pngxP->maxval, &gotItFromSbit, errorLevelP);
     else
         gotItFromSbit = false;
 
     if (!gotItFromSbit) {
-        if (pngInfoP->color_type == PNG_COLOR_TYPE_PALETTE) {
+        if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_PALETTE) {
             if (alphaHandling == ALPHA_ONLY) {
-                if (pngInfoP->color_type == PNG_COLOR_TYPE_GRAY ||
-                    pngInfoP->color_type == PNG_COLOR_TYPE_RGB)
+                if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY ||
+                    pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB)
                     /* The alpha mask will be all opaque, so maxval 1
                        is plenty
                     */
-                    *maxvalP = 1;
-                else if (paletteHasPartialTransparency(pngInfoP))
+                    pngxP->maxval = 1;
+                else if (paletteHasPartialTransparency(pngxP))
                     /* Use same maxval as PNG transparency palette for
                        simplicity
                     */
-                    *maxvalP = 255;
+                    pngxP->maxval = 255;
                 else
                     /* A common case, so we conserve bits */
-                    *maxvalP = 1;
+                    pngxP->maxval = 1;
             } else
                 /* Use same maxval as PNG palette for simplicity */
-                *maxvalP = 255;
+                pngxP->maxval = 255;
         } else {
-            *maxvalP = (1l << pngInfoP->bit_depth) - 1;
+            pngxP->maxval = (1l << pngx_bitDepth(pngxP)) - 1;
         }
     }
 }
@@ -873,22 +1106,24 @@ setupSignificantBits(png_struct *        const pngP,
 
 
 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 (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY ||
+        pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY_ALPHA)
 
         retval = FALSE;
-    else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+    else if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_PALETTE) {
+        struct pngx_plte const palette = pngx_plte(pngxP);
+
         bool foundColor;
         unsigned int i;
             
         for (i = 0, foundColor = FALSE;
-             i < info_ptr->num_palette && !foundColor;
+             i < palette.size && !foundColor;
              ++i) {
-            if (iscolor(info_ptr->palette[i]))
+            if (isColor(palette.palette[i]))
                 foundColor = TRUE;
         }
         retval = foundColor;
@@ -901,8 +1136,8 @@ imageHasColor(png_info * const info_ptr) {
 
 
 static void
-determineOutputType(png_info *          const pngInfoP,
-                    enum alpha_handling const alphaHandling,
+determineOutputType(struct pngx *       const pngxP,
+                    enum AlphaHandling  const alphaHandling,
                     pngcolor            const bgColor,
                     xelval              const maxval,
                     int *               const formatP,
@@ -916,7 +1151,7 @@ determineOutputType(png_info *          const pngInfoP,
     } else {            
         /* The output is a normal Netpbm image */
         bool const outputIsColor =
-            imageHasColor(pngInfoP) || !isGrayscale(bgColor);
+            imageHasColor(pngxP) || !isGrayscale(bgColor);
 
         if (alphaHandling == ALPHA_IN) {
             *formatP = PAM_FORMAT;
@@ -942,11 +1177,11 @@ determineOutputType(png_info *          const pngInfoP,
 
 
 static void
-getBackgroundColor(png_info *   const info_ptr,
-                   const char * const requestedColor,
-                   float        const totalgamma,
-                   xelval       const maxval,
-                   pngcolor *   const bgColorP) {
+getBackgroundColor(struct pngx *   const pngxP,
+                   const char *    const requestedColor,
+                   GammaCorrection const gamma,
+                   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.
@@ -964,31 +1199,31 @@ getBackgroundColor(png_info *   const info_ptr,
         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
+    } else if (pngx_chunkIsPresent(pngxP, 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) {
+        png_color_16 const background = pngx_bkgd(pngxP);
+        switch (pngx_colorType(pngxP)) {
         case PNG_COLOR_TYPE_GRAY:
         case PNG_COLOR_TYPE_GRAY_ALPHA:
             bgColorP->r = bgColorP->g = bgColorP->b = 
-                gamma_correct(info_ptr->background.gray, totalgamma);
+                gammaCorrect(background.gray, gamma, pngxP->maxval);
             break;
         case PNG_COLOR_TYPE_PALETTE: {
+            struct pngx_plte const palette = pngx_plte(pngxP);
             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);
+                palette.palette[background.index];
+            *bgColorP = gammaCorrectColor(pngcolorFromByte(rawBgcolor),
+                                          gamma, pngxP->maxval);
         }
         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 = background;
             
-            bgColorP->r = gamma_correct(rawBgcolor.red,   totalgamma);
-            bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
-            bgColorP->b = gamma_correct(rawBgcolor.blue,  totalgamma);
+            *bgColorP = gammaCorrectColor(pngcolorFrom16(rawBgcolor),
+                                          gamma, pngxP->maxval);
         }
         break;
         }
@@ -999,31 +1234,88 @@ getBackgroundColor(png_info *   const info_ptr,
 
 
 
-#define GET_PNG_VAL(p) get_png_val(&(p), pngInfoP->bit_depth)
+static void
+warnNonsquarePixels(struct pngx * const pngxP,
+                    int *         const errorLevelP) {
+
+    if (pngx_pixelAspectRatioIsKnown(pngxP)) {
+        float const r = pngx_pixelAspectRatio(pngxP);
+
+        if (r != 1.0) {
+            const char * const baseMsg = "warning - non-square pixels";
+
+            if (pm_have_float_format())
+                pm_message("%s; to fix do a 'pamscale -%cscale %g'",
+                           baseMsg,
+                           r < 1.0 ? 'x' : 'y',
+                           r < 1.0 ? 1.0 / r : r);
+            else
+                pm_message("%s", baseMsg);
+
+            *errorLevelP = PNMTOPNG_WARNING_LEVEL;
+        }
+    }
+}
+
+
+
+static png_uint_16
+paletteAlpha(struct pngx * const pngxP,
+             png_uint_16   const index,
+             sample        const maxval) {
+
+    png_uint_16 retval;
+
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) {
+        struct pngx_trns const trans = pngx_trns(pngxP);
+
+        if (index < trans.numTrans)
+            retval = trans.trans[index];
+        else
+            retval = maxval;
+    } else
+        retval = maxval;
+
+    return retval;
+}
+
+
+
+#define GET_PNG_VAL(p) getPngVal(&(p), pngx_bitDepth(pngxP))
 
 
 
 static void
 makeTupleRow(const struct pam *  const pamP,
              const tuple *       const tuplerow,
-             png_info *          const pngInfoP,
+             struct pngx *       const pngxP,
              const png_byte *    const pngRasterRow,
              pngcolor            const bgColor,
-             enum alpha_handling const alphaHandling,
-             double              const totalgamma) {
-
+             enum AlphaHandling  const alphaHandling,
+             GammaCorrection     const gamma) {
+/*----------------------------------------------------------------------------
+   Convert a raster row as supplied by libpng, at 'pngRasterRow' and
+   described by *pngxP, to a libpam-style tuple row at 'tupleRow'.
+
+   Where the raster says the pixel isn't opaque, we either include that
+   opacity information in the output pixel or we mix the pixel with background
+   color 'bgColor', as directed by 'alphaHandling'.  Or, if 'alphaHandling'
+   says so, we may produce an output row of _only_ the transparency
+   information.
+-----------------------------------------------------------------------------*/
     const png_byte * pngPixelP;
     unsigned int col;
 
     pngPixelP = &pngRasterRow[0];  /* initial value */
-    for (col = 0; col < pngInfoP->width; ++col) {
-        switch (pngInfoP->color_type) {
+    for (col = 0; col < pngx_imageWidth(pngxP); ++col) {
+        switch (pngx_colorType(pngxP)) {
         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);
+                     pngxP,
+                     isTransparentColor(fgColor, pngxP, gamma) ?
+                     0 : pngxP->maxval);
         }
         break;
 
@@ -1034,13 +1326,14 @@ makeTupleRow(const struct pam *  const pamP,
             fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP);
             alpha = GET_PNG_VAL(pngPixelP);
             setTuple(pamP, tuplerow[col], fgColor, bgColor,
-                     alphaHandling, alpha);
+                     alphaHandling, pngxP, alpha);
         }
         break;
 
         case PNG_COLOR_TYPE_PALETTE: {
-            png_uint_16 const index        = GET_PNG_VAL(pngPixelP);
-            png_color   const paletteColor = pngInfoP->palette[index];
+            png_uint_16      const index        = GET_PNG_VAL(pngPixelP);
+            struct pngx_plte const palette      = pngx_plte(pngxP);
+            png_color        const paletteColor = palette.palette[index];
 
             pngcolor fgColor;
 
@@ -1049,9 +1342,7 @@ makeTupleRow(const struct pam *  const pamP,
             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);
+                     pngxP, paletteAlpha(pngxP, index, pngxP->maxval));
         }
         break;
                 
@@ -1062,8 +1353,9 @@ makeTupleRow(const struct pam *  const pamP,
             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);
+                     pngxP,
+                     isTransparentColor(fgColor, pngxP, gamma) ?
+                     0 : pngxP->maxval);
         }
         break;
 
@@ -1076,12 +1368,13 @@ makeTupleRow(const struct pam *  const pamP,
             fgColor.b = GET_PNG_VAL(pngPixelP);
             alpha     = GET_PNG_VAL(pngPixelP);
             setTuple(pamP, tuplerow[col], fgColor, bgColor,
-                     alphaHandling, alpha);
+                     alphaHandling, pngxP, alpha);
         }
         break;
 
         default:
-            pm_error("unknown PNG color type: %d", pngInfoP->color_type);
+            pm_error("unknown PNG color type: %d",
+                     pngx_colorType(pngxP));
         }
     }
 }
@@ -1103,8 +1396,8 @@ reportOutputFormat(const struct pam * const pamP) {
         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);
+        pm_message("Writing a PAM file with tuple type %s, maxval %lu",
+                   pamP->tuple_type, pamP->maxval);
         break;
     default:
         assert(false); /* Every possible value handled above */
@@ -1115,15 +1408,15 @@ reportOutputFormat(const struct pam * const pamP) {
 
 static void
 writeNetpbm(struct pam *        const pamP,
-            png_info *          const pngInfoP,
-            png_byte **         const pngRaster,
+            struct pngx *       const pngxP,
+            Reader *            const rasterReaderP,
             pngcolor            const bgColor,
-            enum alpha_handling const alphaHandling,
-            double              const totalgamma) {
+            enum AlphaHandling  const alphaHandling,
+            GammaCorrection     const gamma) {
 /*----------------------------------------------------------------------------
    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.
+   'alphaHandling' that is in the PNG image described by *pngxP, reading
+   its raster with the raster reader object *rasterReaderP.
 
    *pamP describes the required output image and is consistent with
    *pngInfoP.
@@ -1141,9 +1434,13 @@ writeNetpbm(struct pam *        const pamP,
 
     tuplerow = pnm_allocpamrow(pamP);
 
-    for (row = 0; row < pngInfoP->height; ++row) {
-        makeTupleRow(pamP, tuplerow, pngInfoP, pngRaster[row], bgColor,
-                     alphaHandling, totalgamma);
+    for (row = 0; row < pngx_imageHeight(pngxP); ++row) {
+        png_byte * const pngRow = reader_read(rasterReaderP);
+
+        assert(pngRow);
+
+        makeTupleRow(pamP, tuplerow, pngxP, pngRow, bgColor,
+                     alphaHandling, gamma);
 
         pnm_writepamrow(pamP, tuplerow);
     }
@@ -1153,97 +1450,68 @@ writeNetpbm(struct pam *        const pamP,
 
 
 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;
+convertpng(FILE *             const ifP, 
+           FILE *             const tfP, 
+           struct CmdlineInfo const cmdline,
+           int *              const errorLevelP) {
+
+    Reader * rasterReaderP;
     pngcolor bgColor;
-    float totalgamma;
+    GammaCorrection gamma;
     struct pam pam;
+    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)");
+    *errorLevelP = 0;
 
-    info_ptr = png_create_info_struct (png_ptr);
-    if (info_ptr == NULL)
-        pm_error("cannot allocate LIBPNG structures");
-
-    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);
-
-    allocPngRaster(info_ptr, &png_image);
+    pngx_create(&pngxP, PNGX_READ, &jmpbuf);
 
-    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);
+    pngx_readStart(pngxP, ifP);
 
     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);
+        dumpPngInfo(pngxP);
 
     if (cmdline.time)
-        show_time(info_ptr);
-    if (tfp)
-        save_text(info_ptr, tfp);
+        showTime(pngxP);
+    if (tfP)
+        saveText(pngxP, 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;
-        }
-    }
+    warnNonsquarePixels(pngxP, errorLevelP);
 
+    setupGammaCorrection(pngxP, cmdline.gammaSpec, cmdline.gamma, &gamma);
+
+    setupSignificantBits(pngxP, cmdline.alpha, errorLevelP);
+
+    getBackgroundColor(pngxP, cmdline.background, gamma, pngxP->maxval,
+                       &bgColor);
+  
     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;
+    pam.height      = pngx_imageHeight(pngxP);
+    pam.width       = pngx_imageWidth(pngxP);
+    pam.maxval      = pngxP->maxval;
 
-    determineOutputType(info_ptr, cmdline.alpha, bgColor, maxval,
+    determineOutputType(pngxP, cmdline.alpha, bgColor, pngxP->maxval,
                         &pam.format, &pam.depth, pam.tuple_type);
 
-    writeNetpbm(&pam, info_ptr, png_image, bgColor, cmdline.alpha, totalgamma);
+    rasterReaderP = cmdline.byrow ? 
+        reader_createRowByRow(pngxP, ifP) : reader_createAllAtOnce(pngxP, ifP);
 
-    fflush(stdout);
+    writeNetpbm(&pam, pngxP, rasterReaderP, bgColor,
+                cmdline.alpha, gamma);
+
+    reader_destroy(rasterReaderP);
 
-    freePngRaster(png_image, info_ptr);
+    pngx_readEnd(pngxP);
 
-    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+    fflush(stdout);
+
+    pngx_destroy(pngxP);
 }
 
 
@@ -1251,10 +1519,10 @@ convertpng(FILE *             const ifp,
 int 
 main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     FILE * tfP;
-    int errorlevel;
+    int errorLevel;
 
     pm_proginit(&argc, argv);
 
@@ -1262,14 +1530,14 @@ main(int argc, const char *argv[]) {
 
     verbose = cmdline.verbose;
 
-    ifP = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
     if (cmdline.text)
         tfP = pm_openw(cmdline.text);
     else
         tfP = NULL;
 
-    convertpng(ifP, tfP, cmdline, &errorlevel);
+    convertpng(ifP, tfP, cmdline, &errorLevel);
 
     if (tfP)
         pm_close(tfP);
@@ -1277,5 +1545,8 @@ main(int argc, const char *argv[]) {
     pm_close(ifP);
     pm_close(stdout);
 
-    return errorlevel;
+    return errorLevel;
 }
+
+
+
diff --git a/converter/other/pngtopnm.c b/converter/other/pngtopnm.c
deleted file mode 100644
index 205df654..00000000
--- a/converter/other/pngtopnm.c
+++ /dev/null
@@ -1,1255 +0,0 @@
-/*
-** pngtopnm.c -
-** 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>
-**
-** 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 "pnm.h"
-
-#if PNG_LIBPNG_VER >= 10500
-#error Your PNG library (<png.h>) is incompatible with this Netpbm source code.
-#error You need either an older PNG library (older than 1.5) or
-#error newer Netpbm source code (at least 10.55)
-#endif
-
-/* A hack until we can remove direct access to png_info from the program */
-#if PNG_LIBPNG_VER >= 10400
-#define TRANS_ALPHA trans_alpha
-#else
-#define TRANS_ALPHA trans
-#endif
-
-
-enum alpha_handling {ALPHA_NONE, ALPHA_ONLY, ALPHA_MIX};
-
-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 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, 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, "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)
-        pm_error("You cannot specify both -alpha and -mix");
-    else if (alphaSpec)
-        cmdlineP->alpha = ALPHA_ONLY;
-    else if (mixSpec)
-        cmdlineP->alpha = ALPHA_MIX;
-    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 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;
-}
-
-
-
-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 void 
-setXel(xel *               const xelP, 
-       pngcolor            const foreground,
-       pngcolor            const background,
-       enum alpha_handling const alpha_handling,
-       png_uint_16         const alpha) {
-
-    if (alpha_handling == ALPHA_ONLY) {
-        PNM_ASSIGN1(*xelP, alpha);
-    } else {
-        if ((alpha_handling == ALPHA_MIX) && (alpha != maxval)) {
-            double const opacity      = (double)alpha / maxval;
-            double const transparency = 1.0 - opacity;
-
-            pngcolor mix;
-
-            mix.r = foreground.r * opacity + background.r * transparency + 0.5;
-            mix.g = foreground.g * opacity + background.g * transparency + 0.5;
-            mix.b = foreground.b * opacity + background.b * transparency + 0.5;
-            PPM_ASSIGN(*xelP, mix.r, mix.g, mix.b);
-        } else
-            PPM_ASSIGN(*xelP, foreground.r, foreground.g, foreground.b);
-    }
-}
-
-
-
-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 bool
-iscolor(png_color const c) {
-
-    return c.red != c.green || c.green != c.blue;
-}
-
-
-
-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') {
-                unsigned int k;
-                for (k = 0; k < 16; ++k)
-                    putc(' ', tfP);
-            }
-        }
-        putc('\n', tfP);
-    }
-}
-
-
-
-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 (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
-dumpPngInfo(struct pngx * const pngxP) {
-
-    png_info * const info_ptr = pngxP->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_PLTE)
-        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 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,
-                   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 (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) {
-        const png_color_16 * const transColorP = transColor(pngxP);
-
-        /* 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 (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;
-
-    return retval;
-}
-
-
-
-static void
-setupGammaCorrection(struct pngx * const pngxP,
-                     float         const displaygamma,
-                     float *       const totalgammaP) {
-
-    if (displaygamma == -1.0)
-        *totalgammaP = -1.0;
-    else {
-        float imageGamma;
-        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");
-            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(pngxP->png_ptr, displaygamma, imageGamma);
-            *totalgammaP = imageGamma * displaygamma;
-            /* in case of gamma-corrections, sBIT's as in the
-               PNG-file are not valid anymore 
-            */
-            pngxP->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
-setupSignificantBits(struct pngx *       const pngxP,
-                     enum alpha_handling const alpha,
-                     png_uint_16 *       const maxvalP,
-                     int *               const errorLevelP) {
-/*----------------------------------------------------------------------------
-  Figure out what maxval would best express the information in the PNG
-  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) {
-            if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
-                info_ptr->color_type == PNG_COLOR_TYPE_RGB)
-                /* The alpha mask will be all opaque, so maxval 1 is plenty */
-                *maxvalP = 1;
-            else if (paletteHasPartialTransparency(info_ptr))
-                /* 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 << info_ptr->bit_depth) - 1;
-    }
-
-    /* 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 
-    */
-    
-    if (info_ptr->valid & PNG_INFO_sBIT) {
-        switch (alpha) {
-        case ALPHA_MIX:
-            if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
-                info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-                break;
-            if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
-                (info_ptr->valid & PNG_INFO_tRNS)) {
-
-                bool trans_mix;
-                unsigned int i;
-                trans_mix = TRUE;
-                for (i = 0; i < info_ptr->num_trans; ++i)
-                    if (info_ptr->TRANS_ALPHA[i] != 0 && info_ptr->TRANS_ALPHA[i] != 255) {
-                        trans_mix = FALSE;
-                        break;
-                    }
-                if (!trans_mix)
-                    break;
-            }
-
-            /* else fall though to normal case */
-
-        case ALPHA_NONE:
-            if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
-                 info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
-                 info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
-                (info_ptr->sig_bit.red != info_ptr->sig_bit.green ||
-                 info_ptr->sig_bit.red != info_ptr->sig_bit.blue) &&
-                alpha == ALPHA_NONE) {
-                pm_message("This program cannot handle "
-                           "different bit depths for color channels");
-                pm_message("writing file with %d bit resolution",
-                           info_ptr->bit_depth);
-                *errorLevelP = PNMTOPNG_WARNING_LEVEL;
-            } else {
-                if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) &&
-                    (info_ptr->sig_bit.red < 255)) {
-                    unsigned int i;
-                    for (i = 0; i < info_ptr->num_palette; ++i) {
-                        info_ptr->palette[i].red   >>=
-                            (8 - info_ptr->sig_bit.red);
-                        info_ptr->palette[i].green >>=
-                            (8 - info_ptr->sig_bit.green);
-                        info_ptr->palette[i].blue  >>=
-                            (8 - info_ptr->sig_bit.blue);
-                    }
-                    *maxvalP = (1l << info_ptr->sig_bit.red) - 1;
-                    if (verbose)
-                        pm_message ("image has fewer significant bits, "
-                                    "writing file with %d bits per channel", 
-                                    info_ptr->sig_bit.red);
-                } else
-                    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(pngxP->png_ptr, &(info_ptr->sig_bit));
-                        *maxvalP = (1l << info_ptr->sig_bit.red) - 1;
-                        if (verbose)
-                            pm_message("image has fewer significant bits, "
-                                       "writing file with %d "
-                                       "bits per channel", 
-                                       info_ptr->sig_bit.red);
-                    } else 
-                        if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
-                             info_ptr->color_type ==
-                                 PNG_COLOR_TYPE_GRAY_ALPHA) &&
-                            (info_ptr->sig_bit.gray < info_ptr->bit_depth)) {
-                            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, "
-                                           "writing file with %d bits",
-                                           info_ptr->sig_bit.gray);
-                        }
-            }
-            break;
-
-        case ALPHA_ONLY:
-            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(pngxP->png_ptr, &info_ptr->sig_bit);
-                if (verbose)
-                    pm_message ("image has fewer significant bits, "
-                                "writing file with %d bits", 
-                                info_ptr->sig_bit.alpha);
-                *maxvalP = (1l << info_ptr->sig_bit.alpha) - 1;
-            }
-            break;
-
-        }
-    }
-}
-
-
-
-static bool
-imageHasColor(struct pngx * const pngxP) {
-
-    bool retval;
-
-    if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
-        pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-
-        retval = FALSE;
-    else if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
-        bool foundColor;
-        unsigned int i;
-            
-        for (i = 0, foundColor = FALSE;
-             i < pngxP->info_ptr->num_palette && !foundColor;
-             ++i) {
-            if (iscolor(pngxP->info_ptr->palette[i]))
-                foundColor = TRUE;
-        }
-        retval = foundColor;
-    } else
-        retval = TRUE;
-
-    return retval;
-}
-
-
-
-static void
-determineOutputType(struct pngx *       const pngxP,
-                    enum alpha_handling const alphaHandling,
-                    pngcolor            const bgColor,
-                    xelval              const maxval,
-                    int *               const pnmTypeP) {
-
-    if (alphaHandling != ALPHA_ONLY &&
-        (imageHasColor(pngxP) || !isGrayscale(bgColor)))
-        *pnmTypeP = PPM_TYPE;
-    else {
-        if (maxval > 1)
-            *pnmTypeP = PGM_TYPE;
-        else
-            *pnmTypeP = PBM_TYPE;
-    }
-}
-
-
-
-static void
-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.
-   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 (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 (pngxP->info_ptr->color_type) {
-        case PNG_COLOR_TYPE_GRAY:
-        case PNG_COLOR_TYPE_GRAY_ALPHA:
-            bgColorP->r = bgColorP->g = bgColorP->b = 
-                gamma_correct(pngxP->info_ptr->background.gray, totalgamma);
-            break;
-        case PNG_COLOR_TYPE_PALETTE: {
-            png_color const rawBgcolor = 
-                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);
-        }
-        break;
-        case PNG_COLOR_TYPE_RGB:
-        case PNG_COLOR_TYPE_RGB_ALPHA: {
-            png_color_16 const rawBgcolor = pngxP->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;
-}
-
-
-
-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 pnmType,
-         struct pngx *       const pngxP,
-         png_byte **         const pngRaster,
-         pngcolor            const bgColor,
-         enum alpha_handling const alphaHandling,
-         double              const totalgamma) {
-/*----------------------------------------------------------------------------
-   Write a PNM of either the image or the alpha mask, according to
-   'alphaHandling' that is in the PNG image described by *pngxP and
-   pngRaster[][].
-
-   '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)",
-                   pnmType == PBM_TYPE ? "PBM" :
-                   pnmType == PGM_TYPE ? "PGM" :
-                   pnmType == PPM_TYPE ? "PPM" :
-                   "UNKNOWN!", 
-                   maxval);
-    
-    xelrow = pnm_allocrow(pngxP->info_ptr->width);
-
-    pnm_writepnminit(stdout,
-                     pngxP->info_ptr->width, pngxP->info_ptr->height, maxval,
-                     pnmType, plainFalse);
-
-    for (row = 0; row < pngxP->info_ptr->height; ++row) {
-        makeXelRow(xelrow, maxval, pnmType, pngxP, pngRaster[row], bgColor,
-                   alphaHandling, totalgamma);
-
-        pnm_writepnmrow(ofP, xelrow, pngxP->info_ptr->width, maxval,
-                        pnmType, plainFalse);
-    }
-    pnm_freerow (xelrow);
-}
-
-
-
-static void 
-convertpng(FILE *             const ifP, 
-           FILE *             const tfP, 
-           struct cmdlineInfo const cmdline,
-           int *              const errorLevelP) {
-
-    png_byte ** pngRaster;
-    int pnmType;
-    pngcolor bgColor;
-    float totalgamma;
-    jmp_buf jmpbuf;
-    struct pngx * pngxP;
-
-    *errorLevelP = 0;
-
-    if (setjmp(jmpbuf))
-        pm_error ("setjmp returns error condition");
-
-    pngx_createRead(&pngxP, &jmpbuf);
-
-    readPng(pngxP, ifP, &pngRaster);
-
-    if (verbose)
-        dumpPngInfo(pngxP);
-
-    if (cmdline.time)
-        showTime(pngxP);
-    if (tfP)
-        saveText(pngxP, tfP);
-
-    warnNonsquarePixels(pngxP, errorLevelP);
-
-    setupGammaCorrection(pngxP, cmdline.gamma, &totalgamma);
-
-    setupSignificantBits(pngxP, cmdline.alpha, &maxval, errorLevelP);
-
-    getBackgroundColor(pngxP, cmdline.background, totalgamma, maxval,
-                       &bgColor);
-
-    determineOutputType(pngxP, cmdline.alpha, bgColor, maxval, &pnmType);
-
-    writePnm(stdout, maxval, pnmType, pngxP, pngRaster, bgColor, 
-             cmdline.alpha, totalgamma);
-
-    fflush(stdout);
-
-    freePngRaster(pngRaster, pngxP);
-
-    pngx_destroy(pngxP);
-}
-
-
-
-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/pngtxt.c b/converter/other/pngtxt.c
index bbbec099..e02ee227 100644
--- a/converter/other/pngtxt.c
+++ b/converter/other/pngtxt.c
@@ -1,122 +1,251 @@
+#define HAVE_PNGLIB_WITH_ITXT 0
+
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
+
 #include <png.h>
 
+#include "mallocvar.h"
 #include "nstring.h"
+#include "pngx.h"
 #include "pngtxt.h"
 #include "pm.h"
-#include "mallocvar.h"
-
-#define MAXCOMMENTS 256
 
 
 
 static void
-readOffKey(char           const textline[],
-           unsigned int   const lineLength,
-           unsigned int * const cursorP,
-           char **        const keyP) {
+readToken(char           const textline[],
+          unsigned int   const lineLength,
+          unsigned int * const cursorP,
+          const char **  const tokenP) {
+/*----------------------------------------------------------------------------
+   Read a token from 'textline' (whose length is 'lineLength'), assuming the
+   cursor is positioned to it now, leaving the cursor positioned after it.
 
-    /* Get the comment key */
+   Tokens are delimited by white space.  We don't skip any white space before
+   or after the token.  Ergo, if we are positioned to white space right now,
+   the token we read is a null string.
+-----------------------------------------------------------------------------*/
+    char * tokenBuffer;
     char * cp;
     unsigned int cursor;
 
     cursor = *cursorP;
     
-    MALLOCARRAY(cp, lineLength + 1);  /* leave room for terminating NUL */
-    if (cp == NULL) 
-        pm_error("Unable to allocate memory for text chunks");
-    
-    *keyP = cp;
+    MALLOCARRAY(tokenBuffer, lineLength + 1);
+    /* leave room for terminating NUL */
+    if (tokenBuffer == NULL) 
+        pm_error("Unable to allocate memory for a %u-character "
+                 "text string file line", lineLength);
+
+    cp = &tokenBuffer[0];  /* initial value */
     
     if (textline[0] == '"') {
-        ++cursor;  /* skip past opening " */
+        ++cursor;  /* skip past opening quotation mark */
         while (textline[cursor] != '"') {
-            if (textline[cursor] == '\0') {
-                *cp = '\0';
-                pm_error("Invalid comment file format:  keyword contains "
+            if (cursor >= lineLength)
+                pm_error("Invalid text string file format.  Line ends in "
+                         "the middle of a quoted token.  Text at the end of "
+                         "the line is '%s'", tokenBuffer);
+            if (textline[cursor] == '\0')
+                pm_error("Invalid text string file format:  Token contains "
                          "a NUL character.  Text leading up to the NUL "
-                         "character is '%s'", *keyP);
-            }
+                         "character is '%s'", tokenBuffer);
             *(cp++) = textline[cursor++];
         }
-        ++cursor;  /* skip past closing " */
+        ++cursor;  /* skip past closing quotation mark */
     } else {
-        while (cursor < lineLength && 
-               textline[cursor] != ' '  && textline[cursor] != '\t' &&
-               textline[cursor] != '\0')
+        while ((cursor < lineLength) && 
+               (textline[cursor] != ' ') && (textline[cursor] != '\t')) {
+            
+            if (textline[cursor] == '\0')
+                pm_error("Invalid text string file format:  Token contains "
+                         "a NUL character.  Text leading up to the NUL "
+                         "character is '%s'", tokenBuffer);
             *(cp++) = textline[cursor++];
+        }
     }
     *cp++ = '\0';
 
     *cursorP = cursor;
+
+    *tokenP = tokenBuffer;
 }
 
 
 
 
 static void
-startComment(struct png_text_struct * const commentP, 
-             char                     const textline[],
-             unsigned int             const lineLength,
-             bool                     const compressed) {
+skipWhiteSpace(char           const textline[],
+               unsigned int   const lineLength,
+               unsigned int * const cursorP) {
 /*----------------------------------------------------------------------------
-   Assuming 'textline' is the first line of a comment in a comment file,
-   put the information from it in the comment record *commentP.
-   Use the text on this line as the comment text, even though the true
-   comment text may include text from subsequent continuation lines as
-   well.
+   Move *cursorP past white space (or, for some reason, NUL characters),
+   in 'textline', which is 'lineLength' long.
+-----------------------------------------------------------------------------*/
+    unsigned int cursor;
 
-   'textline' is not NUL-terminated.  Its length is 'lineLength', and
-   it is at least one character long.  'textline' does not contain a
-   newline character.
+    cursor = *cursorP;  /* initial value */
+
+    while (cursor < lineLength && 
+           (textline[cursor] == ' ' || textline[cursor] == '\t' ||
+            textline[cursor] == '\0'))
+        ++cursor;
 
-   'compressed' means the comment text is compressed.
+    *cursorP = cursor;
+}
+
+
+
+static void
+readTextString(char          const textline[],
+               unsigned int  const lineLength,
+               unsigned int  const startPos,
+               png_charp *   const textStringP,
+               png_size_t *  const textStringLengthP) {
+/*----------------------------------------------------------------------------
+  Extract the text string at 'startPos' in the buffer 'textline', whose
+  length is 'lineLength'.  Return it in newly malloced storage with a
+  pointer to that storage as 'textString' and the size of the text as
+  *textStringLengthP.
 -----------------------------------------------------------------------------*/
-    unsigned int cursor;
+    char * cp;
+
+    MALLOCARRAY(cp, lineLength + 1);  /* incl '\0' */
+    if (!cp) 
+        pm_error("Unable to allocate memory for text chunks");
+
+    memcpy(cp, textline + startPos, lineLength - startPos);
+    cp[lineLength - startPos] = '\0';  /* for safety - not part of text */
+    *textStringP = cp;
+    *textStringLengthP = lineLength - startPos;
+}
+
 
-    /* the following is a not that accurate check on Author or Title */
-    if ((!compressed) || (textline[0] == 'A') || (textline[0] == 'T'))
-        commentP->compression = -1;
-    else
-        commentP->compression = 0;
+
+static void
+startTextChunkEngl(png_text *   const textChunkP, 
+                   char         const textline[],
+                   unsigned int const lineLength,
+                   bool         const isCompressed,
+                   bool         const verbose) {
+/*----------------------------------------------------------------------------
+   Assuming 'textline' is the first line of an entry in an English text
+   string file, put the information from it in the comment record *textChunkP.
+   Use the text on this line as the comment text, even though the true text
+   string may include text from subsequent continuation lines as well.
+
+   'textline' is not NUL-terminated.  Its length is 'lineLength', and it is at
+   least one character long.  'textline' does not contain a newline character.
+
+   'isCompressed' means it is a compressed text chunk.
+-----------------------------------------------------------------------------*/
+    unsigned int cursor;
 
     cursor = 0;
 
-    readOffKey(textline, lineLength, &cursor, &commentP->key);
+    {
+        const char * key;
+
+        readToken(textline, lineLength, &cursor, &key);
+
+        pngx_setTextKey(textChunkP, key);
+
+        pm_strfree(key);
+    }
+
+    skipWhiteSpace(textline, lineLength, &cursor);
+
+    pngx_setTextLang(textChunkP, NULL);
+
+    readTextString(textline, lineLength, cursor, &textChunkP->text,
+                   &textChunkP->text_length);
+
+    textChunkP->compression =
+        isCompressed ? PNG_TEXT_COMPRESSION_zTXt : PNG_TEXT_COMPRESSION_NONE;
+}
+
+
+
+static void
+startTextChunkIntl(png_text *   const textChunkP, 
+                   char         const textline[],
+                   unsigned int const lineLength,
+                   bool         const isCompressed,
+                   bool         const verbose) {
+/*----------------------------------------------------------------------------
+  Assuming 'textline' is the first line of an entry in an international (not
+  English) text string file, put the information from it in the text chunk
+  *textChunkP.  Use the text on this line as the text string, even though the
+  true text string may include text from subsequent continuation lines as
+  well.
+
+  'textline' is not NUL-terminated.  Its length is 'lineLength', and it is at
+  least one character long.  'textline' does not contain a newline character.
+
+  'isInternational' means it is an international (i.e. non-English) text
+  chunk.
+
+  Leave the language attribute (textChunkP->lang) unset.
+-----------------------------------------------------------------------------*/
+    unsigned int cursor;
+
+    cursor = 0;  /* Initial value */
 
-    /* skip over delimiters between key and comment text */
-    while (cursor < lineLength && 
-           (textline[cursor] == ' ' || textline[cursor] == '\t' ||
-           textline[cursor] == '\0'))
-        ++cursor;
-    
     {
-        /* Get the first line of the comment text */
-        unsigned int const startPos = cursor;
-        char *cp;
-
-        MALLOCARRAY(cp, lineLength+1);  /* leave room for safety NUL */
-        if (!cp) 
-            pm_error("Unable to allocate memory for text chunks");
-
-        memcpy(cp, textline + startPos, lineLength - startPos);
-        cp[lineLength - startPos] = '\0';  /* for safety - not part of text */
-        commentP->text = cp;
-        commentP->text_length = lineLength - startPos;
+        const char * key;
+
+        readToken(textline, lineLength, &cursor, &key);
+
+        pngx_setTextKey(textChunkP, key);
+
+        pm_strfree(key);
     }
+
+    skipWhiteSpace(textline, lineLength, &cursor);
+
+    {
+        const char * isoLang;
+
+        readToken(textline, lineLength, &cursor, &isoLang);
+
+        pngx_setTextLang(textChunkP, isoLang);
+
+        pm_strfree(isoLang);
+    }
+
+    skipWhiteSpace(textline, lineLength, &cursor);
+
+    {
+        const char * langKey;
+    
+        readToken(textline, lineLength, &cursor, &langKey);
+
+        pngx_setTextLangKey(textChunkP, langKey);
+
+        pm_strfree(langKey);
+    }
+
+    skipWhiteSpace(textline, lineLength, &cursor);
+
+    /* Beginning of text string (continuation lines may follow) */
+    readTextString(textline, lineLength, cursor, &textChunkP->text,
+                   &textChunkP->text_length);
+
+    textChunkP->compression = 
+        isCompressed ? PNG_ITXT_COMPRESSION_zTXt :PNG_ITXT_COMPRESSION_NONE;
 }
 
 
 
 static void
-continueComment(struct png_text_struct * const commentP, 
-                char                     const textline[],
-                unsigned int             const lineLength) {
+continueTextString(png_text *   const textChunkP, 
+                   char         const textline[],
+                   unsigned int const lineLength) {
 /*----------------------------------------------------------------------------
-   Update the comment record *commentP by adding to it the text
-   from textline[], which is a continuation line from a comment file.
+   Update the text chunk *textChunkP by adding to it the text from
+   textline[], which is a continuation line from a text string file.
 
    'textline' is not NUL-terminated.  Its length is 'lineLength', and
    it is at least one character long.  'textline' does not contain a
@@ -125,29 +254,27 @@ continueComment(struct png_text_struct * const commentP,
     unsigned int cursor;  /* cursor into textline[] */
 
     unsigned int const newTextLength =
-        commentP->text_length + lineLength + 1 + 1;
+        textChunkP->text_length + lineLength + 1 + 1;
 
-    REALLOCARRAY(commentP->text, newTextLength);
+    REALLOCARRAY(textChunkP->text, newTextLength);
 
-    if (commentP->text == NULL)
-        pm_error("Unable to allocate %u bytes of memory for comment chunk",
+    if (textChunkP->text == NULL)
+        pm_error("Unable to allocate %u bytes of memory for text string",
                  newTextLength);
 
-    commentP->text[commentP->text_length++] = '\n';
+    textChunkP->text[textChunkP->text_length++] = '\n';
 
-    /* Skip past leading delimiter characters in file line */
-    cursor = 0;
-    while (textline[cursor] == ' ' || textline[cursor] == '\t' ||
-           textline[cursor] == '\0')
-        ++cursor;
+    cursor = 0; 
+
+    skipWhiteSpace(textline, lineLength, &cursor);
 
-    memcpy(commentP->text + commentP->text_length,
+    memcpy(textChunkP->text + textChunkP->text_length,
            textline + cursor,
            lineLength - cursor);
 
-    commentP->text_length += lineLength - cursor;
+    textChunkP->text_length += lineLength - cursor;
 
-    commentP->text[commentP->text_length] = '\0';  /* for safety */
+    textChunkP->text[textChunkP->text_length] = '\0';  /* for safety */
 }
 
 
@@ -171,16 +298,16 @@ getFileLine(FILE *         const fileP,
 -----------------------------------------------------------------------------*/
     char * textline;  /* malloc'ed */
     unsigned int cursor;  /* cursor into textline[] */
-    unsigned int allocated;
+    unsigned int allocatedSz;
         /* The number of characters of space that are allocated for
            'textline' 
         */
     bool eol;
     bool gotSomething;
 
-    allocated = 128;  /* initial value */
+    allocatedSz = 128;  /* initial value */
 
-    MALLOCARRAY(textline, allocated);
+    MALLOCARRAY(textline, allocatedSz);
     if (textline == NULL)
         pm_error("Unable to allocate buffer to read a line of a file.");
     
@@ -197,9 +324,10 @@ getFileLine(FILE *         const fileP,
         if (c == '\n' || c == EOF)
             eol = TRUE;
         else {
-            if (cursor > allocated - 1 - 1) { /* leave space for safety NUL */
-                allocated *= 2;
-                REALLOCARRAY(textline, allocated);
+            /* leave space for safety NUL */
+            if (cursor > allocatedSz - 1 - 1) {
+                allocatedSz *= 2;
+                REALLOCARRAY(textline, allocatedSz);
                 if (textline == NULL)
                     pm_error("Unable to allocate buffer to read a line of "
                              "a file.");
@@ -222,93 +350,153 @@ getFileLine(FILE *         const fileP,
 
 static void
 handleArrayAllocation(png_text **    const arrayP,
-                      unsigned int * const allocatedCommentsP,
-                      unsigned int   const commentIdx) {
+                      unsigned int * const allocatedChunkCtP,
+                      unsigned int   const chunkIdx) {
 
-    if (commentIdx >= *allocatedCommentsP) {
-        *allocatedCommentsP *= 2;
-        REALLOCARRAY(*arrayP, *allocatedCommentsP);
+    if (chunkIdx >= *allocatedChunkCtP) {
+        *allocatedChunkCtP *= 2;
+        REALLOCARRAY(*arrayP, *allocatedChunkCtP);
         if (*arrayP == NULL) 
-            pm_error("unable to allocate memory for comment array");
+            pm_error("unable to allocate memory for %u text chunks",
+                *allocatedChunkCtP);
     }
 }
 
 
+
+static bool
+isContinuationLine(const char * const line) {
+/*----------------------------------------------------------------------------
+   Text line 'line', if it is from a text string file, is a continuation of a
+   text string started in an earlier line.
+
+   What identifies a line as a continuation line is that it starts with
+   a space or tab.
+-----------------------------------------------------------------------------*/
+    return line[0] == ' ' || line[0] == '\t';
+}
+
+
+
+static void
+reportChunkCt(bool         const ztxt,
+              bool         const itxt,
+              unsigned int const chunkCt) {
+
+    const char * chunkType;
+
+    if (itxt)
+        chunkType = "iTXt";
+    else {
+        if (ztxt)
+            chunkType = "zTXt";
+        else
+            chunkType = "tEXt";
+    }
+
+    pm_message("Writing %u %s chunks", chunkCt, chunkType);
+}
+
+
+
 /******************************************************************************
                             EXTERNAL SUBROUTINES
 ******************************************************************************/
 
 
 void 
-pnmpng_read_text (png_info * const info_ptr, 
-                  FILE *     const tfp, 
-                  bool       const ztxt,
-                  bool       const verbose) {
-
-    const char * textline;
-    unsigned int lineLength;
-    unsigned int commentIdx;
-    bool noCommentsYet;
+pngtxt_addChunk(struct pngx * const pngxP,
+                FILE *        const tfP, 
+                bool          const ztxt,
+                bool          const itxt,
+                bool          const verbose) {
+/*----------------------------------------------------------------------------
+   Add text chunks (tEXt, zTXt, or iTXt) to the PNG image represented by
+   *pngxP as directed by file *tfP.
+
+   'itxt' means to make them international language (iTXt) chunks.  Otherwise
+   they are either tEXt or zTXt chunks, depending upon 'ztxt'.
+
+   'ztxt' means to make the text compressed.  If the chunks are not
+   international (i.e. 'itxt' is false), this means the chunks are zTXt chunks
+   instead of 'tEXt' chunks.
+-----------------------------------------------------------------------------*/
+    bool noChunksYet;
     bool eof;
-    unsigned int allocatedComments;
-        /* Number of entries currently allocated for the info_ptr->text
-           array 
-        */
+    png_textp text;  /* An array; one chunk per element */
+    unsigned int chunkCt;
+        /* Number of chunks we have completed in the 'text' array */
+    unsigned int allocatedChunkCt;
+        /* Number of entries currently allocated for the PNG text array */
+
+    /* In an international text string file, the first entry tells the
+       language of all of the chunks, by having key 'Language'.
+    */
 
-    allocatedComments = 256;  /* initial value */
+    allocatedChunkCt = 256;  /* initial value */
 
-    MALLOCARRAY(info_ptr->text, allocatedComments);
-    if (info_ptr->text == NULL) 
-        pm_error("unable to allocate memory for comment array");
+    MALLOCARRAY(text, allocatedChunkCt);
+    if (text == NULL) 
+        pm_error("unable to allocate memory for text chunk array");
 
-    commentIdx = 0;
-    noCommentsYet = TRUE;
-   
-    eof = FALSE;
-    while (!eof) {
-        getFileLine(tfp, &textline, &lineLength);
+    for (chunkCt = 0, noChunksYet = true, eof = false; !eof; ) {
+        const char * textline;
+        unsigned int lineLength;
+
+        getFileLine(tfP, &textline, &lineLength);
         if (textline == NULL)
-            eof = TRUE;
+            eof = true;
         else {
             if (lineLength == 0) {
                 /* skip this empty line */
             } else {
-                handleArrayAllocation(&info_ptr->text, &allocatedComments,
-                                      commentIdx);
-                if ((textline[0] != ' ') && (textline[0] != '\t')) {
-                    /* Line doesn't start with white space, which
-                       means it starts a new comment.  
-                    */
-                    if (noCommentsYet) {
-                        /* No previous comment to move past */
-                    } else
-                        ++commentIdx;
-                    noCommentsYet = FALSE;
+                handleArrayAllocation(&text, &allocatedChunkCt, chunkCt);
+
+                if (!isContinuationLine(textline)) {
+                    png_text * textChunkP;
 
-                    startComment(&info_ptr->text[commentIdx], 
-                                 textline, lineLength, ztxt);
+                    if (noChunksYet) {
+                        /* No previous chunk to move past */
+                    } else
+                        ++chunkCt;
+                    noChunksYet = false;
+                    
+                    textChunkP = &text[chunkCt];
+                
+                    if (itxt)
+                        startTextChunkIntl(textChunkP, 
+                                           textline, lineLength, ztxt,
+                                           verbose);
+                    else
+                        startTextChunkEngl(textChunkP, 
+                                           textline, lineLength, ztxt,
+                                           verbose);
                 } else {
+                    png_text * const textChunkP = &text[chunkCt];
+
                     /* Line starts with whitespace, which means it is
-                       a continuation of the current comment.
+                       a continuation of the current text string.
                     */
-                    if (noCommentsYet)
-                        pm_error("Invalid comment file format: "
+                    if (noChunksYet)
+                        pm_error("Invalid text string file format: "
                                  "first line is a continuation line! "
                                  "(It starts with whitespace)");
-                    continueComment(&info_ptr->text[commentIdx], 
-                                    textline, lineLength);
+                    continueTextString(textChunkP, textline, lineLength);
                 }
             }
-            strfree(textline);
+            pm_strfree(textline);
         }
-    } 
-    if (noCommentsYet)
-        info_ptr->num_text = 0;
-    else
-        info_ptr->num_text = commentIdx + 1;
+    }
+    if (!noChunksYet)
+        ++chunkCt;
 
     if (verbose)
-        pm_message("%d comments placed in text chunk", info_ptr->num_text);
+        reportChunkCt(ztxt, itxt, chunkCt);
+
+    if (chunkCt > 0)
+        pngx_setText(pngxP, text, chunkCt);
+
+    free(text);
 }
 
 
diff --git a/converter/other/pngtxt.h b/converter/other/pngtxt.h
index ae7fe2c4..3e6ff2af 100644
--- a/converter/other/pngtxt.h
+++ b/converter/other/pngtxt.h
@@ -1,13 +1,17 @@
 #ifndef PNGTXT_H_INCLUDED
 #define PNGTXT_H_INCLUDED
 
-#include "pm_c_util.h"
+#include <stdbool.h>
+#include <stdio.h>
 #include <png.h>
 
+struct pngx;
+
 void 
-pnmpng_read_text (png_info * const info_ptr, 
-                  FILE *     const tfp, 
-                  bool const ztxt,
-                  bool const verbose);
+pngtxt_addChunk(struct pngx * const pngxP,
+                FILE *        const tfp, 
+                bool          const ztxt,
+                bool          const itxt,
+                bool          const verbose);
 
 #endif
diff --git a/converter/other/pngx.c b/converter/other/pngx.c
new file mode 100644
index 00000000..185b4a27
--- /dev/null
+++ b/converter/other/pngx.c
@@ -0,0 +1,757 @@
+#include <assert.h>
+#include <png.h>
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "pm.h"
+
+/* <png.h> defines (or doesn't) PNG_iTXt_SUPPORTED to tell us whether libpng
+   has facilities related to PNG iTXt chunks.
+*/
+#ifdef PNG_iTXt_SUPPORTED
+  #define HAVE_PNGLIB_WITH_ITXT 1
+#else
+  #define HAVE_PNGLIB_WITH_ITXT 0
+#endif
+
+#include "pngx.h"
+
+
+static void
+errorHandler(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);
+}
+
+
+
+void
+pngx_create(struct pngx ** const pngxPP,
+            pngx_rw        const rw,
+            jmp_buf *      const jmpbufP) {
+
+    struct pngx * pngxP;
+
+    MALLOCVAR(pngxP);
+
+    if (!pngxP)
+        pm_error("Failed to allocate memory for PNG object");
+    else {
+        pngxP->numPassesRequired = 1;
+
+        switch(rw) {
+        case PNGX_READ:
+            pngxP->png_ptr = png_create_read_struct(
+                PNG_LIBPNG_VER_STRING,
+                jmpbufP, errorHandler, NULL);
+            break;
+        case PNGX_WRITE:
+            pngxP->png_ptr = png_create_write_struct(
+                PNG_LIBPNG_VER_STRING,
+                jmpbufP, errorHandler, NULL);
+            break;
+        }
+        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;
+        }
+        pngxP->rw = rw;
+    }
+}
+
+
+
+void
+pngx_destroy(struct pngx * const pngxP) {
+
+    switch(pngxP->rw) {
+    case PNGX_READ:
+        png_destroy_read_struct(&pngxP->png_ptr, &pngxP->info_ptr, NULL);
+        break;
+    case PNGX_WRITE:
+        png_destroy_write_struct(&pngxP->png_ptr, &pngxP->info_ptr);
+        break;
+    }
+
+    free(pngxP);
+}
+
+
+
+bool
+pngx_chunkIsPresent(struct pngx * const pngxP,
+                    uint32_t      const chunkType) {
+
+    return png_get_valid(pngxP->png_ptr, pngxP->info_ptr, chunkType);
+}
+
+
+
+unsigned int
+pngx_bitDepth(struct pngx * const pngxP) {
+
+    return png_get_bit_depth(pngxP->png_ptr, pngxP->info_ptr);
+}
+
+
+
+png_color_16
+pngx_bkgd(struct pngx * const pngxP) {
+
+    png_color_16 * colorP;
+
+    png_get_bKGD(pngxP->png_ptr, pngxP->info_ptr, &colorP);
+
+    return *colorP;
+}
+
+
+
+png_byte
+pngx_colorType(struct pngx * const pngxP) {
+
+    return png_get_color_type(pngxP->png_ptr, pngxP->info_ptr);
+}
+
+
+
+png_byte
+pngx_filterType(struct pngx * const pngxP) {
+
+    return png_get_filter_type(pngxP->png_ptr, pngxP->info_ptr);
+}
+
+
+
+double
+pngx_gama(struct pngx * const pngxP) {
+
+    double retval;
+
+    png_get_gAMA(pngxP->png_ptr, pngxP->info_ptr, &retval);
+
+    return retval;
+}
+
+
+
+uint32_t
+pngx_imageHeight(struct pngx * const pngxP) {
+
+    return png_get_image_height(pngxP->png_ptr, pngxP->info_ptr);
+}
+
+
+
+uint32_t
+pngx_imageWidth(struct pngx * const pngxP) {
+
+    return png_get_image_width(pngxP->png_ptr, pngxP->info_ptr);
+}
+
+
+
+png_byte
+pngx_interlaceType(struct pngx * const pngxP) {
+
+    return png_get_interlace_type(pngxP->png_ptr, pngxP->info_ptr);
+}
+
+
+
+struct pngx_plte
+pngx_plte(struct pngx * const pngxP) {
+
+    struct pngx_plte retval;
+
+    int size;
+
+    png_get_PLTE(pngxP->png_ptr, pngxP->info_ptr, &retval.palette, &size);
+
+    assert(size >= 0);
+
+    retval.size = size;
+
+    return retval;
+}
+
+
+
+png_color_8
+pngx_sbit(struct pngx *   const pngxP) {
+
+    png_color_8 * sbitP;
+
+    png_get_sBIT(pngxP->png_ptr, pngxP->info_ptr, &sbitP);
+
+    return *sbitP;
+}
+
+
+
+struct pngx_text
+pngx_text(struct pngx * const pngxP) {
+
+    struct pngx_text retval;
+
+    int size;
+
+    png_get_text(pngxP->png_ptr, pngxP->info_ptr, &retval.line, &size);
+
+    assert(size >= 0);
+
+    retval.size = size;
+
+    return retval;
+}
+
+
+
+png_time
+pngx_time(struct pngx * const pngxP) {
+
+    png_time * timeP;
+
+    png_get_tIME(pngxP->png_ptr, pngxP->info_ptr, &timeP);
+
+    return *timeP;
+}
+
+
+
+struct pngx_trns
+pngx_trns(struct pngx * const pngxP) {
+
+    struct pngx_trns retval;
+
+    int numTrans;
+    png_color_16 * transColorP;
+
+    png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr,
+                 &retval.trans, &numTrans, &transColorP);
+
+    assert(numTrans >= 0);
+
+    retval.numTrans = numTrans;
+    retval.transColor = *transColorP;
+
+    return retval;
+}
+
+
+
+uint32_t
+pngx_xPixelsPerMeter(struct pngx * const pngxP) {
+/*----------------------------------------------------------------------------
+  Horizontal pixel density in pixel per meter; 0 if unknown.
+-----------------------------------------------------------------------------*/
+    return png_get_x_pixels_per_meter(pngxP->png_ptr, pngxP->info_ptr);
+}
+
+
+
+float
+pngx_pixelAspectRatio(struct pngx * const pngxP) {
+/*----------------------------------------------------------------------------
+  Aspect ratio - y/x.  0.0 if unknown
+-----------------------------------------------------------------------------*/
+    return png_get_pixel_aspect_ratio(pngxP->png_ptr, pngxP->info_ptr);
+}
+
+
+
+bool
+pngx_pixelAspectRatioIsKnown(struct pngx * const pngxP) {
+/*----------------------------------------------------------------------------
+  There is pixel aspect ratio information in the PNG image.
+-----------------------------------------------------------------------------*/
+    return png_get_pixel_aspect_ratio(pngxP->png_ptr, pngxP->info_ptr) != 0.0;
+}
+
+
+
+uint32_t
+pngx_yPixelsPerMeter(struct pngx * const pngxP) {
+/*----------------------------------------------------------------------------
+  Vertical pixel density in pixel per meter; 0 if unknown.
+-----------------------------------------------------------------------------*/
+    return png_get_y_pixels_per_meter(pngxP->png_ptr, pngxP->info_ptr);
+}
+
+
+
+void
+pngx_removeChunk(struct pngx * const pngxP,
+                 uint32_t      const chunkType) {
+
+    png_set_invalid(pngxP->png_ptr, pngxP->info_ptr, chunkType);
+}
+
+
+
+void
+pngx_setBkgdPalette(struct pngx * const pngxP,
+                    unsigned int  const backgroundIndex) {
+
+    png_color_16 background;
+
+    background.index = backgroundIndex;
+
+    png_set_bKGD(pngxP->png_ptr, pngxP->info_ptr, &background);
+}
+
+
+
+void
+pngx_setBkgdRgb(struct pngx * const pngxP,
+                png_color_16  const backgroundArg) {
+
+    png_color_16 background;
+
+    background = backgroundArg;
+
+    png_set_bKGD(pngxP->png_ptr, pngxP->info_ptr, &background);
+}
+
+
+
+void
+pngx_setChrm(struct pngx *      const pngxP,
+             struct pngx_chroma const chroma) {
+
+    png_set_cHRM(pngxP->png_ptr, pngxP->info_ptr, 
+                 chroma.wx, chroma.wy,
+                 chroma.rx, chroma.ry,
+                 chroma.gx, chroma.gy,
+                 chroma.bx, chroma.by);
+}
+
+
+
+const char *
+pngx_srgbIntentDesc(pngx_srgbIntent const srgbIntent) {
+
+    switch (srgbIntent) {
+    case PNGX_PERCEPTUAL:            return "PERCEPTUAL";
+    case PNGX_RELATIVE_COLORIMETRIC: return "RELATIVE COLORIMETRIC";
+    case PNGX_SATURATION:            return "SATURATION";
+    case PNGX_ABSOLUTE_COLORIMETRIC: return "ABSOLUTE_COLORIMETRIC";
+    }
+    assert(false);
+}
+
+
+
+static int
+const libpngSrgbIntentCode(pngx_srgbIntent const srgbIntent) {
+
+    switch (srgbIntent) {
+    case PNGX_PERCEPTUAL:            return 0;
+    case PNGX_RELATIVE_COLORIMETRIC: return 1;
+    case PNGX_SATURATION:            return 2;
+    case PNGX_ABSOLUTE_COLORIMETRIC: return 3;
+    }
+
+    assert(false);  /* All cases above return */
+}
+
+
+
+void
+pngx_setSrgb(struct pngx *   const pngxP,
+             pngx_srgbIntent const srgbIntent) {
+
+    png_set_sRGB(pngxP->png_ptr, pngxP->info_ptr,
+                 libpngSrgbIntentCode(srgbIntent));
+}
+
+
+
+void
+pngx_setCompressionSize(struct pngx * const pngxP,
+                        unsigned int  const bufferSize) {
+
+#if PNG_LIBPNG_VER >= 10009
+    png_set_compression_buffer_size(pngxP->png_ptr, bufferSize);
+#else
+    pm_error("Your PNG library cannot set the compression buffer size.  "
+             "You need at least Version 1.0.9 of Libpng; you have Version %s",
+             PNG_LIBPNG_VER_STRING);
+#endif
+}
+
+
+
+void
+pngx_setFilter(struct pngx * const pngxP,
+               int           const filterSet) {
+
+    png_set_filter(pngxP->png_ptr, 0, filterSet);
+}
+
+
+
+void
+pngx_setGama(struct pngx * const pngxP,
+             float         const fileGamma) {
+
+    png_set_gAMA(pngxP->png_ptr, pngxP->info_ptr, fileGamma);
+}
+
+
+
+void
+pngx_setGamma(struct pngx * const pngxP,
+              float         const screenGamma,
+              float         const imageGamma) {
+
+    png_set_gamma(pngxP->png_ptr, screenGamma, imageGamma);
+}
+
+
+
+void
+pngx_setHist(struct pngx * const pngxP,
+             png_uint_16 * const histogram) {
+
+    png_set_hIST(pngxP->png_ptr, pngxP->info_ptr, histogram);
+}
+
+
+
+void
+pngx_setIhdr(struct pngx * const pngxP,
+             unsigned int  const width,
+             unsigned int  const height,
+             unsigned int  const bitDepth,
+             int           const colorType,
+             int           const interlaceMethod,
+             int           const compressionMethod,
+             int           const filterMethod) {
+
+    png_set_IHDR(pngxP->png_ptr, pngxP->info_ptr, width, height,
+                 bitDepth, colorType, interlaceMethod, compressionMethod,
+                 filterMethod);
+}
+
+
+
+void
+pngx_setInterlaceHandling(struct pngx * const pngxP) {
+
+    pngxP->numPassesRequired = png_set_interlace_handling(pngxP->png_ptr);
+}
+
+
+
+void
+pngx_setPacking(struct pngx * const pngxP) {
+
+    png_set_packing(pngxP->png_ptr);
+}
+
+
+
+void
+pngx_setPhys(struct pngx *    const pngxP,
+             struct pngx_phys const phys) {
+
+    png_set_pHYs(pngxP->png_ptr, pngxP->info_ptr, 
+                 phys.x, phys.y, phys.unit);
+}
+
+
+
+void
+pngx_setPlte(struct pngx * const pngxP,
+             png_color *   const palette,
+             unsigned int  const paletteSize) {
+
+    png_set_PLTE(pngxP->png_ptr, pngxP->info_ptr, palette, paletteSize);
+}
+
+
+
+void
+pngx_setSbit(struct pngx * const pngxP,
+             png_color_8   const sbitArg) {
+
+    png_color_8 sbit;
+
+    sbit = sbitArg;
+
+    png_set_sBIT(pngxP->png_ptr, pngxP->info_ptr, &sbit);
+}
+
+
+
+void
+pngx_setShift(struct pngx * const pngxP,
+              png_color_8   const sigBitArg) {
+
+    png_color_8 sigBit;
+
+    sigBit = sigBitArg;
+
+    png_set_shift(pngxP->png_ptr, &sigBit);
+}
+
+
+
+void
+pngx_setSigBytes(struct pngx * const pngxP,
+                 unsigned int  const sigByteCt) {
+
+    assert(sigByteCt <= INT_MAX);
+
+    png_set_sig_bytes(pngxP->png_ptr, sigByteCt);
+}
+
+
+
+void
+pngx_setTextKey(png_text *   const textP,
+                const char * const key) {
+
+    /* textP->key is improperly declared in libpng as char *; should
+       be const char *
+    */
+    textP->key = (char *)pm_strdup(key);
+}
+
+
+
+void
+pngx_setTextLang(png_text *   const textP,
+                 const char * const language) {
+
+#if HAVE_PNGLIB_WITH_ITXT
+    if (language)
+        textP->lang = (char *)pm_strdup(language);
+    else
+        textP->lang = NULL;
+#else
+    if (language)
+        pm_error("PNG library does not have ability to create an iTXT "
+                 "chunk (i.e. to create a PNG with text strings in a language "
+                 "other than English)");
+#endif
+}
+
+
+
+void
+pngx_setTextLangKey(png_text *   const textP,
+                    const char * const key) {
+
+#if HAVE_PNGLIB_WITH_ITXT
+    textP->lang_key = (char *)pm_strdup(key);
+#else
+    pm_error("PNG library does not have ability to create an iTXT "
+             "chunk (i.e. to create a PNG with text strings in a language "
+             "other than English)");
+#endif
+}
+
+
+void
+pngx_termText(png_text * const textP) {
+
+    pm_strfree(textP->key);
+
+#if HAVE_PNGLIB_WITH_ITXT
+    if (textP->lang) {
+        pm_strfree(textP->lang);
+        pm_strfree(textP->lang_key);
+    }
+#endif
+
+    free(textP->text);
+}
+
+
+
+void
+pngx_setText(struct pngx * const pngxP,
+             png_text *    const textP,
+             unsigned int  const count) {
+
+    png_set_text(pngxP->png_ptr, pngxP->info_ptr, textP, count);
+}
+
+
+
+void
+pngx_setTime(struct pngx * const pngxP,
+             time_t        const timeArg) {
+
+    png_time pngTime;
+
+    png_convert_from_time_t(&pngTime, timeArg);
+
+    png_set_tIME(pngxP->png_ptr, pngxP->info_ptr, &pngTime);
+}
+
+
+
+void
+pngx_setTrnsPalette(struct pngx *    const pngxP,
+                    const png_byte * const transPalette,
+                    unsigned int     const paletteSize) {
+
+    png_set_tRNS(pngxP->png_ptr, pngxP->info_ptr,
+                 (png_byte *)transPalette, paletteSize, NULL);
+}
+
+
+
+void
+pngx_setTrnsValue(struct pngx * const pngxP,
+                  png_color_16  const transColorArg) {
+
+    png_color_16 transColor;
+
+    transColor = transColorArg;
+
+    png_set_tRNS(pngxP->png_ptr, pngxP->info_ptr,
+                 NULL, 0, &transColor);
+}
+
+
+
+void
+pngx_readInfo(struct pngx * const pngxP) {
+
+    png_read_info(pngxP->png_ptr, pngxP->info_ptr);
+}
+
+
+
+void
+pngx_writeInfo(struct pngx * const pngxP) {
+
+    png_write_info(pngxP->png_ptr, pngxP->info_ptr);
+}
+
+
+
+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;
+}
+
+
+
+void
+pngx_readStart(struct pngx * const pngxP,
+               FILE *        const ifP) {
+
+    size_t sigByteCt;
+            
+    verifyFileIsPng(ifP, &sigByteCt);
+
+    /* Declare that we already read the signature bytes */
+    pngx_setSigBytes(pngxP, (unsigned int)sigByteCt);
+
+    png_init_io(pngxP->png_ptr, ifP);
+
+    pngx_readInfo(pngxP);
+
+    if (pngx_bitDepth(pngxP) < 8)
+        pngx_setPacking(pngxP);
+}
+
+
+
+void
+pngx_readRow(struct pngx * const pngxP,
+             png_byte *    const rowBuf,
+             png_byte *    const displayRow) {
+
+    png_read_row(pngxP->png_ptr, rowBuf, displayRow);
+}
+
+
+
+void
+pngx_readImage(struct pngx * const pngxP,
+               png_byte **   const image) {
+
+    png_read_image(pngxP->png_ptr, image);
+}
+
+
+
+void
+pngx_writeRow(struct pngx *    const pngxP,
+              const png_byte * const line) {
+
+    png_write_row(pngxP->png_ptr, (png_byte *)line);
+}
+
+
+
+void
+pngx_readEnd(struct pngx * const pngxP) {
+
+    /* Note that some of info_ptr is not defined until png_read_end() 
+       completes.  That's because it comes from chunks that are at the
+       end of the stream.  In particular, text and time chunks may
+       be at the end.  Furthermore, they may be in both places, in
+       which case info_ptr contains different information before and
+       after png_read_end().
+    */
+    png_read_end(pngxP->png_ptr, pngxP->info_ptr);
+}
+
+
+
+void
+pngx_writeEnd(struct pngx * const pngxP) {
+
+    png_write_end(pngxP->png_ptr, pngxP->info_ptr);
+}
+
+
+
diff --git a/converter/other/pngx.h b/converter/other/pngx.h
new file mode 100644
index 00000000..81e0dc55
--- /dev/null
+++ b/converter/other/pngx.h
@@ -0,0 +1,274 @@
+#ifndef PNGX_H_INCLUDED
+#define PNGX_H_INCLUDED
+
+#include <png.h>
+#include "pm_c_util.h"
+
+/* pngx is designed to be an extension of the PNG library to make using
+   the PNG library easier and cleaner.
+*/
+
+struct pngx_chroma {
+    float wx;
+    float wy;
+    float rx;
+    float ry;
+    float gx;
+    float gy;
+    float bx;
+    float by;
+};
+
+struct pngx_phys {
+    int x;
+    int y;
+    int unit;
+};
+
+struct pngx_text {
+    png_text *   line;
+    unsigned int size;
+};
+
+struct pngx_plte {
+    png_color *  palette;
+    unsigned int size;
+};
+
+struct pngx_trns {
+    png_byte *    trans;
+    unsigned int  numTrans;
+    png_color_16  transColor;
+};
+
+typedef enum {PNGX_READ, PNGX_WRITE} pngx_rw;
+
+struct pngx {
+    png_structp  png_ptr;
+    png_infop    info_ptr;
+    pngx_rw      rw;
+    png_uint_16  maxval;
+    unsigned int numPassesRequired;
+        /* The number of times we have write the complete image to the
+           compressor.  This is more than one when the compressor is set
+           up to do an interlaced format.
+        */
+};
+
+void
+pngx_create(struct pngx ** const pngxPP,
+            pngx_rw        const rw,
+            jmp_buf *      const jmpbufP);
+
+void
+pngx_destroy(struct pngx * const pngxP);
+
+bool
+pngx_chunkIsPresent(struct pngx * const pngxP,
+                    uint32_t      const chunkType);
+
+unsigned int
+pngx_bitDepth(struct pngx * const pngxP);
+
+png_color_16
+pngx_bkgd(struct pngx *  const pngxP);
+
+png_byte
+pngx_colorType(struct pngx * const pngxP);
+
+png_byte
+pngx_filterType(struct pngx * const pngxP);
+
+double
+pngx_gama(struct pngx * const pngxP);
+
+uint32_t
+pngx_imageHeight(struct pngx * const pngxP);
+
+uint32_t
+pngx_imageWidth(struct pngx * const pngxP);
+
+png_byte
+pngx_interlaceType(struct pngx * const pngxP);
+
+struct pngx_plte
+pngx_plte(struct pngx * const pngxP);
+
+png_color_8
+pngx_sbit(struct pngx * const pngxP);
+
+struct pngx_text
+pngx_text(struct pngx * const pngxP);
+
+png_time
+pngx_time(struct pngx * const pngxP);
+
+struct pngx_trns
+pngx_trns(struct pngx * const pngxP);
+
+uint32_t
+pngx_xPixelsPerMeter(struct pngx * const pngxP);
+
+uint32_t
+pngx_yPixelsPerMeter(struct pngx * const pngxP);
+
+float
+pngx_pixelAspectRatio(struct pngx * const pngxP);
+
+bool
+pngx_pixelAspectRatioIsKnown(struct pngx * const pngxP);
+
+void
+pngx_removeChunk(struct pngx * const pngxP,
+                 uint32_t      const chunkType);
+
+void
+pngx_setBkgdPalette(struct pngx * const pngxP,
+                    unsigned int  const backgroundIndex);
+
+void
+pngx_setBkgdRgb(struct pngx * const pngxP,
+                png_color_16  const backgroundArg);
+
+void
+pngx_setChrm(struct pngx *      const pngxP,
+             struct pngx_chroma const chroma);
+
+typedef enum {
+    PNGX_PERCEPTUAL,
+    PNGX_RELATIVE_COLORIMETRIC,
+    PNGX_SATURATION,
+    PNGX_ABSOLUTE_COLORIMETRIC
+} pngx_srgbIntent;
+
+const char *
+pngx_srgbIntentDesc(pngx_srgbIntent const srgbIntent);
+
+void
+pngx_setSrgb(struct pngx *   const pngxP,
+             pngx_srgbIntent const srgbIntent);
+
+void
+pngx_setCompressionSize(struct pngx * const pngxP,
+                        unsigned int  const bufferSize);
+
+void
+pngx_setFilter(struct pngx * const pngxP,
+               int           const filterSet);
+
+void
+pngx_setGama(struct pngx * const pngxP,
+             float         const fileGamma);
+
+void
+pngx_setGamma(struct pngx * const pngxP,
+              float         const displayGamma,
+              float         const imageGamma);
+
+void
+pngx_setHist(struct pngx * const pngxP,
+             png_uint_16 * const histogram);
+
+void
+pngx_setIhdr(struct pngx * const pngxP,
+             unsigned int  const width,
+             unsigned int  const height,
+             unsigned int  const bitDepth,
+             int           const colorType,
+             int           const interlaceMethod,
+             int           const compressionMethod,
+             int           const filterMethod);
+
+void
+pngx_setInterlaceHandling(struct pngx * const pngxP);
+
+void
+pngx_setInvalid(struct pngx * const pngxP);
+
+void
+pngx_setPacking(struct pngx * const pngxP);
+
+void
+pngx_setPhys(struct pngx *    const pngxP,
+             struct pngx_phys const phys);
+
+void
+pngx_setPlte(struct pngx * const pngxP,
+             png_color *   const palette,
+             unsigned int  const paletteSize);
+
+void
+pngx_setSbit(struct pngx * const pngxP,
+             png_color_8   const sbit);
+
+void
+pngx_setShift(struct pngx * const pngxP,
+              png_color_8   const sigBitArg);
+
+void
+pngx_setSigBytes(struct pngx * const pngxP,
+                 unsigned int  const sigByteCt);
+
+void
+pngx_setTextKey(png_text *   const textP,
+                const char * const key);
+
+void
+pngx_setTextLang(png_text *   const textP,
+                 const char * const language);
+
+void
+pngx_setTextLangKey(png_text *   const textP,
+                    const char * const key);
+
+void
+pngx_termText(png_text * const textP);
+
+void
+pngx_setText(struct pngx * const pngxP,
+             png_textp     const textP,
+             unsigned int  const count);
+
+void
+pngx_setTime(struct pngx * const pngxP,
+             time_t        const timeArg);
+
+void
+pngx_setTrnsPalette(struct pngx *    const pngxP,
+                    const png_byte * const transPalette,
+                    unsigned int     const paletteSize);
+
+void
+pngx_setTrnsValue(struct pngx * const pngxP,
+                  png_color_16  const transColorArg);
+
+void
+pngx_readInfo(struct pngx * const pngxP);
+
+void
+pngx_writeInfo(struct pngx * const pngxP);
+
+void
+pngx_readStart(struct pngx * const pngxP,
+               FILE *        const ifP);
+
+void
+pngx_readRow(struct pngx * const pngxP,
+             png_byte *    const rowBuf,
+             png_byte *    const displayRow);
+
+void
+pngx_readImage(struct pngx * const pngxP,
+               png_byte **   const image);
+
+void
+pngx_writeRow(struct pngx *    const pngxP,
+              const png_byte * const line);
+
+void
+pngx_readEnd(struct pngx * const pngxP);
+
+void
+pngx_writeEnd(struct pngx * const pngxP);
+
+#endif
diff --git a/converter/other/pnmtojpeg.c b/converter/other/pnmtojpeg.c
index b33b36b3..7e7272a0 100644
--- a/converter/other/pnmtojpeg.c
+++ b/converter/other/pnmtojpeg.c
@@ -271,7 +271,7 @@ parseCommandLine(const int argc, char ** argv,
     cmdlineP->comment = NULL;
     cmdlineP->exif_filespec = NULL;
 
-    /* Make private copy of arguments for optParseOptions to corrupt */
+    /* Make private copy of arguments for pm_optParseOptions to corrupt */
     argc_parse = argc;
     for (i=0; i < argc+1; i++) argv_parse[i] = argv[i];
 
@@ -279,7 +279,7 @@ parseCommandLine(const 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_parse, argv_parse, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc_parse, argv_parse, opt, sizeof(opt), 0);
 
     if (!qualitySpec)
         cmdlineP->quality = -1;  /* unspecified */
@@ -898,7 +898,7 @@ write_exif_header(struct jpeg_compress_struct * const cinfoP,
         pm_error("Invalid length %u at start of exif file", length);
     else {
         unsigned char * exif_data;
-        int rc;
+        size_t rc;
         size_t const data_length = length - 2;  
             /* Subtract 2 byte length field*/
 
@@ -906,14 +906,15 @@ write_exif_header(struct jpeg_compress_struct * const cinfoP,
 
         exif_data = malloc(data_length);
         if (exif_data == NULL)
-            pm_error("Unable to allocate %d bytes for exif header buffer",
-                     data_length);
+            pm_error("Unable to allocate %u bytes for exif header buffer",
+                     (unsigned)data_length);
 
         rc = fread(exif_data, 1, data_length, exif_file);
 
         if (rc != data_length)
             pm_error("Premature end of file on exif header file.  Should be "
-                     "%d bytes of data, read only %d", data_length, rc);
+                     "%u bytes of data, read only %u",
+                     (unsigned)data_length, (unsigned)rc);
 
         jpeg_write_marker(cinfoP, JPEG_APP0+1, 
                           (const JOCTET *) exif_data, data_length);
diff --git a/converter/other/pnmtopalm/Makefile b/converter/other/pnmtopalm/Makefile
index 7f99f95a..edc7da64 100644
--- a/converter/other/pnmtopalm/Makefile
+++ b/converter/other/pnmtopalm/Makefile
@@ -8,8 +8,10 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 include $(BUILDDIR)/config.mk
 
 BINARIES = palmtopnm pnmtopalm
+PORTBINARIES = $(BINARIES) gen_palm_colormap
 SCRIPTS =
-OBJECTS = $(BINARIES:%=%.o) palmcolormap.o
+ADDL_OBJECTS = palmcolormap.o
+OBJECTS = $(BINARIES:%=%.o) $(ADDL_OBJECTS) gen_palm_colormap.o
 MERGE_OBJECTS = $(BINARIES:%=%.o2) palmcolormap.o
 MERGEBINARIES = $(BINARIES)
 DATAFILES = palmcolor8.map palmgray1.map palmgray2.map palmgray4.map
@@ -18,17 +20,7 @@ all: $(BINARIES)
 
 include $(SRCDIR)/common.mk
 
-LIBOPTS = $(shell $(LIBOPT) $(NETPBMLIB))
-
-$(BINARIES): %: %.o palmcolormap.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $< palmcolormap.o $(LIBOPTS) \
-	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
-
-gen_palm_colormap : % : %.c palmcolormap.o
-	$(CC) -I importinc $(CPPFLAGS) $(CFLAGS) -o $@ \
-	  $< palmcolormap.o \
-	  $(LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(LADD)
-
+$(BINARIES): $(ADDL_OBJECTS)
 
 clean: cleanspecial
 .PHONY: cleanspecial
diff --git a/converter/other/pnmtopalm/gen_palm_colormap.c b/converter/other/pnmtopalm/gen_palm_colormap.c
index c7172c6b..0f3f8a5f 100644
--- a/converter/other/pnmtopalm/gen_palm_colormap.c
+++ b/converter/other/pnmtopalm/gen_palm_colormap.c
@@ -3,8 +3,8 @@
  * Based on an earlier version by Bill Janssen  <bill@janssen.org>
  */
 
-#include "ppm.h"
-#include "pm_c_util.h"
+#include "netpbm/ppm.h"
+#include "netpbm/pm_c_util.h"
 
 #include "palm.h"
 
diff --git a/converter/other/pnmtopalm/palm.h b/converter/other/pnmtopalm/palm.h
index 170c8cec..718a66cf 100644
--- a/converter/other/pnmtopalm/palm.h
+++ b/converter/other/pnmtopalm/palm.h
@@ -44,13 +44,9 @@ typedef struct {
 
 typedef Colormap_s * Colormap;
 
-int
-palmcolor_compare_indices(const void * const p1,
-                          const void * const p2);
+qsort_comparison_fn palmcolor_compare_indices;
 
-int
-palmcolor_compare_colors(const void * const p1,
-                         const void * const p2);
+qsort_comparison_fn palmcolor_compare_colors;
 
 Colormap
 palmcolor_build_custom_8bit_colormap(unsigned int const rows,
diff --git a/converter/other/pnmtopalm/palmcolormap.c b/converter/other/pnmtopalm/palmcolormap.c
index a1a1cec1..0f47558c 100644
--- a/converter/other/pnmtopalm/palmcolormap.c
+++ b/converter/other/pnmtopalm/palmcolormap.c
@@ -10,7 +10,9 @@
 int
 palmcolor_compare_indices(const void * const p1,
                           const void * const p2) {
-
+/*----------------------------------------------------------------------------
+   This is a 'qsort' collation function.
+-----------------------------------------------------------------------------*/
     if ((*((Color) p1) & 0xFF000000) < (*((Color) p2) & 0xFF000000))
         return -1;
     else if ((*((Color) p1) & 0xFF000000) > (*((Color) p2) & 0xFF000000))
@@ -24,7 +26,9 @@ palmcolor_compare_indices(const void * const p1,
 int
 palmcolor_compare_colors(const void * const p1,
                          const void * const p2) {
-
+/*----------------------------------------------------------------------------
+   This is a 'qsort' collation function.
+-----------------------------------------------------------------------------*/
     unsigned long const val1 = *((const unsigned long *) p1) & 0xFFFFFF;
     unsigned long const val2 = *((const unsigned long *) p2) & 0xFFFFFF;
 
diff --git a/converter/other/pnmtopalm/palmtopnm.c b/converter/other/pnmtopalm/palmtopnm.c
index e686571b..0f76207d 100644
--- a/converter/other/pnmtopalm/palmtopnm.c
+++ b/converter/other/pnmtopalm/palmtopnm.c
@@ -92,7 +92,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -114,7 +114,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
diff --git a/converter/other/pnmtopalm/pnmtopalm.c b/converter/other/pnmtopalm/pnmtopalm.c
index 90737b78..a7d1fd46 100644
--- a/converter/other/pnmtopalm/pnmtopalm.c
+++ b/converter/other/pnmtopalm/pnmtopalm.c
@@ -13,7 +13,13 @@
  *
  * See LICENSE file for licensing information.
  *
- *  
+ * References for the Palm Bitmap format:
+ *
+ * https://web.archive.org/web/20030621112139/http://www.palmos.com:80/dev/support/docs/
+ * https://web.archive.org/web/20030413080018/http://www.palmos.com:80/dev/support/docs/palmos/ReferenceTOC.html
+ *
+ * http://www.trantor.de/kawt/doc/palmimages.html
+ * (above retrieved August 2017)
  */
 
 #include <string.h>
@@ -26,6 +32,7 @@
 #include "palm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
+#include "runlength.h"
 
 enum compressionType {COMP_NONE, COMP_SCANLINE, COMP_RLE, COMP_PACKBITS};
 
@@ -33,8 +40,8 @@ struct cmdline_info {
     /* 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 *transparent;          /* -transparent value.  Null if unspec */
+    const char * inputFilespec;  /* Filespecs of input files */
+    const char * transparent;    /* -transparent value.  Null if unspec */
     unsigned int depth;         /* -depth value.  0 if unspec */
     unsigned int maxdepth;      /* -maxdepth value.  0 if unspec */
     enum compressionType compression;
@@ -90,7 +97,7 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
     opt.short_allowed = FALSE; /* We have some short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (depthSpec) {
@@ -289,7 +296,7 @@ formatName(int const format) {
         
 
 static void
-findTransparentColor(char *         const colorSpec, 
+findTransparentColor(const char *   const colorSpec, 
                      pixval         const newMaxval,
                      bool           const directColor, 
                      pixval         const maxval, 
@@ -497,7 +504,7 @@ writeDummy() {
    Write a dummy Palm Bitmap header.  This is a 16 byte header, of
    type version 1 and with (only) pixelSize set to 0xFF.
 
-   An old viewer will see this as invalid due to the pixelSize, and stop
+   An old viewer will see this as invalid because of the pixelSize, and stop
    reading the stream.  A new viewer will recognize this for what it is
    (a dummy header designed to stop old viewers from reading further in
    the stream) and continue reading the stream.  Presumably, what follows
@@ -836,101 +843,26 @@ rleCompressAndBufferRow(const unsigned char * const rowdata,
 
 
 static void
-computeNextPackbitsRun(const unsigned char * const rowdata,
-                       unsigned int          const rowbytes,
-                       unsigned int          const startPos,
-                       unsigned int *        const nextPosP,
-                       unsigned char *       const output,
-                       int *                 const countP) {
-
-    unsigned int pos;
-    int count;
-    
-    pos = startPos;
-    count = 0;
-    
-    if (rowdata[pos] == rowdata[pos + 1]) {
-        ++pos;
-        --count;
-        while ((count > -127) && (pos < (rowbytes - 1)) &&
-               (rowdata[pos] == rowdata[pos + 1])) {
-            ++pos;
-            --count;
-        }
-        ++pos;  /* push pos past end of this run */
-    } else {
-        output[count] = rowdata[pos];
-        ++pos;
-        while ((count < 127) && (pos < (rowbytes - 1)) && 
-               (rowdata[pos] != rowdata[pos + 1])) {
-            ++count;
-            output[count] = rowdata[pos];
-            ++pos;
-        }
-        /* trailing literal */
-        if ((count < 127) && (pos == (rowbytes - 1)) &&
-            (rowdata[pos - 1] != rowdata[pos])) {
-            ++count;
-            output[count] = rowdata[pos];
-            ++pos;
-        }
-    }
-    *nextPosP = pos;
-    *countP = count;
-}
-
-
-
-static void
-addPackbitsRunToBuffer(const unsigned char * const rowdata,
-                       unsigned int          const rowbytes,
-                       unsigned int          const pos,
-                       unsigned char *       const output,
-                       int                   const count,
-                       struct seqBuffer *    const rasterBufferP) {
-
-    addByteToBuffer(rasterBufferP, (unsigned char)(signed char)count);
-    if (count < 0) {
-        addByteToBuffer(rasterBufferP, rowdata[pos - 1]);
-    } else {
-        unsigned int j;
-        for (j = 0; j <= count; j++)
-            addByteToBuffer(rasterBufferP, output[j]);
-    }
-    
-    if (pos == (rowbytes - 1) && (rowdata[pos - 1] != rowdata[pos])) {
-        /* orphaned byte, treat as literal */
-        addByteToBuffer(rasterBufferP, 0);
-        addByteToBuffer(rasterBufferP, rowdata[pos]);
-    }
-}
-
-
-
-static void
 packbitsCompressAndBufferRow(const unsigned char * const rowdata,
                              unsigned int          const rowbytes,
                              struct seqBuffer *    const rasterBufferP) {
 /*----------------------------------------------------------------------------
-   Take the raw Palm Bitmap row 'rowdata', which is 'rowbytess' bytes, and
+   Take the raw Palm Bitmap row 'rowdata', which is 'rowbytes' bytes, and
    add the packbits-compressed representation of it to the buffer 
    with handle 'rasterBufferP'.
 -----------------------------------------------------------------------------*/
-    unsigned int position;
-        /* byte position within the row */
-
-    position = 0;  /* Start at beginning of row */
+    unsigned char * compressedData;
+    size_t          compressedDataCt;
+    unsigned int    byteCt;
 
-    while (position < rowbytes - 1) {
-        unsigned char output[128];
-        int count;
+    pm_rlenc_allocoutbuf(&compressedData, rowbytes, PM_RLE_PACKBITS);
+    pm_rlenc_compressbyte(rowdata, compressedData, PM_RLE_PACKBITS,
+                          rowbytes, &compressedDataCt);
 
-        computeNextPackbitsRun(rowdata, rowbytes, position, 
-                               &position, output, &count);
+    for (byteCt = 0; byteCt < compressedDataCt; ++byteCt)
+        addByteToBuffer(rasterBufferP, compressedData[byteCt]);
 
-        addPackbitsRunToBuffer(rowdata, rowbytes, position, output, count,
-                               rasterBufferP);
-    }
+    free(compressedData);
 }
 
 
@@ -1027,7 +959,13 @@ bufferRaster(xel **               const xels,
     createBuffer(rasterBufferPP);
     
     MALLOCARRAY_NOFAIL(rowdata, rowbytes);
-    MALLOCARRAY_NOFAIL(lastrow, rowbytes);
+    if (compression == COMP_SCANLINE)
+        MALLOCARRAY_NOFAIL(lastrow, rowbytes);
+    else
+        lastrow = NULL;
+
+    /* clear pad bytes to suppress valgrind error */
+    rowdata[rowbytes - 1] = rowdata[rowbytes - 2] = 0x00;
 
     /* And write out the data. */
     for (row = 0; row < rows; ++row) {
@@ -1039,8 +977,9 @@ bufferRaster(xel **               const xels,
         if (compression == COMP_SCANLINE)
             memcpy(lastrow, rowdata, rowbytes);
     }
-    free(lastrow);
     free(rowdata);
+    if (compression == COMP_SCANLINE)
+        free(lastrow);
 }
 
 
diff --git a/converter/other/pnmtopclxl.c b/converter/other/pnmtopclxl.c
index e16afb14..8cabb614 100644
--- a/converter/other/pnmtopclxl.c
+++ b/converter/other/pnmtopclxl.c
@@ -34,6 +34,7 @@
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "nstring.h"
+#include "runlength.h"
 
 #include "pclxl.h"
 
@@ -54,7 +55,7 @@ struct cmdlineInfo {
        in a form easy for the program to use.
     */
     InputSource * sourceP;
-    int dpi;
+    unsigned int dpi;
     enum MediaSize format;
     unsigned int feederSpec;
     int feeder;
@@ -71,6 +72,7 @@ struct cmdlineInfo {
     unsigned int verbose;
     const char * jobsetup;  /* -jobsetup option value.  NULL if none */
     unsigned int rendergray;
+    unsigned int embedded;
 };
 
 
@@ -89,7 +91,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -126,12 +128,14 @@ parseCommandLine(int argc, char ** argv,
             &jobsetupSpec,      0);
     OPTENT3(0, "rendergray", OPT_FLAG,    NULL,
             &cmdlineP->rendergray, 0 );
+    OPTENT3(0, "embedded", OPT_FLAG,    NULL,
+            &cmdlineP->embedded, 0 );
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!dpiSpec)
@@ -174,6 +178,20 @@ parseCommandLine(int argc, char ** argv,
     if (!jobsetupSpec)
         cmdlineP->jobsetup = NULL;
 
+    if (cmdlineP->embedded) {
+        if (xoffsSpec || yoffsSpec || formatSpec || cmdlineP->duplexSpec
+            || cmdlineP->copiesSpec || dpiSpec || cmdlineP->center
+            || cmdlineP->feederSpec || cmdlineP->outtraySpec
+            || jobsetupSpec || cmdlineP->rendergray)
+            pm_error("With -embedded, you may not specify "
+                     "-xoffs, -yoffs, -format, -duplex, copies, -dpi, "
+                     "-center, -feeder, -outtray, -jobsetup, or -rendergray");
+
+        if (argc-1 > 1)
+            pm_message("With -embedded, you may not specify more than one "
+                       "input image.  You specified %u", argc-1);
+    }
+
     if (argc-1 < 1) {
         MALLOCVAR(cmdlineP->sourceP);
         cmdlineP->sourceP->name = "-";
@@ -196,154 +214,248 @@ parseCommandLine(int argc, char ** argv,
 
 
 
-#define XY_RLE_FBUFSIZE (1024)
-typedef struct XY_rle {
-    int error;
-    unsigned char buf[128];
-    int bpos;
-    int state;  
-    unsigned char *fbuf;
-    int fbpos;
-    int fbufsize;
-    int fd;
-} XY_rle;
-
+static void
+freeSource(InputSource * const firstSourceP) {
+    
+    InputSource * sourceP;
 
-static void 
-XY_RLEreset(XY_rle *rle) 
-{   
-    rle->state = eSTART;
-    rle->bpos = 0;
-    rle->fbpos=0;
-    rle->error=0;
-}
-static XY_rle * 
-XY_RLEnew(int size) {
-    XY_rle *rle;
-
-    MALLOCVAR(rle);
-    if(rle==NULL)
-        return rle;
-    if(size<1024)
-        size=1024;
-    rle->fbuf=malloc(size);
-    rle->fbufsize=size;
-    if(!rle->fbuf) {
-        free(rle);
-        return NULL;
+    sourceP = firstSourceP;
+    while(sourceP) {
+        InputSource * const nextP = sourceP->next;
+        free(sourceP);
+        sourceP = nextP;
     }
-    return rle;
 }
+
+
+
 static void
-XY_RLEdelete(XY_rle *rle) {
-    free(rle->fbuf);
-    free(rle);
+freeCmdline(struct cmdlineInfo const cmdline) {
+
+    freeSource(cmdline.sourceP);
 }
 
-static int 
-out(XY_rle *rle,int count) {
-    if(rle->state==eRLE) {
-        rle->fbuf[rle->fbpos++]=-count+1;
-        rle->fbuf[rle->fbpos++]=rle->buf[0];
-    } else if(rle->bpos>0) {
-        rle->fbuf[rle->fbpos++]=count-1;
-        memcpy(rle->fbuf+rle->fbpos,rle->buf,count);
-        rle->fbpos+=count;
+
+
+static int
+XY_Write(int          const fd,
+         const void * const buf,
+         int          const cnt) {
+
+    int len;
+    bool error;
+
+    for (len =0, error = false; len < cnt && !error;) {
+        ssize_t const rc = write(fd, (char*)buf + len , cnt - len);
+        if (rc <= 0)
+            error = true;
+        else
+            len += rc;
     }
-    if(rle->fbpos+129>rle->fbufsize) {
-        if (rle->fbufsize > INT_MAX/1.2)
-            pm_error("Arithmetic overflow during attempt to expand RLE "
-                     "output buffer beyond %u", rle->fbufsize);
-        rle->fbufsize*=1.2; 
-        rle->fbuf=realloc(rle->fbuf,rle->fbufsize);
-        if(rle->fbuf==NULL) {
-            pm_error("Out of memory while attempting to expand RLE "
-                     "output buffer beyond %u", rle->fbufsize);
-            rle->error=-1;
-            rle->fbpos=0;
-            return -1;
-        }
+    return error ? -1 : len;
+}
+
+
+
+#define XY_Puts(fd, str)  XY_Write(fd, str, strlen(str))
+
+
+
+typedef struct pclGenerator {
+    enum ColorDepth colorDepth;
+    enum Colorspace colorSpace;
+    int width,height;
+    unsigned int linelen;       /* bytes per line */
+    unsigned int paddedLinelen; /* bytes per line + padding */
+    unsigned char * data;
+    unsigned int cursor;
+    void (*getnextrow)(struct pclGenerator *, struct pam *);
+} pclGenerator;
+
+
+
+static void
+pnmToPcllinePackbits(pclGenerator * const pclGeneratorP,
+                     struct pam *   const pamP) {
+
+    tuple * tuplerow;
+    unsigned char accum;
+    unsigned char bitmask;
+    unsigned int col, padCt;
+    unsigned int const padsize =
+        pclGeneratorP->paddedLinelen - pclGeneratorP->linelen;
+        
+    tuplerow = pnm_allocpamrow(pamP);
+
+    pnm_readpamrow(pamP, tuplerow);
+
+    bitmask = 0x80; accum = 0x00;
+    for (col = 0; col < pamP->width; ++col) {
+        if (tuplerow[col][0] == PAM_PBM_WHITE)
+            accum |= bitmask;
+        bitmask >>= 1;
+        if (bitmask == 0) {
+            pclGeneratorP->data[pclGeneratorP->cursor++] = accum;
+            bitmask = 0x80; accum = 0x0;
+        } 
     }
-    rle->bpos=0;
-    rle->state=eSTART;
-    return 0;
+    if (bitmask != 0x80)
+        pclGeneratorP->data[pclGeneratorP->cursor++] = accum;
+
+    for (padCt = 0; padCt < padsize; ++padCt)
+        pclGeneratorP->data[pclGeneratorP->cursor++] = 0;
+
+    pnm_freepamrow(tuplerow);
 }
-static int
-XY_RLEfinish (XY_rle *rle) {
-    out(rle,rle->bpos);
-    if(rle->error<0) 
-        return rle->error;
-    else
-        return rle->fbpos;
+
+
+
+static unsigned int
+pclPaddedLinelen(unsigned int const linelen) {
+
+    if (UINT_MAX - 3 < linelen)
+        pm_error("Image too big to process");
+
+    return (((linelen + 3) / 4) * 4);
 }
-static  void
-rle_putbyte(XY_rle *rle,unsigned char u) 
-{
-    switch (rle->state) {
-        case eRLE:
-            if(u!=rle->buf[0]) {
-                out(rle,rle->bpos);
-            }   
-            break;
-        case eLIT:
-            if((u==rle->buf[rle->bpos-1])&&(u==rle->buf[rle->bpos-2])) {
-                out(rle,rle->bpos-2);
-                rle->buf[0]=u;
-                rle->bpos+=2;
-                rle->state=eRLE;
-            }   
-            break;
-        case eSTART:
-            if(rle->bpos==1) {
-                if(u==rle->buf[rle->bpos-1]) {
-                    rle->state=eRLE;
-                } else {
-                    rle->state=eLIT;
-                }   
-            }
-            break;
+
+
+
+static unsigned int
+pclDatabuffSize(unsigned int const paddedLinelen) {
+
+    if (UINT_MAX / 20 < paddedLinelen)
+        pm_error("Image too big to process");
+
+    return (paddedLinelen * 20);
+}
+
+
+
+static void
+createPclGeneratorPackbits(struct pam *    const pamP,
+                           pclGenerator ** const pclGeneratorPP) {
+
+    /* Samples are black or white and packed 8 to a byte */
+
+    pclGenerator * pclGeneratorP;
+
+    MALLOCVAR_NOFAIL(pclGeneratorP);
+
+    pclGeneratorP->colorDepth = e1Bit;
+    pclGeneratorP->colorSpace = eGray;
+    pclGeneratorP->linelen = (pamP->width+7)/8;
+    pclGeneratorP->paddedLinelen = pclPaddedLinelen(pclGeneratorP->linelen);
+    pclGeneratorP->height = pamP->height;
+    pclGeneratorP->width = (pamP->width);
+
+    pclGeneratorP->data =
+        malloc(pclDatabuffSize(pclGeneratorP->paddedLinelen));
     
+    if (pclGeneratorP->data == NULL)
+        pm_error("Unable to allocate row buffer.");
+
+    pclGeneratorP->getnextrow = pnmToPcllinePackbits;
+
+    *pclGeneratorPP = pclGeneratorP;
+}
+
+
+
+static void
+pnmToPcllineWholebytes(pclGenerator * const pclGeneratorP,
+                       struct pam *   const pamP) {
+
+    tuple * tuplerow;
+    unsigned int col, padCt;
+    unsigned int const padsize =
+        pclGeneratorP->paddedLinelen - pclGeneratorP->linelen;
+
+    tuplerow = pnm_allocpamrow(pamP);
+
+    pnm_readpamrow(pamP, tuplerow);
+
+    for (col = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane) {
+            pclGeneratorP->data[pclGeneratorP->cursor++] = 
+                pnm_scalesample(tuplerow[col][plane], pamP->maxval, 255);
+        }
     }
-    rle->buf[rle->bpos++]=u;
-    if(rle->bpos==128) {
-        out(rle,rle->bpos);
-    }
+
+    for(padCt=0; padCt < padsize; ++padCt)
+        pclGeneratorP->data[pclGeneratorP->cursor++] = 0;
+
+    pnm_freepamrow(tuplerow);
 }
 
 
 
 static void
-XY_RLEput(XY_rle *rle,const unsigned char buf[],int count) 
-{
-    int i;
-    for(i=0;i<count;i++) {
-        rle_putbyte(rle,buf[i]);
+createPclGeneratorWholebytes(struct pam *    const pamP,
+                             pclGenerator ** const pclGenPP) {
+    /* One sample per byte */
+
+    pclGenerator * pclGenP;
+
+    MALLOCVAR_NOFAIL(pclGenP);
+    
+    if (pamP->depth <  3)
+        pclGenP->colorSpace = eGray;
+    else
+        pclGenP->colorSpace = eRGB;
+
+    pclGenP->colorDepth = e8Bit;
+    pclGenP->height     = pamP->height;
+    pclGenP->width      = pamP->width;
+
+    if (UINT_MAX / pamP->width < pamP->depth)
+        pm_error("Image too big to process");
+    else {
+        pclGenP->linelen = pamP->width * pamP->depth;
+        pclGenP->paddedLinelen = pclPaddedLinelen(pclGenP->linelen);
+        pclGenP->data = malloc(pclDatabuffSize(pclGenP->paddedLinelen));
+        if (pclGenP->data == NULL)
+            pm_error("Unable to allocate row buffer.");
     }
     
+    pclGenP->getnextrow = pnmToPcllineWholebytes;
+
+    *pclGenPP = pclGenP;
 }
 
 
-static int
-XY_Write(int fd, const void *buf,int cnt) {
-        int len=0;
-        while(len<cnt) {
-                int n = write(fd,(char*)buf+len,cnt-len);
-                if(n<=0)
-                    pm_error("Failed to write %u bytes to fd %d", cnt - len, fd);
-                len+=n;
-        }
-        return len;
+
+static void
+destroyPclGenerator(pclGenerator * const pclGenP) {
+
+    free(pclGenP->data);
+
+    free(pclGenP);
+}
+
+
+
+static void 
+createPclGenerator(struct pam *        const pamP,
+                   pclGenerator **     const pclGeneratorPP,
+                   bool                const colorok) {
+
+    if (pamP->depth > 1 && !colorok)
+        pm_message("WARNING: generating a color print stream because the "
+                   "input image is PPM.  "
+                   "To generate a black and white print stream, run the input "
+                   "through Ppmtopgm.  To suppress this warning, use the "
+                   "-colorok option.");
+
+    if (pamP->depth == 1 && pamP->maxval == 1) 
+        createPclGeneratorPackbits(pamP, pclGeneratorPP);
+    else 
+        createPclGeneratorWholebytes(pamP, pclGeneratorPP);
 }
-#define XY_Puts(fd,str)  XY_Write(fd,str,strlen(str))
 
-typedef struct pclGenerator {
-    enum ColorDepth colorDepth;
-    enum Colorspace colorSpace;
-    int width,height;
-    int linelen; /* bytes per line */
-    unsigned char *data;
-    void (*getnextrow)(const struct pclGenerator *, struct pam *);
-} pclGenerator;
+
+
 
 struct tPrinter { 
     const char *name;
@@ -358,74 +470,85 @@ struct tPrinter {
 
 
 static int
-out_ubyte(int fd,unsigned char data) {
-    return XY_Write(fd,&data,1);
+out_ubyte(int           const fd,
+          unsigned char const data) {
+
+    return XY_Write(fd, &data, 1);
 }
-static  int 
-XL_Operator(int fd,enum Operator const data)  {
-    return out_ubyte(fd,data);
+
+
+
+static int 
+XL_Operator(int           const fd,
+            enum Operator const data)  {
+
+    return out_ubyte(fd, data);
 }
+
+
+
 static int
-out_uint16(int fd,unsigned short data ) {
+out_uint16(int            const fd,
+           unsigned short const data ) {
+
     unsigned char c[2];
-    c[0]=data&0xff; c[1]=data>>8;
-    return XY_Write(fd,c,2);
+
+    c[0] = data & 0xff;
+    c[1] = data >>8;
+
+    return XY_Write(fd, c , ARRAY_SIZE(c));
 }
+
+
+
 static int
 out_uint32(int fd,unsigned int data ) {
     unsigned char c[4];
     c[0] = data&0xff; c[1]=(data>>8)&0xff; c[2]=(data>>16)&0xff; c[3]=data>>24;
     return XY_Write(fd,c,4);
 }
+
+
+
 static int
-out_sint16(int fd,signed short sdata ) {
-    unsigned short data=(unsigned short)sdata;    
+out_sint16(int          const fd,
+           signed short const sdata ) {
+
+    unsigned short const data= (unsigned short)sdata;    
+
     unsigned char c[2];
-    c[0]=data&0xff; c[1]=data>>8;
-    return XY_Write(fd,c,2);
-}
-#if 0
-static int
-out_sint32(int fd,signed int sdata ) {
-    unsigned int data=(unsigned int)sdata;
-    unsigned char c[4];
-    c[0] = data&0xff; c[1]=(data>>8)&0xff; c[2]=(data>>16)&0xff; c[3]=data>>24;
-    return XY_Write(fd,c,4);
-}
-#endif
 
-static int
-xl_ubyte(int fd,unsigned char data) {
-    unsigned char const tag=0xc0;
-    XY_Write(fd,&tag,1);
-    return out_ubyte(fd,data);
-}
-static int
-xl_uint16(int fd,unsigned short data ) {
-    unsigned char const tag=0xc1;
-    XY_Write(fd,&tag,1);
-    return out_uint16(fd,data);
-}
-#if 0
-static int
-xl_uint32(int fd,unsigned int data ) {
-    unsigned char const c=0xc2;
-    XY_Write(fd,&c,1);
-    return out_uint32(fd,data);
+    c[0] = data & 0xff;
+    c[1] = data >> 8;
+
+    return XY_Write(fd, c, ARRAY_SIZE(c));
 }
+
+
+
 static int
-xl_sint16(int fd,signed short data ) {
-    unsigned char const c=0xc3;
-    XY_Write(fd,&c,1);
-    return out_sint16(fd,data);
+xl_ubyte(int           const fd,
+         unsigned char const data) {
+
+    unsigned char const tag = 0xc0;
+
+    XY_Write(fd, &tag, 1);
+
+    return out_ubyte(fd, data);
 }
+
+
+
 static int
-xl_sint32(int fd,signed int data ) {
-    unsigned char const c=0xc4;
-    XY_Write(fd,&c,1);
-    return out_sint32(fd,data);
+xl_uint16(int            const fd,
+          unsigned short const data) {
+
+    unsigned char const tag = 0xc1;
+
+    XY_Write(fd, &tag, 1);
+
+    return out_uint16(fd, data);
 }
-#endif
 
 
 
@@ -439,10 +562,10 @@ xl_ubyte_array(int                   const fd,
     
     head[0] = 0xc8;
     head[1] = 0xc1;
-    head[2] = len&0xff;
-    head[3] = (len>>8)&0xff;
+    head[2] = len & 0xff;
+    head[3] = (len >> 8) & 0xff;
 
-    XY_Write(fd, head, 4);
+    XY_Write(fd, head, ARRAY_SIZE(head));
 
     for (i = 0; i < len; ++i)
         out_ubyte(fd, data[i]);
@@ -452,123 +575,182 @@ xl_ubyte_array(int                   const fd,
 
 
 
-#if 0
-static int
-xl_uint16_array(int fd,unsigned short *data,int len) {
-    int i;
-    unsigned char head[4];
-    head[0]=0xc9;head[1]=0xc1;head[2]=len&0xff;head[3]=(len>>8)&0xff;
-    XY_Write(fd,head,4);
-    for(i=0;i<len;i++) {
-        out_uint16(fd,data[i]);
-    }
-    return 0;
-}
-static int
-xl_uint32_array(int fd,unsigned int *data,int len) {
-    int i;
-    unsigned char head[4];
-    head[0]=0xca;head[1]=0xc1;head[2]=len&0xff;head[3]=(len>>8)&0xff;
-    XY_Write(fd,head,4);
-    for(i=0;i<len;i++) {
-        out_uint32(fd,data[i]);
-    }
-    return 0;
-}
 static int
-xl_sint16_array(int fd,signed short *data,int len) {
-    int i;
-    unsigned char head[4];
-    head[0]=0xcb;head[1]=0xc1;head[2]=len&0xff;head[3]=(len>>8)&0xff;
-    XY_Write(fd,head,4);
-    for(i=0;i<len;i++) {
-        out_sint16(fd,data[i]);
-    }
-    return 0;
-}
-static int
-xl_sint32_array(int fd,signed int *data,int len) {
-    int i;
-    unsigned char head[4];
-    head[0]=0xcc;head[1]=0xc1;head[2]=len&0xff;head[3]=(len>>8)&0xff;
-    XY_Write(fd,head,4);
-    for(i=0;i<len;i++) {
-        out_sint32(fd,data[i]);
-    }
-    return 0;
-}
+xl_uint16_xy(int            const fd,
+             unsigned short const xdata,
+             unsigned short const ydata ) {
 
-static int
-xl_ubyte_xy(int fd,unsigned char xdata,unsigned char ydata) {
-    unsigned char const tag=0xd0;
-    XY_Write(fd,&data,1);
-    out_ubyte(fd,ydata);
-    return out_ubyte(fd,xdata);
+    unsigned char const tag = 0xd1;
+
+    XY_Write(fd, &tag, 1);
+    out_uint16(fd, xdata);
+
+    return out_uint16(fd, ydata);
 }
-#endif
+
+
+
 static int
-xl_uint16_xy(int fd,unsigned short xdata,unsigned short ydata ) {
-    unsigned char const tag=0xd1;
-    XY_Write(fd,&tag,1);
-    out_uint16(fd,xdata);
-    return out_uint16(fd,ydata);
+xl_sint16_xy(int          const fd,
+             signed short const xdata,
+             signed short const ydata ) {
+
+    unsigned char const tag = 0xd3;
+
+    XY_Write(fd, &tag, 1);
+
+    out_sint16(fd, xdata);
+
+    return out_sint16(fd, ydata);
 }
-#if 0
+
+
+
 static int
-xl_uint32_xy(int fd,unsigned int xdata,unsigned int ydata ) {
-    unsigned char const tag=0xd2;
-    XY_Write(fd,&tag,1);
-    out_uint32(fd,xdata);
-    return out_uint32(fd,ydata);
+xl_attr_ubyte(int            const fd,
+              enum Attribute const data) {
+
+    unsigned char const tag = 0xf8;
+
+    XY_Write(fd, &tag, 1);
+
+    return out_ubyte(fd, data);
 }
-#endif
+
+
+
 static int
-xl_sint16_xy(int fd,signed short xdata,signed short ydata ) {
-    unsigned char const tag=0xd3;
-    XY_Write(fd,&tag,1);
-    out_sint16(fd,xdata);
-    return out_sint16(fd,ydata);
+xl_dataLength(int          const fd,
+              unsigned int const dataLength ) {
+
+    unsigned char const tag = 0xfa;
+
+    XY_Write(fd, &tag, 1);
+
+    return out_uint32(fd, dataLength);
 }
 
-#if 0
-static int
-xl_sint32_xy(int fd,signed int xdata,signed int ydata ) {
-    unsigned char const tag=0xd4;
-    XY_Write(fd,&tag,1);
-    out_sint32(fd,xdata);
-    return out_sint32(fd,ydata);
+
+
+static void
+openDataSource(int             const outFd,
+               enum DataOrg    const dataOrg,
+               enum DataSource const dataSource) {
+
+    xl_ubyte(outFd, dataOrg);    xl_attr_ubyte(outFd, aDataOrg);
+    xl_ubyte(outFd, dataSource); xl_attr_ubyte(outFd, aSourceType);
+    XL_Operator(outFd, oOpenDataSource);
 }
-#endif
 
-static int
-xl_attr_ubyte(int fd,enum Attribute const data) {
-    unsigned char const tag=0xf8;
-    XY_Write(fd,&tag,1);
-    return out_ubyte(fd,data);
+
+
+static void
+closeDataSource(int const outFd) {
+
+    XL_Operator(outFd, oCloseDataSource);
 }
-#if 0
-static int
-xl_attr_uint16(int fd,enum Attribute const data ) {
-    unsigned char const tag=0xf9;
-    XY_Write(fd,&tag,1);
-    return out_uint16(fd,data);
+
+
+
+static void
+convertAndWriteRleBlock(int                  const outFd,
+                        pclGenerator *       const pclGeneratorP,
+                        struct pam *         const pamP,
+                        int                  const firstLine,
+                        int                  const lineCt,
+                        unsigned char *      const outbuf) {
+
+    size_t rlelen;
+    unsigned int line;
+
+    pclGeneratorP->cursor = 0;
+    for (line = firstLine; line < firstLine + lineCt; ++line) {
+        pclGeneratorP->getnextrow(pclGeneratorP, pamP);
+    }
+
+    pm_rlenc_compressbyte(pclGeneratorP->data, outbuf, PM_RLE_PACKBITS,
+                          pclGeneratorP->paddedLinelen * lineCt, &rlelen);
+
+    xl_dataLength(outFd, rlelen); 
+    XY_Write(outFd, outbuf, rlelen);
 }
-#endif
-static int
-xl_dataLength(int fd,unsigned int dataLength ) {
-    unsigned char const tag=0xfa;
-    XY_Write(fd,&tag,1);
-    return out_uint32(fd,dataLength);
+
+
+
+/*
+ * ------------------------------------------------------------
+ * XL_WriteImage
+ *  Write a PCL-XL image to the datastream 
+ * ------------------------------------------------------------
+ */
+static void 
+convertAndWriteImage(int            const outFd,
+                     pclGenerator * const pclGenP,
+                     struct pam *   const pamP) {
+
+    size_t const inSize = (pclGenP-> linelen + 3) * 20;
+
+    int blockStartLine;
+    unsigned char * outbuf;
+
+    xl_ubyte(outFd, eDirectPixel); xl_attr_ubyte(outFd, aColorMapping);
+    xl_ubyte(outFd, pclGenP->colorDepth); xl_attr_ubyte(outFd, aColorDepth);
+    xl_uint16(outFd, pclGenP->width); xl_attr_ubyte(outFd, aSourceWidth);  
+    xl_uint16(outFd, pclGenP->height); xl_attr_ubyte(outFd, aSourceHeight);    
+    xl_uint16_xy(outFd, pclGenP->width*1, pclGenP->height*1); 
+    xl_attr_ubyte(outFd, aDestinationSize);   
+    XL_Operator(outFd, oBeginImage);
+
+    pm_rlenc_allocoutbuf(&outbuf, inSize, PM_RLE_PACKBITS);
+
+    for (blockStartLine = 0; blockStartLine < pclGenP->height; ) {
+        unsigned int const blockHeight =
+            MIN(20, pclGenP->height-blockStartLine);
+
+        xl_uint16(outFd, blockStartLine); xl_attr_ubyte(outFd, aStartLine); 
+        xl_uint16(outFd, blockHeight); xl_attr_ubyte(outFd, aBlockHeight);
+        xl_ubyte(outFd, eRLECompression); xl_attr_ubyte(outFd, aCompressMode);
+        /* In modern PCL-XL, we could use a PadBytesMultiple attribute
+           here to avoid having to pad the data to a multiple of 4
+           bytes.  But PCL-XL 1.1 didn't have PadBytesMultiple.
+           xl_ubyte(outFd, 1); xl_attr_ubyte(outFd, aPadBytesMultiple); 
+        */
+        XL_Operator(outFd, oReadImage);
+        convertAndWriteRleBlock(outFd, pclGenP, pamP,
+                                blockStartLine, blockHeight, outbuf);
+        blockStartLine += blockHeight;
+    }
+    pm_rlenc_freebuf(outbuf);
+    XL_Operator(outFd, oEndImage);
 }
 
-#if 0
-static int
-xl_dataLengthbytes(int fd,unsigned char dataLengthBytes) {
-    unsigned char const tag=0xfb;
-    XY_Write(fd,&tag,1);
-    return out_ubyte(fd,dataLengthBytes);
+
+
+static void
+printEmbeddedImage(int                 const outFd,
+                   const InputSource * const sourceP,
+                   bool                const colorok) {
+
+    FILE * ifP;
+    struct pam pam;
+    pclGenerator * pclGeneratorP;
+
+    openDataSource(outFd, eBinaryLowByteFirst, eDefaultSource);
+
+    ifP = pm_openr(sourceP->name);
+
+    pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
+                
+    createPclGenerator(&pam, &pclGeneratorP, colorok);
+
+    convertAndWriteImage(outFd, pclGeneratorP, &pam);
+    
+    destroyPclGenerator(pclGeneratorP);
+
+    pm_close(ifP);
+
+    closeDataSource(outFd);
 }
-#endif 
 
 
 
@@ -628,10 +810,10 @@ jobHead(int          const outFd,
         copyFile(userJobSetupFileName, outFd);
 
     if (renderGray)
-        XY_Puts(outFd,"@PJL SET RENDERMODE=GRAYSCALE\n");  
+        XY_Puts(outFd, "@PJL SET RENDERMODE=GRAYSCALE\n");  
 
-    XY_Puts(outFd,"@PJL ENTER LANGUAGE=PCLXL\n");  
-    XY_Puts(outFd,") HP-PCL XL;1;1;Generated by Netpbm Pnmtopclxl\n");  
+    XY_Puts(outFd, "@PJL ENTER LANGUAGE=PCLXL\n");  
+    XY_Puts(outFd, ") HP-PCL XL;1;1;Generated by Netpbm Pnmtopclxl\n");  
 }
 
 
@@ -683,18 +865,6 @@ beginPage(int                 const outFd,
 
 
 static void
-openDataSource(int             const outFd,
-               enum DataOrg    const dataOrg,
-               enum DataSource const dataSource) {
-
-    xl_ubyte(outFd, dataOrg); xl_attr_ubyte(outFd,aDataOrg);
-    xl_ubyte(outFd, dataSource); xl_attr_ubyte(outFd,aSourceType);
-    XL_Operator(outFd,oOpenDataSource);
-}
-
-
-
-static void
 setColorSpace(int                   const outFd,
               enum Colorspace       const colorSpace,
               const unsigned char * const palette,
@@ -747,7 +917,7 @@ positionCursor(int            const outFd,
                float          const yoffs,
                int            const imageWidth,
                int            const imageHeight,
-               int            const dpi,
+               unsigned int   const dpi,
                enum MediaSize const format) {
 /*----------------------------------------------------------------------------
    Emit printer control to position the cursor to start the page.
@@ -771,88 +941,6 @@ positionCursor(int            const outFd,
 
 
 static void
-convertAndWriteRleBlock(int                  const outFd,
-                        const pclGenerator * const pclGeneratorP,
-                        struct pam *         const pamP,
-                        int                  const firstLine,
-                        int                  const nlines,
-                        XY_rle *             const rle) {
-
-    unsigned char const pad[4] = {0,0,0,0};
-    unsigned int const paddedLinelen = ((pclGeneratorP->linelen+3)/4)*4;
-    int rlelen;
-    unsigned int line;
-    
-    XY_RLEreset(rle);
-
-    for (line = firstLine; line < firstLine + nlines; ++line) {
-        pclGeneratorP->getnextrow(pclGeneratorP, pamP);
-        XY_RLEput(rle, pclGeneratorP->data, pclGeneratorP->linelen);
-        XY_RLEput(rle, pad, paddedLinelen - pclGeneratorP->linelen);
-    }
-    rlelen = XY_RLEfinish(rle);
-    if (rlelen<0) 
-        pm_error("Error on Making rle");
-
-    xl_dataLength(outFd, rlelen); 
-    XY_Write(outFd, rle->fbuf, rlelen);
-}
-
-
-
-/*
- * ------------------------------------------------------------
- * XL_WriteImage
- *  Write a PCL-XL image to the datastream 
- * ------------------------------------------------------------
- */
-static void 
-convertAndWriteImage(int                  const outFd,
-                     const pclGenerator * const pclGenP,
-                     struct pam *         const pamP) {
-
-    int blockStartLine;
-    XY_rle * rle;
-
-    xl_ubyte(outFd, eDirectPixel); xl_attr_ubyte(outFd, aColorMapping);
-    xl_ubyte(outFd, pclGenP->colorDepth); xl_attr_ubyte(outFd, aColorDepth);
-    xl_uint16(outFd, pclGenP->width); xl_attr_ubyte(outFd, aSourceWidth);  
-    xl_uint16(outFd, pclGenP->height); xl_attr_ubyte(outFd, aSourceHeight);    
-    xl_uint16_xy(outFd, pclGenP->width*1, pclGenP->height*1); 
-    xl_attr_ubyte(outFd, aDestinationSize);   
-    XL_Operator(outFd, oBeginImage);
-
-    if (pclGenP->linelen > INT_MAX / 20)
-        pm_error("Image too big");
-    rle = XY_RLEnew(pclGenP->linelen*20);
-    if (!rle) 
-        pm_error("Unable to allocate %d bytes for the RLE buffer",
-                 pclGenP->linelen * 20);
-
-    blockStartLine = 0;
-    while (blockStartLine < pclGenP->height) {
-        unsigned int const blockHeight =
-            MIN(20, pclGenP->height-blockStartLine);
-        xl_uint16(outFd, blockStartLine); xl_attr_ubyte(outFd, aStartLine); 
-        xl_uint16(outFd, blockHeight); xl_attr_ubyte(outFd, aBlockHeight);
-        xl_ubyte(outFd, eRLECompression); xl_attr_ubyte(outFd, aCompressMode);
-        /* In modern PCL-XL, we could use a PadBytesMultiple attribute
-           here to avoid having to pad the data to a multiple of 4
-           bytes.  But PCL-XL 1.1 didn't have PadBytesMultiple.
-           xl_ubyte(outFd, 1); xl_attr_ubyte(outFd, aPadBytesMultiple); 
-        */
-        XL_Operator(outFd, oReadImage);
-        convertAndWriteRleBlock(outFd, pclGenP, pamP,
-                                blockStartLine, blockHeight, rle);
-        blockStartLine += blockHeight;
-    }
-    XY_RLEdelete(rle);
-    XL_Operator(outFd, oEndImage);
-}
-
-
-
-static void
 endPage(int          const outFd,
         bool         const doCopies,
         unsigned int const copies) {
@@ -870,10 +958,10 @@ endPage(int          const outFd,
 
 static void
 convertAndPrintPage(int                  const outFd,
-                    const pclGenerator * const pclGeneratorP,
+                    pclGenerator *       const pclGeneratorP,
                     struct pam *         const pamP,
                     enum MediaSize       const format,
-                    int                  const dpi,
+                    unsigned int         const dpi,
                     bool                 const center,
                     float                const xoffs,
                     float                const yoffs,
@@ -936,161 +1024,10 @@ endSession(int outFd) {
 
 
 static void
-pnmToPcllinePackbits(const pclGenerator * const pclGeneratorP,
-                     struct pam *         const pamP) {
-
-    tuple * tuplerow;
-    unsigned int pcl_cursor;
-    unsigned char accum;
-    unsigned char bitmask;
-    unsigned int col;
-        
-    tuplerow = pnm_allocpamrow(pamP);
-
-    pnm_readpamrow(pamP, tuplerow);
-
-    pcl_cursor = 0; bitmask = 0x80; accum = 0x00;
-    for (col = 0; col < pamP->width; ++col) {
-        if (tuplerow[col][0] == PAM_PBM_WHITE)
-            accum |= bitmask;
-        bitmask >>= 1;
-        if (bitmask == 0) {
-            pclGeneratorP->data[pcl_cursor++] = accum;
-            bitmask = 0x80; accum = 0x0;
-        } 
-    }
-    if (bitmask != 0x80)
-        pclGeneratorP->data[pcl_cursor++] = accum;
-
-    pnm_freepamrow(tuplerow);
-}
-
-
-
-static void
-createPclGeneratorPackbits(struct pam *    const pamP,
-                           pclGenerator ** const pclGeneratorPP) {
-
-    /* Samples are black or white and packed 8 to a byte */
-
-    pclGenerator * pclGeneratorP;
-
-    MALLOCVAR_NOFAIL(pclGeneratorP);
-
-    pclGeneratorP->colorDepth = e1Bit;
-    pclGeneratorP->colorSpace = eGray;
-    pclGeneratorP->linelen = (pamP->width+7)/8;
-    pclGeneratorP->height = pamP->height;
-    pclGeneratorP->width = (pamP->width);
-
-    pclGeneratorP->data = malloc(pclGeneratorP->linelen);
-    
-    if (pclGeneratorP->data == NULL)
-        pm_error("Unable to allocate row buffer.");
-
-    pclGeneratorP->getnextrow = pnmToPcllinePackbits;
-
-    *pclGeneratorPP = pclGeneratorP;
-}
-
-
-
-static void
-pnmToPcllineWholebytes(const pclGenerator * const pclGeneratorP,
-                       struct pam *         const pamP) {
-
-    tuple * tuplerow;
-    unsigned int pcl_cursor;
-    unsigned int col;
-
-    tuplerow = pnm_allocpamrow(pamP);
-
-    pnm_readpamrow(pamP, tuplerow);
-
-    pcl_cursor = 0; /* initial value */
-    
-    for (col = 0; col < pamP->width; ++col) {
-        unsigned int plane;
-        for (plane = 0; plane < pamP->depth; ++plane) {
-            pclGeneratorP->data[pcl_cursor++] = 
-                pnm_scalesample(tuplerow[col][plane], pamP->maxval, 255);
-        }
-    }
-    pnm_freepamrow(tuplerow);
-}
-
-
-
-static void
-createPclGeneratorWholebytes(struct pam *    const pamP,
-                             pclGenerator ** const pclGenPP) {
-    /* One sample per byte */
-
-    pclGenerator * pclGenP;
-
-    MALLOCVAR_NOFAIL(pclGenP);
-    
-    if (pamP->depth <  3)
-        pclGenP->colorSpace = eGray;
-    else
-        pclGenP->colorSpace = eRGB;
-
-    pclGenP->colorDepth = e8Bit;
-    pclGenP->height     = pamP->height;
-    pclGenP->width      = pamP->width;
-    pclGenP->linelen    = pamP->width * pamP->depth;
-
-    if (UINT_MAX / pamP->width < pamP->depth)
-        pm_error("Image to big to process");
-    else
-        pclGenP->data = malloc(pamP->width * pamP->depth);
-
-    if (pclGenP->data == NULL)
-        pm_error("Unable to allocate row buffer.");
-    
-    pclGenP->getnextrow = pnmToPcllineWholebytes;
-
-    *pclGenPP = pclGenP;
-}
-
-
-
-static void
-destroyPclGenerator(pclGenerator * const pclGenP) {
-
-    free(pclGenP->data);
-
-    free(pclGenP);
-}
-
-
-
-static void 
-createPclGenerator(struct pam *        const pamP,
-                   pclGenerator **     const pclGeneratorPP,
-                   bool                const colorok) {
-
-    if (pamP->depth > 1 && !colorok)
-        pm_message("WARNING: generating a color print stream because the "
-                   "input image is PPM.  "
-                   "To generate a black and white print stream, run the input "
-                   "through Ppmtopgm.  To suppress this warning, use the "
-                   "-colorok option.");
-
-    if (pamP->depth == 1 && pamP->maxval == 1) 
-        createPclGeneratorPackbits(pamP, pclGeneratorPP);
-    else 
-        createPclGeneratorWholebytes(pamP, pclGeneratorPP);
-}
-
-
-
-
-static void
 printPages(int                 const outFd,
            InputSource *       const firstSourceP,
            enum MediaSize      const format,
-           int                 const dpi,
+           unsigned int        const dpi,
            bool                const center,
            float               const xoffs,
            float               const yoffs,
@@ -1116,12 +1053,12 @@ printPages(int                 const outFd,
     sourceNum = 0;   /* initial value */
 
     while (sourceP) {
-        FILE * in_file;
+        FILE * ifP;
         struct pam pam;
-        bool eof;
+        int eof;
         unsigned int pageNum;
 
-        in_file = pm_openr(sourceP->name);
+        ifP = pm_openr(sourceP->name);
 
         ++sourceNum;
 
@@ -1129,14 +1066,14 @@ printPages(int                 const outFd,
 
         eof = FALSE;
         while(!eof) {
-            pnm_nextimage(in_file, &eof);
+            pnm_nextimage(ifP, &eof);
             if (!eof) {
                 pclGenerator * pclGeneratorP;
 
                 ++pageNum;
                 pm_message("Processing File %u, Page %u", sourceNum, pageNum);
 
-                pnm_readpaminit(in_file, &pam, PAM_STRUCT_SIZE(tuple_type));
+                pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
                 
                 createPclGenerator(&pam, &pclGeneratorP, colorok);
                 
@@ -1149,25 +1086,10 @@ printPages(int                 const outFd,
                 destroyPclGenerator(pclGeneratorP);
             }
         }
-        pm_close(in_file);
+        pm_close(ifP);
         sourceP = sourceP->next; 
     }
-    XL_Operator(outFd, oCloseDataSource);
-}
-
-
-
-static void
-freeSource(InputSource * const firstSourceP) {
-    
-    InputSource * sourceP;
-
-    sourceP = firstSourceP;
-    while(sourceP) {
-        InputSource * const nextP = sourceP->next;
-        free(sourceP);
-        sourceP = nextP;
-    }
+    closeDataSource(outFd);
 }
 
 
@@ -1189,25 +1111,28 @@ main(int argc, char *argv[]) {
 
     parseCommandLine(argc, argv, &cmdline);
 
-    jobHead(outFd, cmdline.rendergray, cmdline.jobsetup);
-
-    beginSession(outFd, cmdline.dpi, cmdline.dpi, eInch, 
-                 FALSE, eBackChAndErrPage);
-
-    printPages(outFd,cmdline.sourceP,
-               cmdline.format, cmdline.dpi, cmdline.center,
-               cmdline.xoffs, cmdline.yoffs,
-               cmdline.duplexSpec, cmdline.duplex,
-               cmdline.copiesSpec, cmdline.copies,
-               cmdline.feederSpec, cmdline.feeder,
-               cmdline.outtraySpec, cmdline.outtray,
-               cmdline.colorok
-        );
-    endSession(outFd);
-
-    jobEnd(outFd);
-
-    freeSource(cmdline.sourceP);
+    if (cmdline.embedded)
+        printEmbeddedImage(outFd, cmdline.sourceP, cmdline.colorok);
+    else {
+        jobHead(outFd, cmdline.rendergray, cmdline.jobsetup);
+
+        beginSession(outFd, cmdline.dpi, cmdline.dpi, eInch, 
+                     FALSE, eBackChAndErrPage);
+
+        printPages(outFd, cmdline.sourceP,
+                   cmdline.format, cmdline.dpi, cmdline.center,
+                   cmdline.xoffs, cmdline.yoffs,
+                   cmdline.duplexSpec, cmdline.duplex,
+                   cmdline.copiesSpec, cmdline.copies,
+                   cmdline.feederSpec, cmdline.feeder,
+                   cmdline.outtraySpec, cmdline.outtray,
+                   cmdline.colorok
+            );
+        endSession(outFd);
+
+        jobEnd(outFd);
+    }
+    freeCmdline(cmdline);
 
     return 0;
 }
diff --git a/converter/other/pnmtopng.README b/converter/other/pnmtopng.README
deleted file mode 100644
index bfa524dc..00000000
--- a/converter/other/pnmtopng.README
+++ /dev/null
@@ -1,101 +0,0 @@
-Pnmtopng and Pngtopnm are based on programs of the same name in the 
-Pnmtopng package owned by Alexander Lehmann and Willem Van Schaik,
-available at http://www.libpng.org/pub/png/src on 2001.07.14.
-
-I added it to and adapted it to Netpbm on 2000.03.02 to make it more
-easily available to people.  I applied a patch on 2000.06.03 to bring
-it up the 2.37.4 release of that package.  I updated it again on
-2001.07.14 to bring it up to Release 2.37.5.  There is no process in
-place to bring improvements to the base package into the Netpbm
-version, but there hasn't been a lot of update activity anyway.
-
-Attached below is the file README from Release 2.37.5 of the base
-package.
-
-Here are the differences between the base and the Netpbm version:
-
-  I added an "unsigned" to make formal and actual arguments to png_sig_cmp()
-  match and quiet a compiler warning.
-
-  I fixed an "include" statement so the dependencies work out right.
-
-  I removed the BIGGRAYS stuff, which became obsolete in Netpbm 9.0
-
-  I replaced a PPM_MAXVAL with PPM_OVERALLMAXAL to handle the new 16 bits
-  formats.
-
-  macro VERSION is defined directly in pngtopnm.c and pnmtopng.c instead
-  of being included via file version.h.
-
-  Pnmtopng, since June 2001, reads one row at a time instead of holding 
-  the entire image in memory.  That makes it work with large bitmaps
-  where it would otherwise run out of memory.  It also works faster with
-  bitmaps since a bit takes up only a bit of memory in a cached input 
-  file, but 96 bits of memory after reading it into a Netpbm data 
-  structure.
-
-  The base Pnmtopng ignores -transparent if it specifies a color that
-  isn't in the image.  Netpbm's Pnmtopng selects a nearby color that _is_
-  in the image, which is what base Pnmtopng did before October 2000.
-  Netpbm's Pnmtopng lets you put an '=' sign before the color to specify
-  that you don't want a nearby color to be chosen, i.e. you want the
-  base Pnmtopng function.  This is consistent with Pnmtogif.
-
-There were some other changes necessary before Netpbm 9.0, but the change
-of the xelval type from 1 byte to 4 made them unnecessary.
-
-
-** PNMTOPNG / PNGTOPNM
-** version 2.37.5 - 24 October 2000
-
-[This is a semi-official bug-fix and enhancement release; I sort of took over
- maintenance of this package while Willem was on an extended bike trip, and
- for now I'm continuing with periodic, small updates.  Version 2.37 (March
- 1998) was never publicly released, partly because Willem had hoped to quiet
- gcc's "<var> might be clobbered by `longjmp'" warnings.  Those are fixed in
- 2.37.2; under Solaris, they resulted in stack corruption even when there was
- no error in the image files or libraries.  Version 2.37.3 fixes a minor bug
- w.r.t. error exits and generally does cleaner error exits (close files, etc.)
- Version 2.37.4 fixes a bug that caused 16-shade grayscale images to be written
- as 8-bit grayscale instead of (smaller) 4-bit colormapped images (bug report,
- analysis and fix by Rafal Rzeczkowski), and it supports the new/upcoming
- pbmplus release.  Version 2.37.5 fixes a bug in -transparent handling (pnmtopng
- no longer chooses an approximate color if the specified one isn't present) and
- quiets a gcc warning in the non-16-bit version.
- --Greg Roelofs]
-
-The utilities pnmtopng and pngtopnm are based on other pbm tools and require
-the libraries included in the pbmplus/netpbm package. Also required are the
-png library and the zlib compression library.
-
-These can be found at:
-	ftp://swrinde.nde.swri.edu/pub/png/src/libpng-*
-	ftp://swrinde.nde.swri.edu/pub/png/src/zlib-*
-	ftp://ftp.x.org/contrib/utilities/netpbm-1mar1994*
-or see
-	http://www.libpng.org/pub/png/apps/pnmtopng.html
-	http://netpbm.sourceforge.net/
-	http://www.acme.com/software/pbmplus/		[update coming soon?]
-
-To compile and install a makefile is provided. Do check the directories
-where you have put the required libraries. Then either accommodate the 
-makefile or make links from generic names (e.g., zlib) to version-specific
-directories (e.g., zlib-1.1.3), which is the recommended way.
-
-For testing purposes, have a look at the test-set PngSuite.tar.gz, which
-contains a small test-image for every PNG color type and for most PNG chunk
-types. It can be found at:
-	http://www.schaik.com/pngsuite/pngsuite.html
-	ftp://swrinde.nde.swri.edu/pub/png/images/suite/
-
-Other web pages with PNG images are at:
-	http://www.libpng.org/pub/png/png-textures.html
-	http://www.libpng.org/pub/png/pngs-img.html
-	http://www.libpng.org/pub/png/pngpic2.html
-	http://www.libpng.org/pub/png/colorcube/
-	http://www.libpng.org/pub/png/pngmisc.html#images
-
-------
-Alexander Lehmann <lehmann@usa.net>
-Willem van Schaik <willem@schaik.com>
-Greg Roelofs <newt@pobox.com>
diff --git a/converter/other/pnmtopng.c b/converter/other/pnmtopng.c
index 76c7a3a4..3899a9d2 100644
--- a/converter/other/pnmtopng.c
+++ b/converter/other/pnmtopng.c
@@ -1,6 +1,5 @@
 /*
-** pnmtopng.c -
-** read a portable anymap and produce a Portable Network Graphics file
+** read a PNM image and produce a Portable Network Graphics file
 **
 ** derived from pnmtorast.c (c) 1990,1991 by Jef Poskanzer and some
 ** parts derived from ppmtogif.c by Marcel Wijkstra <wijkstra@fwi.uva.nl>
@@ -59,31 +58,22 @@
 #include <assert.h>
 #include <string.h> /* strcat() */
 #include <limits.h>
-#include <png.h>    /* includes zlib.h and setjmp.h */
+#include <png.h>
+/* Because of a design error in png.h, you must not #include <setjmp.h> before
+   <png.h>.  If you do, png.h won't compile.
+*/
+#include <setjmp.h> 
+#include <zlib.h>
 
 #include "pm_c_util.h"
 #include "pnm.h"
+#include "pngx.h"
 #include "pngtxt.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "version.h"
 
-#if PNG_LIBPNG_VER >= 10500
-#error Your PNG library (<png.h>) is incompatible with this Netpbm source code.
-#error You need either an older PNG library (older than 1.5) or
-#error newer Netpbm source code (at least 10.55)
-#endif
-
-/* 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
-
-
 struct zlibCompression {
     /* These are parameters that describe a form of zlib compression.
        Values have the same meaning as the similarly named arguments to
@@ -103,23 +93,6 @@ struct zlibCompression {
     unsigned int buffer_size;
 };
 
-struct chroma {
-    float wx;
-    float wy;
-    float rx;
-    float ry;
-    float gx;
-    float gy;
-    float bx;
-    float by;
-};
-
-struct phys {
-    int x;
-    int y;
-    int unit;
-};
-
 typedef struct cahitem {
     xel color;
     gray alpha;
@@ -133,7 +106,7 @@ 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 *  inputFileName;  /* '-' if stdin */
     const char *  alpha;
     unsigned int  verbose;
     unsigned int  downscale;
@@ -144,9 +117,11 @@ struct cmdlineInfo {
     float         gamma;        /* Meaningless if !gammaSpec */
     unsigned int  hist;
     unsigned int  rgbSpec;
-    struct chroma rgb;          /* Meaningless if !rgbSpec */
+    struct pngx_chroma rgb;          /* Meaningless if !rgbSpec */
     unsigned int  sizeSpec;
-    struct phys   size;         /* Meaningless if !sizeSpec */
+    struct pngx_phys   size;         /* Meaningless if !sizeSpec */
+    unsigned int srgbintentSpec;
+    pngx_srgbIntent srgbintent;
     const char *  text;         /* NULL if none */
     const char *  ztxt;         /* NULL if none */
     unsigned int  modtimeSpec;
@@ -156,7 +131,6 @@ struct cmdlineInfo {
     int           filterSet;
     unsigned int  force;
     unsigned int  libversion;
-    unsigned int  compressionSpec;
     struct zlibCompression zlibCompression;
 };
 
@@ -192,8 +166,8 @@ static int errorlevel;
 
 
 static void
-parseSizeOpt(const char *  const sizeOpt,
-             struct phys * const sizeP) {
+parseSizeOpt(const char *       const sizeOpt,
+             struct pngx_phys * const sizeP) {
 
     int count;
     
@@ -207,8 +181,8 @@ parseSizeOpt(const char *  const sizeOpt,
 
 
 static void
-parseRgbOpt(const char *    const rgbOpt,
-            struct chroma * const rgbP) {
+parseRgbOpt(const char *         const rgbOpt,
+            struct pngx_chroma * const rgbP) {
 
     int count;
     
@@ -228,6 +202,27 @@ parseRgbOpt(const char *    const rgbOpt,
 
 
 static void
+parseSrgbintentOpt(const char *      const srgbintentOpt,
+                   pngx_srgbIntent * const srgbintentP) {
+    
+    if (streq(srgbintentOpt, "perceptual"))
+        *srgbintentP = PNGX_PERCEPTUAL;
+    else if (streq(srgbintentOpt, "relativecolorimetric"))
+        *srgbintentP = PNGX_RELATIVE_COLORIMETRIC;
+    else if (streq(srgbintentOpt, "saturation"))
+        *srgbintentP = PNGX_SATURATION;
+    else if (streq(srgbintentOpt, "absolutecolorimetric"))
+        *srgbintentP = PNGX_ABSOLUTE_COLORIMETRIC;
+    else
+        pm_error("Unrecognized sRGB intent value '%s'.  We understand "
+                 "only 'perceptual', 'relativecolorimetric', "
+                 "'saturation', and 'absolutecolorimetric'",
+                 srgbintentOpt);
+}
+
+
+
+static void
 parseModtimeOpt(const char * const modtimeOpt,
                 time_t *     const modtimeP) {
 
@@ -291,7 +286,7 @@ parseModtimeOpt(const char * const modtimeOpt,
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
@@ -304,7 +299,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -321,6 +316,7 @@ parseCommandLine(int argc, char ** argv,
     const char * modtime;
     const char * compMethod;
     const char * compStrategy;
+    const char * srgbintent;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
@@ -335,6 +331,8 @@ parseCommandLine(int argc, char ** argv,
             &cmdlineP->rgbSpec,    0);
     OPTENT3(0, "size",             OPT_STRING,    &size,
             &cmdlineP->sizeSpec,   0);
+    OPTENT3(0,  "srgbintent",      OPT_STRING,    &srgbintent,
+            &cmdlineP->srgbintentSpec, 0);
     OPTENT3(0, "text",             OPT_STRING,    &cmdlineP->text,
             &textSpec,             0);
     OPTENT3(0, "ztxt",             OPT_STRING,    &cmdlineP->ztxt,
@@ -399,7 +397,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
@@ -469,6 +467,9 @@ parseCommandLine(int argc, char ** argv,
     if (cmdlineP->rgbSpec)
         parseRgbOpt(rgb, &cmdlineP->rgb);
     
+    if (cmdlineP->srgbintentSpec)
+        parseSrgbintentOpt(srgbintent, &cmdlineP->srgbintent);
+
     if (cmdlineP->modtimeSpec)
         parseModtimeOpt(modtime, &cmdlineP->modtime);
 
@@ -506,19 +507,41 @@ parseCommandLine(int argc, char ** argv,
 
 
     if (argc-1 < 1)
-        cmdlineP->inputFilename = "-";
+        cmdlineP->inputFileName = "-";
     else if (argc-1 == 1)
-        cmdlineP->inputFilename = argv[1];
+        cmdlineP->inputFileName = argv[1];
     else
         pm_error("Program takes at most one argument:  input file name");
 }
 
 
 
+static void
+reportInputType(int    const format,
+                xelval const maxval) {
+
+    switch (PNM_FORMAT_TYPE(format)) {
+    case PBM_TYPE:
+        pm_message ("reading a PBM file");
+        break;
+    case PGM_TYPE:
+        pm_message ("reading a PGM file (maxval=%d)", maxval);
+        break;
+    case PPM_TYPE:
+        pm_message ("reading a PPM file (maxval=%d)", maxval);
+        break;
+    default:
+        assert(false);
+    }
+}
+
+
+
 static png_color_16
-xelToPngColor_16(xel const input, 
+xelToPngColor_16(xel    const input, 
                  xelval const maxval, 
                  xelval const pngMaxval) {
+
     png_color_16 retval;
 
     xel scaled;
@@ -648,36 +671,6 @@ lookupColorAlpha(coloralphahash_table const caht,
 
 
 
-static void
-pnmtopng_error_handler(png_structp     const png_ptr,
-                       png_const_charp const 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. */
-
-  fprintf(stderr, "pnmtopng:  fatal libpng error: %s\n", msg);
-  fflush(stderr);
-
-  jmpbuf_ptr = png_get_error_ptr(png_ptr);
-  if (jmpbuf_ptr == NULL) {         /* we are completely hosed now */
-    fprintf(stderr,
-      "pnmtopng:  EXTREMELY fatal error: jmpbuf unrecoverable; terminating.\n");
-    fflush(stderr);
-    exit(99);
-  }
-
-  longjmp(jmpbuf_ptr->jmpbuf, 1);
-}
-
-
 /* The following variables belong to getChv() and freeChv() */
 static bool getChv_computed = FALSE;
 static colorhist_vector getChv_chv;
@@ -921,7 +914,7 @@ tryTransparentColor(FILE *     const ifp,
                     pixel      const transcolor,
                     bool *     const singleColorIsTransP) {
 
-    int const pnm_type = PNM_FORMAT_TYPE(format);
+    int const pnmType = PNM_FORMAT_TYPE(format);
 
     xel * xelrow;
     bool singleColorIsTrans;
@@ -942,7 +935,7 @@ tryTransparentColor(FILE *     const ifp,
                 /* If we have a second transparent color, we're
                    disqualified
                 */
-                if (pnm_type == PPM_TYPE) {
+                if (pnmType == PPM_TYPE) {
                     if (!PPM_EQUAL(xelrow[col], transcolor))
                         singleColorIsTrans = FALSE;
                 } else {
@@ -959,7 +952,7 @@ tryTransparentColor(FILE *     const ifp,
                    the same color as our candidate transparent color,
                    that disqualifies us.
                 */
-                if (pnm_type == PPM_TYPE) {
+                if (pnmType == PPM_TYPE) {
                     if (PPM_EQUAL(xelrow[col], transcolor))
                         singleColorIsTrans = FALSE;
                 } else {
@@ -1064,6 +1057,170 @@ analyzeAlpha(FILE *       const ifP,
 
 
 static void
+determineTransparency(struct cmdlineInfo const cmdline,
+                      FILE *             const ifP,
+                      pm_filepos         const rasterPos,
+                      unsigned int       const cols,
+                      unsigned int       const rows,
+                      xelval             const maxval,
+                      int                const format,
+                      FILE *             const afP,
+                      bool *             const alphaP,
+                      int *              const transparentP,
+                      pixel *            const transColorP,
+                      bool *             const transExactP,
+                      gray ***           const alphaMaskP,
+                      gray *             const alphaMaxvalP) {
+/*----------------------------------------------------------------------------
+   Determine the various aspects of transparency we need to generate the
+   PNG.
+
+   Note that there are two kinds of transparency: pixel-by-pixel
+   transparency/translucency with an alpha mask and all pixels of a certain
+   color being transparent.  Both these exist both in input from the user and
+   as representations in the PNG -- i.e. user may supply an alpha mask,
+   or identify a transparent color and the PNG may contain an alpha mask
+   or identify a transparent color.
+
+   We return as *transparentP:
+   
+     -1 PNG is not to have single-color transparency
+      1 PNG is to have single-color transparency as directed by user
+      2 PNG is to have single-color transparency that effects an alpha
+            mask that the user supplied.
+
+   In the cases where there is to be single-color transparency, *transColorP
+   is that color.
+-----------------------------------------------------------------------------*/
+    if (cmdline.alpha) {
+        pixel alphaTranscolor;
+        bool alphaCanBeTransparencyIndex;
+        bool allOpaque;
+        int alphaCols, alphaRows;
+        gray alphaMaxval;
+        gray ** alphaMask;
+
+        if (verbose)
+            pm_message("reading alpha-channel image...");
+        alphaMask = pgm_readpgm(afP, &alphaCols, &alphaRows, &alphaMaxval);
+
+        if (alphaCols != cols || alphaRows != rows) {
+            pm_error("dimensions for image and alpha mask do not agree");
+        }
+        analyzeAlpha(ifP, rasterPos, cols, rows, maxval, format, 
+                     alphaMask, alphaMaxval, &allOpaque,
+                     &alphaCanBeTransparencyIndex, &alphaTranscolor);
+
+        if (alphaCanBeTransparencyIndex && !cmdline.force) {
+            if (verbose)
+                pm_message("converting alpha mask to transparency index");
+            *alphaP       = FALSE;
+            *transparentP = 2;
+            *transColorP  = alphaTranscolor;
+        } else if (allOpaque) {
+            if (verbose)
+                pm_message("Skipping alpha because mask is all opaque");
+            *alphaP       = FALSE;
+            *transparentP = -1;
+        } else {
+            *alphaP       = TRUE;
+            *transparentP = -1;
+        }
+        *alphaMaxvalP = alphaMaxval;
+        *alphaMaskP   = alphaMask;
+    } else {
+        /* Though there's no alpha_mask, we still need an alpha_maxval for
+           use with trans[], which can have stuff in it if the user specified
+           a transparent color.
+        */
+        *alphaP       = FALSE;
+        *alphaMaxvalP = 255;
+
+        if (cmdline.transparent) {
+            const char * transstring2;  
+            /* The -transparent value, but with possible leading '=' removed */
+            if (cmdline.transparent[0] == '=') {
+                *transExactP = TRUE;
+                transstring2 = &cmdline.transparent[1];
+            } else {
+                *transExactP = FALSE;
+                transstring2 = cmdline.transparent;
+            }  
+            /* We do this funny PPM_DEPTH thing instead of just passing 'maxval'
+               to ppm_parsecolor() because ppm_parsecolor() does a cheap maxval
+               scaling, and this is more precise.
+            */
+            PPM_DEPTH(*transColorP, 
+                      ppm_parsecolor(transstring2, PNM_OVERALLMAXVAL),
+                      PNM_OVERALLMAXVAL, maxval);
+
+            *transparentP = 1;
+        } else
+            *transparentP = -1;
+    }
+}
+
+
+
+static void
+determineBackground(struct cmdlineInfo const cmdline,
+                    xelval             const maxval,
+                    xel *              const backColorP) {
+
+  if (cmdline.background) 
+      PPM_DEPTH(*backColorP,
+                ppm_parsecolor(cmdline.background, PNM_OVERALLMAXVAL), 
+                PNM_OVERALLMAXVAL, maxval);;
+}
+
+
+
+static bool
+hasColor(FILE *       const ifP,
+         unsigned int const cols,
+         unsigned int const rows,
+         xelval       const maxval,
+         int          const format,
+         pm_filepos   const rasterPos) {
+/*----------------------------------------------------------------------------
+   The image contains colors other than black, white, and gray.
+-----------------------------------------------------------------------------*/
+    bool retval;
+
+    if (PNM_FORMAT_TYPE(format) == PPM_TYPE) {
+        unsigned int row;
+        xel * xelrow;    /* malloc'ed */
+            /* The row of the input image currently being analyzed */
+        bool isGray;
+
+        xelrow = pnm_allocrow(cols);
+
+        pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
+
+        for (row = 0, isGray = true; row < rows && isGray; ++row) {
+            unsigned int col;
+
+            pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
+
+            for (col = 0; col < cols && isGray; ++col) {
+                    xel const p = xelrow[col];
+                if (PPM_GETR(p) != PPM_GETG(p) || PPM_GETG(p) != PPM_GETB(p))
+                    isGray = FALSE;
+            }
+        }
+
+        pnm_freerow(xelrow);
+
+        retval = !isGray;
+    } else
+        retval = false;
+
+    return retval;
+}
+
+
+
+static void
 findRedundantBits(FILE *         const ifp, 
                   int            const rasterPos, 
                   int            const cols,
@@ -1588,7 +1745,7 @@ makeOneColorTransparentInPalette(xel            const transColor,
         *transSizeP = 1;
         if (verbose) {
             pixel const p = palette_pnm[0];
-            pm_message("Making all occurences of color (%u, %u, %u) "
+            pm_message("Making all occurrences of color (%u, %u, %u) "
                        "transparent.",
                        PPM_GETR(p), PPM_GETG(p), PPM_GETB(p));
         }
@@ -1753,10 +1910,10 @@ tryAlphaPalette(FILE *         const ifP,
                           palette_pnm, trans_pnm, 
                           paletteSizeP, transSizeP, &tooBig);
     if (tooBig) {
-        asprintfN(impossibleReasonP,
-                  "too many color/transparency pairs "
-                  "(more than the PNG maximum of %u", 
-                  MAXPALETTEENTRIES);
+        pm_asprintf(impossibleReasonP,
+                    "too many color/transparency pairs "
+                    "(more than the PNG maximum of %u", 
+                    MAXPALETTEENTRIES);
     } else
         *impossibleReasonP = NULL;
 } 
@@ -1764,19 +1921,19 @@ tryAlphaPalette(FILE *         const ifP,
 
 
 static void
-computePixelWidth(int            const pnm_type,
-                  unsigned int   const pnm_meaningful_bits,
+computePixelWidth(bool           const colorPng,
+                  unsigned int   const pnmMeaningfulBitCt,
                   bool           const alpha,
                   unsigned int * const bitsPerSampleP,
                   unsigned int * const bitsPerPixelP) {
 
     unsigned int bitsPerSample, bitsPerPixel;
 
-    if (pnm_type == PPM_TYPE || alpha) {
+    if (colorPng || alpha) {
         /* PNG allows only depths of 8 and 16 for a truecolor image 
            and for a grayscale image with an alpha channel.
           */
-        if (pnm_meaningful_bits > 8)
+        if (pnmMeaningfulBitCt > 8)
             bitsPerSample = 16;
         else 
             bitsPerSample = 8;
@@ -1784,24 +1941,24 @@ computePixelWidth(int            const pnm_type,
         /* A grayscale, non-colormapped, no-alpha PNG may have any 
              bit depth from 1 to 16
           */
-        if (pnm_meaningful_bits > 8)
+        if (pnmMeaningfulBitCt > 8)
             bitsPerSample = 16;
-        else if (pnm_meaningful_bits > 4)
+        else if (pnmMeaningfulBitCt > 4)
             bitsPerSample = 8;
-        else if (pnm_meaningful_bits > 2)
+        else if (pnmMeaningfulBitCt > 2)
             bitsPerSample = 4;
-        else if (pnm_meaningful_bits > 1)
+        else if (pnmMeaningfulBitCt > 1)
             bitsPerSample = 2;
         else
             bitsPerSample = 1;
     }
     if (alpha) {
-        if (pnm_type == PPM_TYPE)
+        if (colorPng)
             bitsPerPixel = 4 * bitsPerSample;
         else
             bitsPerPixel = 2 * bitsPerSample;
     } else {
-        if (pnm_type == PPM_TYPE)
+        if (colorPng)
             bitsPerPixel = 3 * bitsPerSample;
         else
             bitsPerPixel = bitsPerSample;
@@ -1849,7 +2006,7 @@ computeColorMap(FILE *         const ifP,
                 int            const cols,
                 int            const rows,
                 xelval         const maxval,
-                int            const pnmType,
+                bool           const colorPng,
                 int            const format,
                 bool           const force,
                 FILE *         const pfP,
@@ -1882,29 +2039,35 @@ computeColorMap(FILE *         const ifP,
   palette_pnm[] and trans_pnm[], allocated by Caller, with sizes
   *paletteSizeP and *transSizeP.
 
+  'pfP' is a handle to the file that the user requested be used for the
+  palette (it's a Netpbm image whose colors are the colors of the palette).
+  'pfP' is null if the user did not request a particular palette.
+
   'background' means the image is to have a background color, and that
   color is 'backcolor'.  'backcolor' is meaningless when 'background'
   is false.
 
   If the image is to have a background color, we return the palette index
   of that color as *backgroundIndexP.
+
+  'colorPng' means the PNG will be of the RGB variety.
 -------------------------------------------------------------------------- */
     if (force)
-        asprintfN(noColormapReasonP, "You requested no color map");
+        pm_asprintf(noColormapReasonP, "You requested no color map");
     else if (maxval > PALETTEMAXVAL)
-        asprintfN(noColormapReasonP, "The maxval of the input image (%u) "
-                  "exceeds the PNG palette maxval (%u)", 
-                  maxval, PALETTEMAXVAL);
+        pm_asprintf(noColormapReasonP, "The maxval of the input image (%u) "
+                    "exceeds the PNG palette maxval (%u)", 
+                    maxval, PALETTEMAXVAL);
     else {
         unsigned int bitsPerPixel;
-        computePixelWidth(pnmType, pnm_meaningful_bits, alpha,
+        computePixelWidth(colorPng, pnm_meaningful_bits, alpha,
                           NULL, &bitsPerPixel);
 
         if (!pfP && bitsPerPixel == 1)
             /* No palette can beat 1 bit per pixel -- no need to waste time
                counting the colors.
             */
-            asprintfN(noColormapReasonP, "pixel is already only 1 bit");
+            pm_asprintf(noColormapReasonP, "pixel is already only 1 bit");
         else {
             /* We'll have to count the colors ('colors') to know if a
                palette is possible and desirable.  Along the way, we'll
@@ -1918,16 +2081,16 @@ computeColorMap(FILE *         const ifP,
                    &chv, &colors);
 
             if (chv == NULL) {
-                asprintfN(noColormapReasonP, 
-                          "More than %u colors found -- too many for a "
-                          "colormapped PNG", MAXCOLORS);
+                pm_asprintf(noColormapReasonP, 
+                            "More than %u colors found -- too many for a "
+                            "colormapped PNG", MAXCOLORS);
             } else {
                 /* There are few enough colors that a palette is possible */
                 if (bitsPerPixel <= paletteIndexBits(colors) && !pfP)
-                    asprintfN(noColormapReasonP, 
-                              "palette index for %u colors would be "
-                              "no smaller than the indexed value (%u bits)", 
-                              colors, bitsPerPixel);
+                    pm_asprintf(noColormapReasonP, 
+                                "palette index for %u colors would be "
+                                "no smaller than the indexed value (%u bits)", 
+                                colors, bitsPerPixel);
                 else {
                     unsigned int paletteSize;
                     unsigned int transSize;
@@ -2010,9 +2173,9 @@ static void computeColorMapLookupTable(
 
 static void
 computeRasterWidth(bool           const colorMapped,
-                   unsigned int   const palette_size,
-                   int            const pnm_type,
-                   unsigned int   const pnm_meaningful_bits,
+                   unsigned int   const paletteSize,
+                   bool           const colorPng,
+                   unsigned int   const pnmMeaningfulBitCt,
                    bool           const alpha,
                    unsigned int * const bitsPerSampleP,
                    unsigned int * const bitsPerPixelP) {
@@ -2023,24 +2186,24 @@ computeRasterWidth(bool           const colorMapped,
 -----------------------------------------------------------------------------*/
     if (colorMapped) {
         /* The raster element is a palette index */
-        if (palette_size <= 2)
+        if (paletteSize <= 2)
             *bitsPerSampleP = 1;
-        else if (palette_size <= 4)
+        else if (paletteSize <= 4)
             *bitsPerSampleP = 2;
-        else if (palette_size <= 16)
+        else if (paletteSize <= 16)
             *bitsPerSampleP = 4;
         else
             *bitsPerSampleP = 8;
         *bitsPerPixelP = *bitsPerSampleP;
         if (verbose)
-            pm_message("Writing %d-bit color indexes", *bitsPerSampleP);
+            pm_message("Writing %u-bit color indexes", *bitsPerSampleP);
     } else {
         /* The raster element is an explicit pixel -- color and transparency */
-        computePixelWidth(pnm_type, pnm_meaningful_bits, alpha,
+        computePixelWidth(colorPng, pnmMeaningfulBitCt, alpha,
                           bitsPerSampleP, bitsPerPixelP);
 
         if (verbose)
-            pm_message("Writing %d bits per component per pixel", 
+            pm_message("Writing %u bits per component per pixel", 
                        *bitsPerSampleP);
     }
 }
@@ -2082,41 +2245,28 @@ createPngPalette(pixel              palette_pnm[],
 
 
 static void
-setCompressionSize(png_struct * const png_ptr,
-                   int const    buffer_size) {
-
-#if PNG_LIBPNG_VER >= 10009
-    png_set_compression_buffer_size(png_ptr, buffer_size);
-#else
-    pm_error("Your PNG library cannot set the compression buffer size.  "
-             "You need at least Version 1.0.9 of Libpng; you have Version %s",
-             PNG_LIBPNG_VER_STRING);
-#endif
-}
-
-
-
-static void
-setZlibCompression(png_struct *           const png_ptr,
+setZlibCompression(struct pngx *          const pngxP,
                    struct zlibCompression const zlibCompression) {
 
     if (zlibCompression.levelSpec)
-        png_set_compression_level(png_ptr, zlibCompression.level);
+        png_set_compression_level(pngxP->png_ptr, zlibCompression.level);
 
     if (zlibCompression.memLevelSpec)
-        png_set_compression_mem_level(png_ptr, zlibCompression.mem_level);
+        png_set_compression_mem_level(pngxP->png_ptr,
+                                      zlibCompression.mem_level);
 
     if (zlibCompression.strategySpec)
-        png_set_compression_strategy(png_ptr, zlibCompression.strategy);
+        png_set_compression_strategy(pngxP->png_ptr, zlibCompression.strategy);
 
     if (zlibCompression.windowBitsSpec)
-        png_set_compression_window_bits(png_ptr, zlibCompression.window_bits);
+        png_set_compression_window_bits(pngxP->png_ptr,
+                                        zlibCompression.window_bits);
 
     if (zlibCompression.methodSpec)
-        png_set_compression_method(png_ptr, zlibCompression.method);
+        png_set_compression_method(pngxP->png_ptr, zlibCompression.method);
 
     if (zlibCompression.bufferSizeSpec) {
-        setCompressionSize(png_ptr, zlibCompression.buffer_size);
+        pngx_setCompressionSize(pngxP, zlibCompression.buffer_size);
     }
 }
                   
@@ -2131,7 +2281,7 @@ makePngLine(png_byte *           const line,
             gray *               const alpha_mask,
             colorhash_table      const cht,
             coloralphahash_table const caht,
-            png_info *           const info_ptr,
+            struct pngx *        const pngxP,
             xelval               const png_maxval,
             unsigned int         const depth) {
             
@@ -2143,20 +2293,20 @@ makePngLine(png_byte *           const line,
         xel p_png;
         xel const p = xelrow[col];
         PPM_DEPTH(p_png, p, maxval, png_maxval);
-        if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
-            info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+        if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY ||
+            pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY_ALPHA) {
             if (depth == 16)
                 *pp++ = PNM_GET1(p_png) >> 8;
             *pp++ = PNM_GET1(p_png) & 0xff;
-        } else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+        } else if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_PALETTE) {
             unsigned int paletteIndex;
             if (alpha)
                 paletteIndex = lookupColorAlpha(caht, &p, &alpha_mask[col]);
             else
                 paletteIndex = ppm_lookupcolor(cht, &p);
             *pp++ = paletteIndex;
-        } else if (info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
-                   info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
+        } else if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB ||
+                   pngx_colorType(pngxP) == PNG_COLOR_TYPE_RGB_ALPHA) {
             if (depth == 16)
                 *pp++ = PPM_GETR(p_png) >> 8;
             *pp++ = PPM_GETR(p_png) & 0xff;
@@ -2169,7 +2319,7 @@ makePngLine(png_byte *           const line,
         } else
             pm_error("INTERNAL ERROR: undefined color_type");
                 
-        if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) {
+        if (pngx_colorType(pngxP) & PNG_COLOR_MASK_ALPHA) {
             int const png_alphaval = (int)
                 alpha_mask[col] * (float) png_maxval / maxval + 0.5;
             if (depth == 16)
@@ -2182,8 +2332,7 @@ makePngLine(png_byte *           const line,
 
 
 static void
-writeRaster(png_struct *         const png_ptr,
-            png_info *           const info_ptr,
+writeRaster(struct pngx *        const pngxP,
             FILE *               const ifP,
             pm_filepos           const rasterPos,
             unsigned int         const cols,
@@ -2191,14 +2340,14 @@ writeRaster(png_struct *         const png_ptr,
             xelval               const maxval,
             int                  const format,
             xelval               const png_maxval,
-            unsigned             const int depth,
+            unsigned int         const depth,
             bool                 const alpha,
             gray **              const alpha_mask,
             colorhash_table      const cht,
             coloralphahash_table const caht
             ) {
 /*----------------------------------------------------------------------------
-   Write the PNG raster via compressor *png_ptr, reading the PNM raster
+   Write the PNG raster via compressor *pngxP, reading the PNM raster
    from file *ifP, position 'rasterPos'.
 
    The PNG raster consists of IDAT chunks.
@@ -2216,7 +2365,7 @@ writeRaster(png_struct *         const png_ptr,
     if (line == NULL)
         pm_error("out of memory allocating PNG row buffer");
 
-    for (pass = 0; pass < png_set_interlace_handling(png_ptr); ++pass) {
+    for (pass = 0; pass < pngxP->numPassesRequired; ++pass) {
         unsigned int row;
         pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
         for (row = 0; row < rows; ++row) {
@@ -2226,9 +2375,9 @@ writeRaster(png_struct *         const png_ptr,
             
             makePngLine(line, xelrow, cols, maxval,
                         alpha, alpha ? alpha_mask[row] : NULL,
-                        cht, caht, info_ptr, png_maxval, depth);
+                        cht, caht, pngxP, png_maxval, depth);
 
-            png_write_row(png_ptr, line);
+            pngx_writeRow(pngxP, line);
         }
     }
     pnm_freerow(xelrow);
@@ -2237,78 +2386,243 @@ writeRaster(png_struct *         const png_ptr,
 
 
 static void
-doGamaChunk(struct cmdlineInfo const cmdline,
-            png_info *         const info_ptr) {
+doHistChunk(struct pngx * const pngxP,
+            bool          const histRequested,
+            pixel         const palettePnm[],
+            FILE *        const ifP,
+            pm_filepos    const rasterPos,
+            unsigned int  const cols,
+            unsigned int  const rows,
+            xelval        const maxval,
+            int           const format,
+            bool          const verbose) {
+
+    if (histRequested) {
+        colorhist_vector chv;
+        unsigned int colorCt;
+        colorhash_table cht;
+        
+        getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, 
+               &chv, &colorCt);
+
+        cht = ppm_colorhisttocolorhash(chv, colorCt);
+                
+        { 
+            png_uint_16 * histogram;  /* malloc'ed */
+        
+            MALLOCARRAY(histogram, MAXCOLORS);
+
+            if (!histogram)
+                pm_error("Failed to allocate memory for %u-color histogram",
+                         MAXCOLORS);
+            else {
+                unsigned int i;
+                for (i = 0 ; i < MAXCOLORS; ++i) {
+                    int const chvIndex = ppm_lookupcolor(cht, &palettePnm[i]);
+                    if (chvIndex == -1)
+                        histogram[i] = 0;
+                    else
+                        histogram[i] = chv[chvIndex].value;
+                }
             
-    if (cmdline.gammaSpec) {
-        /* gAMA chunk */
-        info_ptr->valid |= PNG_INFO_gAMA;
-        info_ptr->gamma = cmdline.gamma;
+                pngx_setHist(pngxP, histogram);
+
+                if (verbose)
+                    pm_message("histogram created in PNG stream");
+            }
+        }
+        ppm_freecolorhash(cht);
     }
 }
 
 
 
 static void
+doIhdrChunk(struct pngx * const pngxP,
+            unsigned int  const width,
+            unsigned int  const height,
+            unsigned int  const depth,
+            bool          const colorMapped,
+            bool          const colorPng,
+            bool          const alpha,
+            bool          const interlace) {
+
+    int const interlaceMethod =
+        interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
+
+    int colorType;
+
+    if (colorMapped)
+        colorType = PNG_COLOR_TYPE_PALETTE;
+    else if (colorPng)
+        colorType = PNG_COLOR_TYPE_RGB;
+    else
+        colorType = PNG_COLOR_TYPE_GRAY;
+
+    if (alpha && colorType != PNG_COLOR_TYPE_PALETTE)
+        colorType |= PNG_COLOR_MASK_ALPHA;
+
+    pngx_setIhdr(pngxP, width, height, depth, colorType,
+                 interlaceMethod, 0, 0);
+}
+
+
+
+static void
+doGamaChunk(struct cmdlineInfo const cmdline,
+            struct pngx *      const pngxP) {
+            
+    if (cmdline.gammaSpec)
+        pngx_setGama(pngxP, cmdline.gamma);
+}
+
+
+
+static void
 doChrmChunk(struct cmdlineInfo const cmdline,
-            png_info *         const info_ptr) {
-
-    if (cmdline.rgbSpec) {
-        /* cHRM chunk */
-        info_ptr->valid |= PNG_INFO_cHRM;
-
-        info_ptr->x_white = cmdline.rgb.wx;
-        info_ptr->y_white = cmdline.rgb.wy;
-        info_ptr->x_red   = cmdline.rgb.rx;
-        info_ptr->y_red   = cmdline.rgb.ry;
-        info_ptr->x_green = cmdline.rgb.gx;
-        info_ptr->y_green = cmdline.rgb.gy;
-        info_ptr->x_blue  = cmdline.rgb.bx;
-        info_ptr->y_blue  = cmdline.rgb.by;
-    }
+            struct pngx *      const pngxP) {
+
+    if (cmdline.rgbSpec)
+        pngx_setChrm(pngxP, cmdline.rgb);
 }
 
 
 
 static void
 doPhysChunk(struct cmdlineInfo const cmdline,
-            png_info *         const info_ptr) {
+            struct pngx *      const pngxP) {
+
+    if (cmdline.sizeSpec)
+        pngx_setPhys(pngxP, cmdline.size);
+}
+
+
+
+static void
+doTimeChunk(struct cmdlineInfo const cmdline,
+            struct pngx *      const pngxP) {
+
+    if (cmdline.modtimeSpec)
+        pngx_setTime(pngxP, cmdline.modtime);
+}
+
+
 
-    if (cmdline.sizeSpec) {
-        /* pHYS chunk */
-        info_ptr->valid |= PNG_INFO_pHYs;
+static void
+reportFilterSet(int const filterSet) {
+
+    pm_message("Limiting filter to: %s%s%s%s%s",
+               (filterSet & PNG_FILTER_NONE)  ? "NONE "  : "",
+               (filterSet & PNG_FILTER_SUB)   ? "SUB "   : "",
+               (filterSet & PNG_FILTER_UP)    ? "UP "    : "",
+               (filterSet & PNG_FILTER_AVG)   ? "AVG "   : "",
+               (filterSet & PNG_FILTER_PAETH) ? "PAETH " : "");
+}
+
+
+
+static void
+doFilters(struct cmdlineInfo const cmdline,
+          struct pngx *      const pngxP) {
 
-        info_ptr->x_pixels_per_unit = cmdline.size.x;
-        info_ptr->y_pixels_per_unit = cmdline.size.y;
-        info_ptr->phys_unit_type    = cmdline.size.unit;
+    if (cmdline.filterSetSpec) {
+        if (verbose)
+            reportFilterSet(cmdline.filterSet);
+        pngx_setFilter(pngxP, cmdline.filterSet);
     }
 }
 
 
 
+static void
+reportTrans(struct pngx * const pngxP) {
+
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) {
+        struct pngx_trns const transInfo = pngx_trns(pngxP);
+
+        pm_message("%u transparency values", transInfo.numTrans);
+        
+        pm_message("Transparent color {gray, red, green, blue} = "
+                   "{%d, %d, %d, %d}",
+                   transInfo.transColor.gray,
+                   transInfo.transColor.red,
+                   transInfo.transColor.green,
+                   transInfo.transColor.blue);
+    } else
+        pm_message("No transparent color");
+}
+
 
 static void
-doTimeChunk(struct cmdlineInfo const cmdline,
-            png_info *         const info_ptr) {
+doTrnsChunk(struct pngx * const pngxP,
+            png_byte      const transPalette[],
+            unsigned int  const transPaletteSize,
+            int           const transparent,
+            pixel         const transColor,
+            xelval        const maxval,
+            xelval        const pngMaxval) {
+
+    switch (pngx_colorType(pngxP)) {
+    case PNG_COLOR_TYPE_PALETTE:
+        if (transPaletteSize > 0)
+            pngx_setTrnsPalette(pngxP, transPalette,
+                                transPaletteSize /* omit opaque values */);
+        break;
+    case PNG_COLOR_TYPE_GRAY:
+    case PNG_COLOR_TYPE_RGB:
+        if (transparent > 0)
+            pngx_setTrnsValue(pngxP,
+                              xelToPngColor_16(transColor, maxval, pngMaxval));
+        break;
+    default:
+        /* This is PNG_COLOR_MASK_ALPHA.  Transparency will be handled
+           by the alpha channel, not a transparency color.
+        */
+    {}
+    }
+    if (verbose)
+        reportTrans(pngxP);
+}
 
-    if (cmdline.modtimeSpec) {
-        /* tIME chunk */
-        info_ptr->valid |= PNG_INFO_tIME;
 
-        png_convert_from_time_t(&info_ptr->mod_time, cmdline.modtime);
+
+static void
+doBkgdChunk(struct pngx * const pngxP,
+            bool          const bkgdRequested,
+            unsigned int  const backgroundIndex,
+            pixel         const backColor,
+            xelval        const maxval,
+            xelval        const pngMaxval,
+            bool          const verbose) {
+    
+    if (bkgdRequested) {
+        if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_PALETTE)
+            pngx_setBkgdPalette(pngxP, backgroundIndex);
+        else {
+            png_color_16 const pngBackground = 
+                xelToPngColor_16(backColor, maxval, pngMaxval);
+            pngx_setBkgdRgb(pngxP, pngBackground);
+            if (verbose)
+                pm_message("Writing bKGD chunk with background color "
+                           " {gray, red, green, blue} = {%d, %d, %d, %d}",
+                           pngBackground.gray, 
+                           pngBackground.red, 
+                           pngBackground.green, 
+                           pngBackground.blue ); 
+        }
     }
 }
 
 
 
 static void
-doSbitChunk(png_info * const pngInfoP,
-            xelval     const pngMaxval,
-            xelval     const maxval,
-            bool       const alpha,
-            xelval     const alphaMaxval) {
+doSbitChunk(struct pngx * const pngxP,
+            xelval        const pngMaxval,
+            xelval        const maxval,
+            bool          const alpha,
+            xelval        const alphaMaxval) {
 
-    if (pngInfoP->color_type != PNG_COLOR_TYPE_PALETTE &&
+    if (pngx_colorType(pngxP) != PNG_COLOR_TYPE_PALETTE &&
         (pngMaxval > maxval || (alpha && pngMaxval > alphaMaxval))) {
 
         /* We're writing in a bit depth that doesn't match the maxval
@@ -2327,38 +2641,55 @@ doSbitChunk(png_info * const pngInfoP,
            sBIT chunk.
         */
 
-        pngInfoP->valid |= PNG_INFO_sBIT;
-
         {
             int const sbitval = pm_maxvaltobits(MIN(maxval, pngMaxval));
 
-            if (pngInfoP->color_type & PNG_COLOR_MASK_COLOR) {
-                pngInfoP->sig_bit.red   = sbitval;
-                pngInfoP->sig_bit.green = sbitval;
-                pngInfoP->sig_bit.blue  = sbitval;
+            png_color_8 sbit;
+
+            if (pngx_colorType(pngxP) & PNG_COLOR_MASK_COLOR) {
+                sbit.red   = sbitval;
+                sbit.green = sbitval;
+                sbit.blue  = sbitval;
             } else
-                pngInfoP->sig_bit.gray = sbitval;
+                sbit.gray  = sbitval;
             
             if (verbose)
                 pm_message("Writing sBIT chunk with bits = %d", sbitval);
-        }
-        if (pngInfoP->color_type & PNG_COLOR_MASK_ALPHA) {
-            pngInfoP->sig_bit.alpha =
-                pm_maxvaltobits(MIN(alphaMaxval, pngMaxval));
-            if (verbose)
-                pm_message("  alpha bits = %d", pngInfoP->sig_bit.alpha);
+
+            if (pngx_colorType(pngxP) & PNG_COLOR_MASK_ALPHA) {
+                sbit.alpha = pm_maxvaltobits(MIN(alphaMaxval, pngMaxval));
+                if (verbose)
+                    pm_message("  alpha bits = %d", sbit.alpha);
+            }
+
+            pngx_setSbit(pngxP, sbit);
         }
     }
 }
 
 
 
+static void
+addSrgbChunk(struct pngx *   const pngxP,
+             pngx_srgbIntent const srgbIntent) {
+
+    pngx_setSrgb(pngxP, srgbIntent);
+
+    if (verbose) {
+        pm_message("writing sRGB chunk with intent value %s",
+                   pngx_srgbIntentDesc(srgbIntent));
+    }
+}
+
+
+
 static void 
 convertpnm(struct cmdlineInfo const cmdline,
-           FILE *             const ifp,
-           FILE *             const afp,
-           FILE *             const pfp,
-           FILE *             const tfp,
+           FILE *             const ifP,
+           FILE *             const ofP,
+           FILE *             const afP,
+           FILE *             const pfP,
+           FILE *             const tfP,
            int *              const errorLevelP
     ) {
 /*----------------------------------------------------------------------------
@@ -2367,442 +2698,237 @@ convertpnm(struct cmdlineInfo const cmdline,
    lazy -- it takes a great deal of work to carry all that information as
    separate arguments -- and it's only a very small violation.
 -----------------------------------------------------------------------------*/
-  xel p;
-  int rows, cols, format;
-  xelval maxval;
-      /* The maxval of the input image */
-  xelval png_maxval;
-      /* The maxval of the samples in the PNG output 
-         (must be 1, 3, 7, 15, 255, or 65535)
-      */
-  pixel transcolor;
-      /* The color that is to be transparent, with maxval equal to that
-         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.
-      */
-  int transparent;
-  bool alpha;
-      /* There will be an alpha mask */
-  unsigned int pnm_meaningful_bits;
-  pixel backcolor;
-      /* The background color, with maxval equal to that of the input
-         image.
-      */
-  png_struct *png_ptr;
-  png_info *info_ptr;
-
-  bool colorMapped;
-  pixel palette_pnm[MAXCOLORS];
-  png_color palette[MAXCOLORS];
-      /* The color part of the color/alpha palette passed to the PNG
-         compressor 
-      */
-  unsigned int palette_size;
-
-  gray trans_pnm[MAXCOLORS];
-  png_byte  trans[MAXCOLORS];
-      /* The alpha part of the color/alpha palette passed to the PNG
-         compressor 
-      */
-  unsigned int trans_size;
-
-  colorhash_table cht;
-  coloralphahash_table caht;
-
-  unsigned int background_index;
-      /* Index into palette[] of the background color. */
-
-  png_uint_16 histogram[MAXCOLORS];
-  gray alpha_maxval;
-  int alpha_rows;
-  int alpha_cols;
-  const char * noColormapReason;
-      /* The reason that we shouldn't make a colormapped PNG, or NULL if
-         we should.  malloc'ed null-terminated string.
-      */
-  unsigned int depth;
-      /* The number of bits per sample in the (uncompressed) png 
-         raster -- if the raster contains palette indices, this is the
-         number of bits in the index.
-      */
-  unsigned int fulldepth;
-      /* The total number of bits per pixel in the (uncompressed) png
-         raster, including all channels.
-      */
-  pm_filepos rasterPos;  
-      /* file position in input image file of start of image (i.e. after
-         the header)
-      */
-  xel *xelrow;    /* malloc'ed */
-      /* The row of the input image currently being processed */
-
-  int pnm_type;
-  xelval maxmaxval;
-  gray ** alpha_mask;
-
-  /* these guys are initialized to quiet compiler warnings: */
-  maxmaxval = 255;
-  alpha_mask = NULL;
-  depth = 0;
-  errorlevel = 0;
-
-  png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
-    &pnmtopng_jmpbuf_struct, pnmtopng_error_handler, NULL);
-  if (png_ptr == NULL) {
-    pm_closer (ifp);
-    pm_error ("cannot allocate main libpng structure (png_ptr)");
-  }
-
-  info_ptr = png_create_info_struct (png_ptr);
-  if (info_ptr == NULL) {
-    png_destroy_write_struct (&png_ptr, (png_infopp)NULL);
-    pm_closer (ifp);
-    pm_error ("cannot allocate libpng info structure (info_ptr)");
-  }
-
-  if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) {
-    png_destroy_write_struct (&png_ptr, &info_ptr);
-    pm_closer (ifp);
-    pm_error ("setjmp returns error condition (1)");
-  }
-
-  pnm_readpnminit (ifp, &cols, &rows, &maxval, &format);
-  pm_tell2(ifp, &rasterPos, sizeof(rasterPos));
-  pnm_type = PNM_FORMAT_TYPE (format);
-
-  xelrow = pnm_allocrow(cols);
-
-  if (verbose) {
-    if (pnm_type == PBM_TYPE)    
-      pm_message ("reading a PBM file (maxval=%d)", maxval);
-    else if (pnm_type == PGM_TYPE)    
-      pm_message ("reading a PGM file (maxval=%d)", maxval);
-    else if (pnm_type == PPM_TYPE)    
-      pm_message ("reading a PPM file (maxval=%d)", maxval);
-  }
-
-  if (pnm_type == PGM_TYPE)
-    maxmaxval = PGM_OVERALLMAXVAL;
-  else if (pnm_type == PPM_TYPE)
-    maxmaxval = PPM_OVERALLMAXVAL;
-
-  if (cmdline.transparent) {
-      const char * transstring2;  
-          /* The -transparent value, but with possible leading '=' removed */
-      if (cmdline.transparent[0] == '=') {
-          transexact = 1;
-          transstring2 = &cmdline.transparent[1];
-      } else {
-          transexact = 0;
-          transstring2 = cmdline.transparent;
-      }  
-      /* We do this funny PPM_DEPTH thing instead of just passing 'maxval'
-         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);
-  }
-  if (cmdline.alpha) {
-    pixel alpha_transcolor;
-    bool alpha_can_be_transparency_index;
-    bool all_opaque;
+    int rows, cols, format;
+    xelval maxval;
+    /* The maxval of the input image */
+    xelval pngMaxval;
+        /* The maxval of the samples in the PNG output 
+           (must be 1, 3, 7, 15, 255, or 65535)
+        */
+    pixel transcolor;
+        /* The color that is to be transparent, with maxval equal to that
+           of the input image.
+        */
+    bool transExact;  
+        /* 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 */
+    unsigned int pnmMeaningfulBitCt;
+    pixel backColor;
+        /* The background color, with maxval equal to that of the input
+           image.
+        */
+    jmp_buf jmpbuf;
+    struct pngx * pngxP;
+
+    bool colorMapped;
+    pixel palettePnm[MAXCOLORS];
+    png_color palette[MAXCOLORS];
+        /* The color part of the color/alpha palette passed to the PNG
+           compressor 
+        */
+    unsigned int paletteSize;
+
+    gray transPnm[MAXCOLORS];
+    png_byte  trans[MAXCOLORS];
+        /* The alpha part of the color/alpha palette passed to the PNG
+           compressor 
+        */
+    unsigned int transSize;
+
+    colorhash_table cht;
+    coloralphahash_table caht;
+
+    unsigned int backgroundIndex;
+        /* Index into palette[] of the background color. */
+
+    gray alphaMaxval;
+    const char * noColormapReason;
+        /* The reason that we shouldn't make a colormapped PNG, or NULL if
+           we should.  malloc'ed null-terminated string.
+        */
+    unsigned int depth;
+        /* The number of bits per sample in the (uncompressed) png 
+           raster -- if the raster contains palette indices, this is the
+           number of bits in the index.
+        */
+    unsigned int fulldepth;
+        /* The total number of bits per pixel in the (uncompressed) png
+           raster, including all channels.
+        */
+    pm_filepos rasterPos;  
+        /* file position in input image file of start of image (i.e. after
+           the header)
+        */
+    xel * xelrow;    /* malloc'ed */
+        /* The row of the input image currently being processed */
+
+    gray ** alpha_mask;
+
+    bool colorPng;
+        /* The PNG shall be of the color (RGB) variety */
+
+    /* We initialize these guys to quiet compiler warnings: */
+    depth = 0;
+
+    errorlevel = 0;
+
+    if (setjmp(jmpbuf))
+        pm_error ("setjmp returns error condition");
+
+    pngx_create(&pngxP, PNGX_WRITE, &jmpbuf);
+
+    pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
+    pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
+
+    xelrow = pnm_allocrow(cols);
 
     if (verbose)
-      pm_message ("reading alpha-channel image...");
-    alpha_mask = pgm_readpgm (afp, &alpha_cols, &alpha_rows, &alpha_maxval);
+        reportInputType(format, maxval);
+
+    determineTransparency(cmdline, ifP, rasterPos, cols, rows, maxval, format,
+                          afP,
+                          &alpha, &transparent, &transcolor, &transExact,
+                          &alpha_mask, &alphaMaxval);
 
-    if (alpha_cols != cols || alpha_rows != rows) {
-      png_destroy_write_struct (&png_ptr, &info_ptr);
-      pm_closer (ifp);
-      pm_error ("dimensions for image and alpha mask do not agree");
+    determineBackground(cmdline, maxval, &backColor);
+
+    if (cmdline.force)
+        colorPng = (PNM_FORMAT_TYPE(format) == PPM_TYPE);
+    else {
+        if (PNM_FORMAT_TYPE(format) == PPM_TYPE) {
+            colorPng = hasColor(ifP, cols, rows, maxval, format, rasterPos); 
+        } else
+            colorPng = false;
     }
-    analyzeAlpha(ifp, rasterPos, cols, rows, maxval, format, 
-                 alpha_mask, alpha_maxval, &all_opaque,
-                 &alpha_can_be_transparency_index, &alpha_transcolor);
-
-    if (alpha_can_be_transparency_index && !cmdline.force) {
-      if (verbose)
-        pm_message ("converting alpha mask to transparency index");
-      alpha = FALSE;
-      transparent = 2;
-      transcolor = alpha_transcolor;
-    } else if (all_opaque) {
-        alpha = FALSE;
-        transparent = -1;
-    } else {
-      alpha = TRUE;
-      transparent = -1;
+
+
+    /* handle `odd' maxvalues */
+
+    if (maxval > 65535 && !cmdline.downscale) {
+        pm_error("can only handle files up to 16-bit "
+                 "(use -downscale to override");
     }
-  } else {
-      /* Though there's no alpha_mask, we still need an alpha_maxval for
-         use with trans[], which can have stuff in it if the user specified
-         a transparent color.
-      */
-      alpha = FALSE;
-      alpha_maxval = 255;
-      transparent = cmdline.transparent ? 1 : -1;
-  }
-  if (cmdline.background) 
-      PPM_DEPTH(backcolor, ppm_parsecolor(cmdline.background, maxmaxval), 
-                maxmaxval, maxval);;
-
-  /* first of all, check if we have a grayscale image written as PPM */
-
-  if (pnm_type == PPM_TYPE && !cmdline.force) {
-      unsigned int row;
-      bool isgray;
-
-      isgray = TRUE;  /* initial assumption */
-      pm_seek2(ifp, &rasterPos, sizeof(rasterPos));
-      for (row = 0; row < rows && isgray; ++row) {
-          unsigned int col;
-          pnm_readpnmrow(ifp, xelrow, cols, maxval, format);
-          for (col = 0; col < cols && isgray; ++col) {
-              p = xelrow[col];
-              if (PPM_GETR(p) != PPM_GETG(p) || PPM_GETG(p) != PPM_GETB(p))
-                  isgray = FALSE;
-          }
-      }
-      if (isgray)
-          pnm_type = PGM_TYPE;
-  }
-
-  /* handle `odd' maxvalues */
-
-  if (maxval > 65535 && !cmdline.downscale) {
-      png_destroy_write_struct(&png_ptr, &info_ptr);
-      pm_closer(ifp);
-      pm_error("can only handle files up to 16-bit "
-               "(use -downscale to override");
-  }
-
-  findRedundantBits(ifp, rasterPos, cols, rows, maxval, format, alpha,
-                    cmdline.force, &pnm_meaningful_bits);
+
+    findRedundantBits(ifP, rasterPos, cols, rows, maxval, format, alpha,
+                      cmdline.force, &pnmMeaningfulBitCt);
   
-  computeColorMap(ifp, rasterPos, cols, rows, maxval, pnm_type, format,
-                  cmdline.force, pfp,
-                  alpha, transparent >= 0, transcolor, transexact, 
-                  !!cmdline.background, backcolor,
-                  alpha_mask, alpha_maxval, pnm_meaningful_bits,
-                  palette_pnm, &palette_size, trans_pnm, &trans_size,
-                  &background_index, &noColormapReason);
-
-  if (noColormapReason) {
-      if (pfp)
-          pm_error("You specified a particular palette, but this image "
-                   "cannot be represented by any palette.  %s",
-                   noColormapReason);
-      if (verbose)
-          pm_message("Not using color map.  %s", noColormapReason);
-      strfree(noColormapReason);
-      colorMapped = FALSE;
-  } else
-      colorMapped = TRUE;
+    computeColorMap(ifP, rasterPos, cols, rows, maxval, colorPng, format,
+                    cmdline.force, pfP,
+                    alpha, transparent >= 0, transcolor, transExact, 
+                    !!cmdline.background, backColor,
+                    alpha_mask, alphaMaxval, pnmMeaningfulBitCt,
+                    palettePnm, &paletteSize, transPnm, &transSize,
+                    &backgroundIndex, &noColormapReason);
+
+    if (noColormapReason) {
+        if (pfP)
+            pm_error("You specified a particular palette, but this image "
+                     "cannot be represented by any palette.  %s",
+                     noColormapReason);
+        if (verbose)
+            pm_message("Not using color map.  %s", noColormapReason);
+        pm_strfree(noColormapReason);
+        colorMapped = FALSE;
+    } else
+        colorMapped = TRUE;
   
-  computeColorMapLookupTable(colorMapped, palette_pnm, palette_size,
-                             trans_pnm, trans_size, alpha, alpha_maxval,
-                             &cht, &caht);
-
-  computeRasterWidth(colorMapped, palette_size, pnm_type, 
-                     pnm_meaningful_bits, alpha,
-                     &depth, &fulldepth);
-  if (verbose)
-    pm_message ("writing a%s %d-bit %s%s file%s",
-                fulldepth == 8 ? "n" : "", fulldepth,
-                colorMapped ? "palette": 
-                (pnm_type == PPM_TYPE ? "RGB" : "gray"),
-                alpha ? (colorMapped ? "+transparency" : "+alpha") : "",
-                cmdline.interlace ? " (interlaced)" : "");
-
-  /* now write the file */
-
-  png_maxval = pm_bitstomaxval(depth);
-
-  if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) {
-    png_destroy_write_struct (&png_ptr, &info_ptr);
-    pm_closer (ifp);
-    pm_error ("setjmp returns error condition (2)");
-  }
-
-  png_init_io (png_ptr, stdout);
-  info_ptr->width = cols;
-  info_ptr->height = rows;
-  info_ptr->bit_depth = depth;
-
-  if (colorMapped)
-    info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
-  else if (pnm_type == PPM_TYPE)
-    info_ptr->color_type = PNG_COLOR_TYPE_RGB;
-  else
-    info_ptr->color_type = PNG_COLOR_TYPE_GRAY;
-
-  if (alpha && info_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
-    info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
-
-  info_ptr->interlace_type = cmdline.interlace;
-
-  doGamaChunk(cmdline, info_ptr);
-
-  doChrmChunk(cmdline, info_ptr);
-
-  doPhysChunk(cmdline, info_ptr);
-
-  if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
-
-    /* creating PNG palette  (PLTE and tRNS chunks) */
-
-    createPngPalette(palette_pnm, palette_size, maxval,
-                     trans_pnm, trans_size, alpha_maxval, 
-                     palette, trans);
-    info_ptr->valid |= PNG_INFO_PLTE;
-    info_ptr->palette = palette;
-    info_ptr->num_palette = palette_size;
-    if (trans_size > 0) {
-        info_ptr->valid |= PNG_INFO_tRNS;
-        info_ptr->TRANS_ALPHA = trans;
-        info_ptr->num_trans = trans_size;   /* omit opaque values */
-    }
-    /* creating hIST chunk */
-    if (cmdline.hist) {
-        colorhist_vector chv;
-        unsigned int colors;
-        colorhash_table cht;
-        
-        getChv(ifp, rasterPos, cols, rows, maxval, format, MAXCOLORS, 
-               &chv, &colors);
+    computeColorMapLookupTable(colorMapped, palettePnm, paletteSize,
+                               transPnm, transSize, alpha, alphaMaxval,
+                               &cht, &caht);
 
-        cht = ppm_colorhisttocolorhash (chv, colors);
-                
-        { 
-            unsigned int i;
-            for (i = 0 ; i < MAXCOLORS; ++i) {
-                int const chvIndex = ppm_lookupcolor(cht, &palette_pnm[i]);
-                if (chvIndex == -1)
-                    histogram[i] = 0;
-                else
-                    histogram[i] = chv[chvIndex].value;
-            }
-        }
+    computeRasterWidth(colorMapped, paletteSize, colorPng,
+                       pnmMeaningfulBitCt, alpha,
+                       &depth, &fulldepth);
+    if (verbose)
+        pm_message ("writing a%s %d-bit %s%s file%s",
+                    fulldepth == 8 ? "n" : "", fulldepth,
+                    colorMapped ? "palette": 
+                    colorPng ? "RGB" : "gray",
+                    alpha ? (colorMapped ? "+transparency" : "+alpha") : "",
+                    cmdline.interlace ? " (interlaced)" : "");
 
-        ppm_freecolorhash(cht);
+    /* now write the file */
 
-        info_ptr->valid |= PNG_INFO_hIST;
-        info_ptr->hist = histogram;
-        if (verbose)
-            pm_message("histogram created");
-    }
-  } else { /* color_type != PNG_COLOR_TYPE_PALETTE */
-    if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
-        info_ptr->color_type == PNG_COLOR_TYPE_RGB) {
-        if (transparent > 0) {
-            info_ptr->valid |= PNG_INFO_tRNS;
-            info_ptr->trans_values = 
-                xelToPngColor_16(transcolor, maxval, png_maxval);
-        }
-    } else {
-        /* This is PNG_COLOR_MASK_ALPHA.  Transparency will be handled
-           by the alpha channel, not a transparency color.
-        */
+    pngMaxval = pm_bitstomaxval(depth);
+
+    if (setjmp (pnmtopng_jmpbuf_struct.jmpbuf)) {
+        pm_error ("setjmp returns error condition (2)");
     }
-    if (verbose) {
-        if (info_ptr->valid && PNG_INFO_tRNS) 
-            pm_message("Transparent color {gray, red, green, blue} = "
-                       "{%d, %d, %d, %d}",
-                       info_ptr->trans_values.gray,
-                       info_ptr->trans_values.red,
-                       info_ptr->trans_values.green,
-                       info_ptr->trans_values.blue);
-        else
-            pm_message("No transparent color");
+
+    doIhdrChunk(pngxP, cols, rows, depth, colorMapped, colorPng, alpha,
+                cmdline.interlace);
+
+    doGamaChunk(cmdline, pngxP);
+
+    doChrmChunk(cmdline, pngxP);
+
+    doPhysChunk(cmdline, pngxP);
+
+    if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_PALETTE) {
+
+        /* creating PNG palette (Not counting the transparency palette) */
+
+        createPngPalette(palettePnm, paletteSize, maxval,
+                         transPnm, transSize, alphaMaxval, 
+                         palette, trans);
+        pngx_setPlte(pngxP, palette, paletteSize);
+
+        doHistChunk(pngxP, cmdline.hist, palettePnm, ifP, rasterPos,
+                    cols, rows, maxval, format, cmdline.verbose);
     }
-  }
-
-  /* bKGD chunk */
-  if (cmdline.background) {
-      info_ptr->valid |= PNG_INFO_bKGD;
-      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
-          info_ptr->background.index = background_index;
-      } else {
-          info_ptr->background = 
-              xelToPngColor_16(backcolor, maxval, png_maxval);
-          if (verbose)
-              pm_message("Writing bKGD chunk with background color "
-                         " {gray, red, green, blue} = {%d, %d, %d, %d}",
-                         info_ptr->background.gray, 
-                         info_ptr->background.red, 
-                         info_ptr->background.green, 
-                         info_ptr->background.blue ); 
-      }
-  }
-
-  doSbitChunk(info_ptr, png_maxval, maxval, alpha, alpha_maxval);
-
-  /* tEXT and zTXT chunks */
-  if (cmdline.text || cmdline.ztxt)
-      pnmpng_read_text(info_ptr, tfp, !!cmdline.ztxt, cmdline.verbose);
-
-  doTimeChunk(cmdline, info_ptr);
-
-  if (cmdline.filterSetSpec)
-      png_set_filter(png_ptr, 0, cmdline.filterSet);
-
-  if (cmdline.filterSet != 0)
-      png_set_filter(png_ptr, 0, cmdline.filterSet);
-
-  setZlibCompression(png_ptr, cmdline.zlibCompression);
-
-  /* write the png-info struct */
-  png_write_info(png_ptr, info_ptr);
-
-  if (cmdline.text || cmdline.ztxt)
-      /* prevent from being written twice with png_write_end */
-      info_ptr->num_text = 0;
-
-  if (cmdline.modtime)
-      /* prevent from being written twice with png_write_end */
-      info_ptr->valid &= ~PNG_INFO_tIME;
-
-  /* let libpng take care of, e.g., bit-depth conversions */
-  png_set_packing (png_ptr);
-
-  writeRaster(png_ptr, info_ptr, ifp, rasterPos, cols, rows, maxval, format,
-              png_maxval, depth, alpha, alpha_mask, cht, caht);
-
-  png_write_end (png_ptr, info_ptr);
-
-
-#if 0
-  /* The following code may be intended to solve some segfault problem
-     that arises with png_destroy_write_struct().  The latter is the
-     method recommended in the libpng documentation and this program 
-     will not compile under Cygwin because the Windows DLL for libpng
-     does not contain png_write_destroy() at all.  Since the author's
-     comment below does not make it clear what the segfault issue is,
-     we cannot consider it.  -Bryan 00.09.15
-*/
 
-  png_write_destroy (png_ptr);
-  /* flush first because free(png_ptr) can segfault due to jmpbuf problems
-     in png_write_destroy */
-  fflush (stdout);
-  free (png_ptr);
-  free (info_ptr);
-#else
-  png_destroy_write_struct(&png_ptr, &info_ptr);
-#endif
+    doTrnsChunk(pngxP, trans, transSize,
+                transparent, transcolor, maxval, pngMaxval);
 
-  pnm_freerow(xelrow);
+    doBkgdChunk(pngxP, !!cmdline.background,
+                backgroundIndex, backColor,
+                maxval, pngMaxval, cmdline.verbose);
 
-  if (cht)
-      ppm_freecolorhash(cht);
-  if (caht)
-      freecoloralphahash(caht);
+    doSbitChunk(pngxP, pngMaxval, maxval, alpha, alphaMaxval);
+
+    if (cmdline.srgbintentSpec)
+        addSrgbChunk(pngxP, cmdline.srgbintent);
+
+    /* tEXT and zTXT chunks */
+    if (cmdline.text || cmdline.ztxt)
+        pngtxt_addChunk(pngxP, tfP, !!cmdline.ztxt, false, cmdline.verbose);
+
+    doTimeChunk(cmdline, pngxP);
+
+    doFilters(cmdline, pngxP);
+
+    setZlibCompression(pngxP, cmdline.zlibCompression);
+
+    png_init_io(pngxP->png_ptr, ofP);
+
+    /* write the png-info struct */
+    pngx_writeInfo(pngxP);
+
+    /* let libpng take care of, e.g., bit-depth conversions */
+    pngx_setPacking(pngxP);
+
+    pngx_setInterlaceHandling(pngxP);
+
+    writeRaster(pngxP, ifP, rasterPos,
+                cols, rows, maxval, format,
+                pngMaxval, depth, alpha, alpha_mask, cht, caht);
+
+    pngx_writeEnd(pngxP);
+
+    pngx_destroy(pngxP);
+
+    pnm_freerow(xelrow);
+
+    if (cht)
+        ppm_freecolorhash(cht);
+    if (caht)
+        freecoloralphahash(caht);
 
-  *errorLevelP = errorlevel;
+    *errorLevelP = errorlevel;
 }
 
 
@@ -2812,20 +2938,33 @@ displayVersion() {
 
     fprintf(stderr,"Pnmtopng version %s.\n", NETPBM_VERSION);
 
-    /* We'd like to display the version of libpng with which we're
-       linked, as we do for zlib, but it isn't practical.
-       While libpng is capable of telling you what it's level
-       is, different versions of it do it two different ways: with
-       png_libpng_ver or with png_get_header_ver.  So we have to be
-       compiled for a particular version just to find out what
-       version it is! It's not worth having a link failure, much
-       less a compile failure, if we choose wrong.
-       png_get_header_ver is not in anything older than libpng 1.0.2a
-       (Dec 1998).  png_libpng_ver is not there in libraries built
-       without USE_GLOBAL_ARRAYS.  Cygwin versions are normally built
-       without USE_GLOBAL_ARRAYS.  -bjh 2002.06.17.
+    /* We'd like to display the version of libpng with which we're _linked_ as
+       well as the one with which we're compiled, but it isn't practical.
+       While libpng is capable of telling you what it's level is, different
+       versions of it do it two different ways: with png_libpng_ver or with
+       png_get_header_ver.  So we have to be compiled for a particular version
+       just to find out what version it is! It's not worth having a link
+       failure, much less a compile failure, if we choose wrong.
+       png_get_header_ver is not in anything older than libpng 1.0.2a (Dec
+       1998).  png_libpng_ver is not there in libraries built without
+       USE_GLOBAL_ARRAYS.  Cygwin versions are normally built without
+       USE_GLOBAL_ARRAYS.  -bjh 2002.06.17.
+
+       We'd also like to display the version of libz with which we're linked,
+       with zlib_version (which nowadays is a macro for zlibVersion), but we
+       can't for reasons of modularity: We don't really link libz.  libpng
+       does.  It's none of our business whether libz is even present.  And at
+       least on Mac OS X, we can't access libz's symbols from here -- we get
+       undefined reference to zlibVersion.  We would have to explicitly link
+       libz just to find out its version.  The right way to do this is for a
+       subroutine in libpng to give us the information.  Until 10.07.08, we
+       did display zlib_version, but for years Mac OS X build was failing (and
+       we erroneously thought it was a libpng-config --ldflags bug).
+
+       We _do_ use the compile-time part of libpng (<png.h>), because it's
+       part of the interface to libpng.
     */
-    fprintf(stderr, "   Compiled with libpng %s.\n",
+    fprintf(stderr, "   Pnmtopng Compiled with libpng %s.\n",
             PNG_LIBPNG_VER_STRING);
     fprintf(stderr, "   Pnmtopng (not libpng) compiled with zlib %s.\n",
             ZLIB_VERSION);
@@ -2835,7 +2974,7 @@ displayVersion() {
 
 
 int 
-main(int argc, char *argv[]) {
+main(int argc, const char * argv[]) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
@@ -2845,7 +2984,7 @@ main(int argc, char *argv[]) {
 
     int errorlevel;
     
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
     
     parseCommandLine(argc, argv, &cmdline);
     
@@ -2855,7 +2994,7 @@ main(int argc, char *argv[]) {
     }
     verbose = cmdline.verbose;
     
-    ifP = pm_openr_seekable(cmdline.inputFilename);
+    ifP = pm_openr_seekable(cmdline.inputFileName);
     
     if (cmdline.alpha)
         afP = pm_openr(cmdline.alpha);
@@ -2874,7 +3013,7 @@ main(int argc, char *argv[]) {
     else
         tfP = NULL;
 
-    convertpnm(cmdline, ifP, afP, pfP, tfP, &errorlevel);
+    convertpnm(cmdline, ifP, stdout, afP, pfP, tfP, &errorlevel);
     
     if (afP)
         pm_close(afP);
diff --git a/converter/other/pnmtops.c b/converter/other/pnmtops.c
index 20395952..c1dadc3e 100644
--- a/converter/other/pnmtops.c
+++ b/converter/other/pnmtops.c
@@ -7,15 +7,19 @@
       1) Use built in Postscript filters /ASCII85Decode, /ASCIIHexDecode,
          /RunLengthDecode, and /FlateDecode;
 
-         We use methods we learned from Dirk Krause's program Bmeps and
-         raster encoding code copied almost directly from Bmeps.
+         We use methods we learned from Dirk Krause's program Bmeps.
+         Previous versions used raster encoding code based on Bmeps
+         code.  This program does not used any code from Bmeps.
 
       2) Use our own filters and redefine /readstring .  This is aboriginal
-         Netpbm code, from when Postscript was young.
+         Netpbm code, from when Postscript was young.  The filters are
+         nearly identical to /ASCIIHexDecode and /RunLengthDecode.  We
+         use the same raster encoding code with slight modifications.
 
-   (2) is the default, because it's been working for ages and we have
-   more confidence in it.  But (1) gives more options.  The user
-   selects (1) with the -psfilter option.
+   (2) is the default.  (1) gives more options, but relies on features
+   introduced in Postscript Level 2, which appeared in 1991.  Postcript
+   devices made before 1991 can't handle them.  The user selects (1)
+   with the -psfilter option.
 
    We also do a few other bold new things only when the user specifies
    -psfilter, because we're not sure they work for everyone.
@@ -31,23 +35,63 @@
 
 #define _BSD_SOURCE  /* Make sure string.h contains strdup() */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
-
-#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
 #include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#ifndef NOFLATE
+#include <zlib.h>
+#endif
 
 #include "pm_c_util.h"
 #include "pam.h"
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "nstring.h"
-#include "bmepsoe.h"
+#include "runlength.h"
+
+
+
+static void
+setSignals() {
+/*----------------------------------------------------------------------------
+   Set up the process-global signal-related state.
+
+   Note that we can't rely on defaults, because much of this is inherited
+   from the process that forked and exec'ed this program.
+-----------------------------------------------------------------------------*/
+    /* See waitForChildren() for why we do this to SIGCHLD */
+
+    struct sigaction sigchldAction;
+    int rc;
+    sigset_t emptySet;
+
+    sigemptyset(&emptySet);
+
+    sigchldAction.sa_handler = SIG_DFL;
+    sigchldAction.sa_mask = emptySet;
+    sigchldAction.sa_flags = SA_NOCLDSTOP;
+
+    rc = sigaction(SIGCHLD, &sigchldAction, NULL);
+
+    if (rc != 0)
+        pm_error("sigaction() to set up signal environment failed, "
+                 "errno = %d (%s)", errno, strerror(errno));
+}
+
+
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
     const char * inputFileName;  /* Filespecs of input file */
-    float scale;
+    float        scale;
     unsigned int dpiX;     /* horiz component of DPI option */
     unsigned int dpiY;     /* vert component of DPI option */
     unsigned int width;              /* in 1/72 inch */
@@ -59,6 +103,8 @@ struct cmdlineInfo {
     unsigned int imagewidth;         /* in 1/72 inch; zero if unspec */
     unsigned int imageheight;        /* in 1/72 inch; zero if unspec */
     unsigned int equalpixels;
+    unsigned int bitspersampleSpec;
+    unsigned int bitspersample;
     unsigned int setpage;
     bool         showpage;
     unsigned int level;
@@ -69,24 +115,29 @@ struct cmdlineInfo {
     unsigned int dict;
     unsigned int vmreclaim;
     unsigned int verbose;
+    unsigned int debug;
 };
 
-
+static bool debug;
 static bool verbose;
 
 
+
 static void
 parseDpi(const char *   const dpiOpt, 
          unsigned int * const dpiXP, 
          unsigned int * const dpiYP) {
 
     char *dpistr2;
-    unsigned int dpiX, dpiY;
+    unsigned long int dpiX, dpiY;
 
     dpiX = strtol(dpiOpt, &dpistr2, 10);
-    if (dpistr2 == dpiOpt) 
+    if (dpistr2 == dpiOpt)
         pm_error("Invalid value for -dpi: '%s'.  Must be either number "
                  "or NxN ", dpiOpt);
+    else if (dpiX > INT_MAX)
+        pm_error("Invalid value for -dpi: '%s'.  "
+                 "Value too large for computation", dpiOpt);
     else {
         if (*dpistr2 == '\0') {
             *dpiXP = dpiX;
@@ -95,8 +146,11 @@ parseDpi(const char *   const dpiOpt,
             char * dpistr3;
 
             dpistr2++;  /* Move past 'x' */
-            dpiY = strtol(dpistr2, &dpistr3, 10);        
-            if (dpistr3 != dpistr2 && *dpistr3 == '\0') {
+            dpiY = strtol(dpistr2, &dpistr3, 10);
+            if (dpiY > INT_MAX)
+                pm_error("Invalid value for -dpi: '%s'.  "
+                         "Value too large for computation", dpiOpt);
+            else if (dpistr3 != dpistr2 && *dpistr3 == '\0') {
                 *dpiXP = dpiX;
                 *dpiYP = dpiY;
             } else {
@@ -110,7 +164,50 @@ parseDpi(const char *   const dpiOpt,
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+validateBps_1_2_4_8_12(unsigned int const bitsPerSample) {
+
+    switch (bitsPerSample) {
+    case 1:
+    case 2:
+    case 4:
+    case 8:
+    case 12:
+        break;
+    default:
+        pm_error("Invalid -bitspersample value: %u.  Must be "
+                 "1, 2, 4, 8, or 12", bitsPerSample);
+    }
+}
+
+
+
+static void
+validateCompDimension(unsigned int const value,
+                      unsigned int const scaleFactor,
+                      const char * const vname) {
+/*----------------------------------------------------------------------------
+  Validate that the image dimension (width or height) 'value' isn't so big
+  that in this program's calculations, involving scale factor 'scaleFactor',
+  it would cause a register overflow.  If it is, abort the program and refer
+  to the offending dimension as 'vname' in the error message.
+
+  Note that this early validation approach (calling this function) means
+  the actual computations don't have to be complicated with arithmetic
+  overflow checks, so they're easier to read.
+-----------------------------------------------------------------------------*/
+    if (value > 0) {
+        unsigned int const maxWidthHeight = INT_MAX - 2;
+        unsigned int const maxScaleFactor = maxWidthHeight / value;
+
+        if (scaleFactor > maxScaleFactor)
+            pm_error("%s is too large for compuations: %u", vname, value);
+    }
+}
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 
     unsigned int imagewidthSpec, imageheightSpec;
@@ -120,19 +217,19 @@ parseCommandLine(int argc, char ** argv,
     float width, height;
     unsigned int noturn;
     unsigned int showpage, noshowpage;
-    const char *dpiOpt;
-    unsigned int dpiSpec;
+    const char * dpiOpt;
+    unsigned int dpiSpec, scaleSpec, widthSpec, heightSpec;
 
     optStruct3 opt;
     unsigned int option_def_index = 0;
-    optEntry *option_def;
+    optEntry * option_def;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
-    OPTENT3(0, "scale",       OPT_FLOAT, &cmdlineP->scale, NULL,         0);
+    OPTENT3(0, "scale",       OPT_FLOAT, &cmdlineP->scale, &scaleSpec,   0);
     OPTENT3(0, "dpi",         OPT_STRING, &dpiOpt,         &dpiSpec,     0);
-    OPTENT3(0, "width",       OPT_FLOAT, &width,           NULL,         0);
-    OPTENT3(0, "height",      OPT_FLOAT, &height,          NULL,         0);
+    OPTENT3(0, "width",       OPT_FLOAT, &width,           &widthSpec,   0);
+    OPTENT3(0, "height",      OPT_FLOAT, &height,          &heightSpec,  0);
     OPTENT3(0, "psfilter",    OPT_FLAG,  NULL, &cmdlineP->psfilter,      0);
     OPTENT3(0, "turn",        OPT_FLAG,  NULL, &cmdlineP->mustturn,      0);
     OPTENT3(0, "noturn",      OPT_FLAG,  NULL, &noturn,                  0);
@@ -144,6 +241,8 @@ parseCommandLine(int argc, char ** argv,
     OPTENT3(0, "equalpixels", OPT_FLAG,  NULL, &cmdlineP->equalpixels,   0);
     OPTENT3(0, "imagewidth",  OPT_FLOAT, &imagewidth,  &imagewidthSpec,  0);
     OPTENT3(0, "imageheight", OPT_FLOAT, &imageheight, &imageheightSpec, 0);
+    OPTENT3(0, "bitspersample", OPT_UINT, &cmdlineP->bitspersample,
+            &cmdlineP->bitspersampleSpec, 0);
     OPTENT3(0, "nosetpage",   OPT_FLAG,  NULL, &nosetpage,               0);
     OPTENT3(0, "setpage",     OPT_FLAG,  NULL, &cmdlineP->setpage,       0);
     OPTENT3(0, "noshowpage",  OPT_FLAG,  NULL, &noshowpage,              0);
@@ -152,19 +251,15 @@ parseCommandLine(int argc, char ** argv,
     OPTENT3(0, "vmreclaim",   OPT_FLAG,  NULL, &cmdlineP->vmreclaim,     0);
     OPTENT3(0, "showpage",    OPT_FLAG,  NULL, &showpage,                0);
     OPTENT3(0, "verbose",     OPT_FLAG,  NULL, &cmdlineP->verbose,       0);
+    OPTENT3(0, "debug",       OPT_FLAG,  NULL, &cmdlineP->debug,         0);
     OPTENT3(0, "level",       OPT_UINT, &cmdlineP->level, 
             &cmdlineP->levelSpec,              0);
     
-    /* DEFAULTS */
-    cmdlineP->scale = 1.0;
-    width = 8.5;
-    height = 11.0;
-
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;
     opt.allowNegNum = FALSE;
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
 
     if (cmdlineP->mustturn && noturn)
         pm_error("You cannot specify both -turn and -noturn");
@@ -175,6 +270,15 @@ parseCommandLine(int argc, char ** argv,
     if (cmdlineP->setpage && nosetpage)
         pm_error("You cannot specify both -setpage and -nosetpage");
 
+    if (!scaleSpec)
+        cmdlineP->scale = 1.0;
+
+    if (!widthSpec)
+        width = 8.5;
+
+    if (!heightSpec)
+        height = 11.0;
+
     if (dpiSpec)
         parseDpi(dpiOpt, &cmdlineP->dpiX, &cmdlineP->dpiY);
     else {
@@ -185,16 +289,23 @@ parseCommandLine(int argc, char ** argv,
     cmdlineP->center  =  !nocenter;
     cmdlineP->canturn =  !noturn;
     cmdlineP->showpage = !noshowpage;
+
+    validateCompDimension(width, 72, "-width value");
+    validateCompDimension(height, 72, "-height value");
     
     cmdlineP->width  = width * 72;
     cmdlineP->height = height * 72;
 
-    if (imagewidthSpec)
+    if (imagewidthSpec) {
+        validateCompDimension(imagewidth, 72, "-imagewidth value");
         cmdlineP->imagewidth = imagewidth * 72;
+    }
     else
         cmdlineP->imagewidth = 0;
-    if (imageheightSpec)
+    if (imageheightSpec) {
+        validateCompDimension(imagewidth, 72, "-imageheight value");
         cmdlineP->imageheight = imageheight * 72;
+    }
     else
         cmdlineP->imageheight = 0;
 
@@ -203,6 +314,9 @@ parseCommandLine(int argc, char ** argv,
         pm_error("You must specify -psfilter in order to specify "
                  "-flate or -ascii85");
 
+    if (cmdlineP->bitspersampleSpec)
+        validateBps_1_2_4_8_12(cmdlineP->bitspersample);
+
     if (argc-1 == 0) 
         cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
@@ -211,320 +325,715 @@ parseCommandLine(int argc, char ** argv,
     else
         cmdlineP->inputFileName = argv[1];
 
+    free(option_def); 
 }
 
 
-/*===========================================================================
-  The native output encoder.  This is archaic and uses global variables.
-  It is probably obsoleted by the bmeps output encoder; we just haven't
-  had a chance to verify that yet.
-===========================================================================*/
 
+static bool
+progIsFlateCapable(void) {
+
+    return
+#ifdef NOFLATE
+        false
+#else
+        true
+#endif
+        ;
+}
+
+
+
+static const char *
+basebasename(const char * const filespec) {
 /*----------------------------------------------------------------------------
-   The following global variables are the native output encoder state.
+  Return filename up to first period
 -----------------------------------------------------------------------------*/
-static unsigned int itemsinline;
-    /* The number of items in the line we are currently building */
-static unsigned int bitsinitem;
-    /* The number of bits filled so far in the item we are currently
-       building 
-    */
-static unsigned int rlebitsinitem;
-    /* The number of bits filled so far in the item we are currently
-       building 
-    */
-static unsigned int bitspersample;
-static unsigned int item, bitshift, items;
-static unsigned int rleitem, rlebitshift;
-static unsigned int repeat, itembuf[128], count, repeatitem, repeatcount;
+    char const dirsep = '/';
+    const char * const lastSlashPos = strrchr(filespec, dirsep);
+
+    char * name;
+    const char * filename;
+
+    if (lastSlashPos)
+        filename = lastSlashPos + 1;
+    else
+        filename = filespec;
+
+    name = strdup(filename);
+    if (name != NULL) {
+        char * const dotPosition = strchr(name, '.');
+
+        if (dotPosition)
+            *dotPosition = '\0';
+    }
+    return name;
+}
 
 
 
 static void
-initNativeOutputEncoder(bool const rle, unsigned int const bitspersample) {
-/*----------------------------------------------------------------------------
-   Initialize the native output encoder.  Call this once per
-   Postscript image that you will write with putitem(), before for the
-   first putitem().
+writeFile(const unsigned char * const buffer,
+          size_t                const writeCt,
+          const char *          const name,
+          FILE *                const ofP) {
 
-   We initialize the item putter state variables, which are the
-   global variable defined above.
------------------------------------------------------------------------------*/
-    itemsinline = 0;
-    items = 0;
+    size_t writtenCt;
+
+    writtenCt = fwrite(buffer, 1, writeCt, ofP);
+
+    if (writtenCt != writeCt)
+        pm_error("Error writing to %s output file", name);
+}
+
+
+
+static void
+writeFileChar(const char * const buffer,
+              size_t       const writeCt,
+              const char * const name,
+              FILE *       const ofP) {
+
+    writeFile((const unsigned char *)buffer, writeCt, name, ofP);
+}
 
-    if (rle) {
-        rleitem = 0;
-        rlebitsinitem = 0;
-        rlebitshift = 8 - bitspersample;
-        repeat = 1;
-        count = 0;
-    } else {
-        item = 0;
-        bitsinitem = 0;
-        bitshift = 8 - bitspersample;
-    }
 
+
+#define MAX_FILTER_CT 10
+    /* The maximum number of filters this code is capable of applying */
+
+
+
+static void
+initPidList(pid_t * const pidList) {
+
+    pidList[0] = (pid_t)0;  /* end of list marker */
 }
 
 
 
 static void
-putitem(void) {
-    const char* const hexits = "0123456789abcdef";
+addToPidList(pid_t * const pidList,
+             pid_t   const newPid) {
+
+    unsigned int i;
+
+    for (i = 0; i < MAX_FILTER_CT && pidList[i]; ++i);
 
-    if (itemsinline == 30) {
-        putchar('\n');
-        itemsinline = 0;
+    assert(i < MAX_FILTER_CT);
+
+    pidList[i] = newPid;
+    pidList[i+1] = (pid_t)0;  /* end of list marker */
+}
+
+
+
+/*===========================================================================
+  The output encoder
+  ===========================================================================*/
+    
+enum OutputType {AsciiHex, Ascii85};
+
+typedef struct {
+    enum OutputType    outputType;
+    bool               compressRle;
+    bool               compressFlate;
+    unsigned int       runlengthRefresh;
+} OutputEncoder;
+
+
+
+static unsigned int
+bytesPerRow (unsigned int const cols,
+             unsigned int const bitsPerSample) {
+/*----------------------------------------------------------------------------
+  Size of row buffer, padded up to byte boundary, given that the image
+  has 'cols' samples per row, 'bitsPerSample' bits per sample.
+-----------------------------------------------------------------------------*/
+    unsigned int retval;
+
+    assert(bitsPerSample==1 || bitsPerSample==2 || bitsPerSample==4 || 
+           bitsPerSample==8 || bitsPerSample==12);
+
+    switch (bitsPerSample) {
+    case 1:
+    case 2:
+    case 4:
+        retval = cols / (8/bitsPerSample)
+            + (cols % (8/bitsPerSample) > 0 ? 1 : 0);
+        /* A more straightforward calculation would be
+           (cols * bitsPerSample + 7) / 8 ,
+           but this overflows when icols is large.
+        */
+        break;
+    case 8:
+        retval = cols;
+        break;
+    case 12:
+        retval = cols + (cols+1)/2;
+        break;
     }
-    assert(item >> 8 == 0);
-    putchar(hexits[item >> 4]);
-    putchar(hexits[item & 15]);
-    ++itemsinline;
-    ++items;
-    item = 0;
-    bitsinitem = 0;
-    bitshift = 8 - bitspersample;
+
+    return retval;
 }
 
 
 
 static void
-flushitem() {
-    if (bitsinitem > 0)
-        putitem();
+initOutputEncoder(OutputEncoder  * const oeP,
+                  unsigned int     const icols,
+                  unsigned int     const bitsPerSample,
+                  bool             const rle,
+                  bool             const flate,
+                  bool             const ascii85,
+                  bool             const psFilter) {
+
+    oeP->outputType = ascii85 ? Ascii85 : AsciiHex;
+
+    if (rle) {
+        oeP->compressRle = true;
+        oeP->runlengthRefresh =
+             psFilter ? 1024*1024*16 : bytesPerRow(icols, bitsPerSample);
+    } else
+        oeP->compressRle = false;
+
+    if (flate) {
+        assert(psFilter);
+        oeP->compressFlate = true;
+    } else
+        oeP->compressFlate = false;
+
+    if (ascii85) {
+        assert(psFilter);
+        oeP->outputType = Ascii85;
+    } else
+        oeP->outputType = AsciiHex;
 }
 
 
 
+typedef void FilterFn(FILE *          const ifP,
+                      FILE *          const ofP,
+                      OutputEncoder * const oeP);
+    /* This is a function that can be run in a separate process to do
+       arbitrary modifications of the raster data stream.
+    */
+       
+
+
+#ifndef NOFLATE
+static void
+initZlib(z_stream * const strmP) {
+
+    int const level = 9; /* maximum compression.  see zlib.h */
+
+    int ret;
+
+    /* allocate deflate state */
+    strmP->zalloc = Z_NULL;
+    strmP->zfree  = Z_NULL;
+    strmP->opaque = Z_NULL;
+
+    ret = deflateInit(strmP, level);
+    if (ret != Z_OK)
+        pm_error("Failed to initialize zlib.");
+}
+#endif
+
+
+
+static FilterFn flateFilter;
+
 static void 
-putxelval(xelval const xv) {
-    if (bitsinitem == 8)
-        putitem();
-    item += xv << bitshift;
-    bitsinitem += bitspersample;
-    bitshift -= bitspersample;
+flateFilter(FILE *          const ifP,
+            FILE *          const ofP,
+            OutputEncoder * const oeP) {
+
+#ifndef NOFLATE
+
+    /* This code is based on def() in zpipe.c.  zpipe is an example program
+       which comes with the zlib source package.  zpipe.c is public domain and
+       is available from the Zlib website: http://www.zlib.net/
+
+       See zlib.h for details on zlib parameters Z_NULL, Z_OK, etc.
+    */
+    unsigned int const chunkSz = 128*1024;
+        /* 128K recommended in zpipe.c.  4096 is not efficient but works. */
+
+    int flush;
+    z_stream strm;
+    unsigned char * in;
+    unsigned char * out;
+
+    in  = pm_allocrow(chunkSz, 1);
+    out = pm_allocrow(chunkSz, 1);
+
+    initZlib(&strm);
+
+    /* compress until end of file */
+    do {
+        strm.avail_in = fread(in, 1, chunkSz, ifP);
+        if (ferror(ifP)) {
+            deflateEnd(&strm);
+            pm_error("Error reading from internal pipe during "
+                     "flate compression.");
+        }
+        flush = feof(ifP) ? Z_FINISH : Z_NO_FLUSH;
+        strm.next_in = in;
+
+        /* run deflate() on input until output buffer not full, finish
+           compression if we have reached end of input.
+        */
+        do {
+            unsigned int have;
+
+            strm.avail_out = chunkSz;
+            strm.next_out = out;
+            deflate(&strm, flush);
+            have = chunkSz - strm.avail_out;
+            writeFile(out, have, "flate filter", ofP);
+        } while (strm.avail_out == 0);
+        assert(strm.avail_in == 0);     /* all input is used */
+
+        /* done when last data in file processed */
+    } while (flush != Z_FINISH);
+
+    free(in);
+    free(out); 
+    deflateEnd(&strm);
+    fclose(ifP);
+    fclose(ofP);
+#else
+    assert(false);    /* filter is never used */ 
+#endif
 }
 
 
 
+/* Run length encoding
+
+   In this simple run-length encoding scheme, compressed and uncompressed
+   strings follow a single index byte N.  N 0-127 means the next N+1
+   bytes are uncompressed; 129-255 means the next byte is to be repeated
+   257-N times.
+
+   In native (non-psfilter) mode, the run length filter must flush at
+   the end of every row.  But the entire raster is sent to the run length
+   filter as one continuous stream.  The run length filter learns the
+   refresh interval from oeP->runlengthRefresh.  In ps-filter mode the
+   run length filter ignores row boundaries and flushes every 4096 bytes.
+*/
+
+static FilterFn rleFilter;
+
 static void
-rleputbuffer() {
-    if (repeat) {
-        item = 256 - count;
-        putitem();
-        item = repeatitem;
-        putitem();
-    } else {
-        unsigned int i;
-    
-        item = count - 1;
-        putitem();
-        for (i = 0; i < count; ++i) {
-            item = itembuf[i];
-            putitem();
-        }
+rleFilter (FILE *          const ifP,
+           FILE *          const ofP,
+           OutputEncoder * const oeP) {
+
+    unsigned int const inSize = oeP->runlengthRefresh;
+
+    bool eof;
+    unsigned char * inbuf;
+    unsigned char * outbuf;
+    size_t outSize;
+
+    MALLOCARRAY(inbuf, inSize);
+    if (inbuf == NULL)
+        pm_error("Failed to allocate %u bytes of memory for RLE filter",
+                  inSize);
+    pm_rlenc_allocoutbuf(&outbuf, inSize, PM_RLE_PACKBITS);
+
+    for (eof = false; !eof; ) {
+        size_t const bytesRead = fread(inbuf, 1, inSize, ifP);
+
+        if (feof(ifP))
+            eof = true;
+        else if (ferror(ifP) || bytesRead == 0)
+            pm_error("Internal read error: RLE compression");
+
+        pm_rlenc_compressbyte(inbuf, outbuf, PM_RLE_PACKBITS,
+                              bytesRead, &outSize);
+        writeFile(outbuf, outSize, "rlePutBuffer", ofP);
     }
-    repeat = 1;
-    count = 0;
+
+    fclose(ifP);
+    fclose(ofP);
 }
 
 
 
+static FilterFn asciiHexFilter;
+
 static void
-rleputitem() {
-    int i;
+asciiHexFilter(FILE *          const ifP,
+               FILE *          const ofP,
+               OutputEncoder * const oeP) {
 
-    if ( count == 128 )
-        rleputbuffer();
+    char const hexits[16] = "0123456789abcdef";
 
-    if ( repeat && count == 0 )
-    { /* Still initializing a repeat buf. */
-        itembuf[count] = repeatitem = rleitem;
-        ++count;
-    }
-    else if ( repeat )
-    { /* Repeating - watch for end of run. */
-        if ( rleitem == repeatitem )
-        { /* Run continues. */
-            itembuf[count] = rleitem;
-            ++count;
-        }
-        else
-        { /* Run ended - is it long enough to dump? */
-            if ( count > 2 )
-            { /* Yes, dump a repeat-mode buffer and start a new one. */
-                rleputbuffer();
-                itembuf[count] = repeatitem = rleitem;
-                ++count;
-            }
-            else
-            { /* Not long enough - convert to non-repeat mode. */
-                repeat = 0;
-                itembuf[count] = repeatitem = rleitem;
-                ++count;
-                repeatcount = 1;
+    bool eof;
+    unsigned char inbuff[40], outbuff[81];
+
+    for (eof = false; !eof; ) {
+        size_t readCt;
+
+        readCt = fread(inbuff, 1, 40, ifP);
+
+        if (readCt == 0)
+            eof = true;
+        else {
+            unsigned int i;
+
+            for (i = 0; i < readCt; ++i) {
+                int const item = inbuff[i]; 
+                outbuff[i*2]   = hexits[item >> 4];
+                outbuff[i*2+1] = hexits[item & 15];
             }
+            outbuff[readCt * 2] = '\n';
+            writeFile(outbuff, readCt * 2 + 1, "asciiHex filter", ofP);
         }
     }
-    else
-    { /* Not repeating - watch for a run worth repeating. */
-        if ( rleitem == repeatitem )
-        { /* Possible run continues. */
-            ++repeatcount;
-            if ( repeatcount > 3 )
-            { /* Long enough - dump non-repeat part and start repeat. */
-                count = count - ( repeatcount - 1 );
-                rleputbuffer();
-                count = repeatcount;
-                for ( i = 0; i < count; ++i )
-                    itembuf[i] = rleitem;
+
+    fclose(ifP);
+    fclose(ofP);
+}
+
+
+
+static FilterFn ascii85Filter;
+
+static void
+ascii85Filter(FILE *          const ifP,
+              FILE *          const ofP,
+              OutputEncoder * const oeP) {
+
+    bool eof;
+    char outbuff[5];
+    unsigned long int value; /* requires 32 bits */
+    int count;
+    int outcount;
+
+    value = 0;  /* initial value */
+    count = 0;  /* initial value */
+    outcount = 0; /* initial value */
+
+    for (eof = false; !eof; ) {
+        int c;
+
+        c = fgetc(ifP);
+
+        if (c == EOF)
+            eof = true;
+        else {
+            value = value*256 + c;
+            ++count;
+
+            if (value == 0 && count == 4) {
+                writeFileChar("z", 1, "ASCII 85 filter", ofP);
+                    /* Ascii85 encoding z exception */
+                ++outcount;
+                count = 0;
+            } else if (count == 4) {
+                outbuff[4] = value % 85 + 33;  value/=85; 
+                outbuff[3] = value % 85 + 33;  value/=85;
+                outbuff[2] = value % 85 + 33;  value/=85;
+                outbuff[1] = value % 85 + 33;
+                outbuff[0] = value / 85 + 33;
+
+                writeFileChar(outbuff, count + 1, "ASCII 85 filter", ofP);
+
+                count = value = 0;
+                outcount += 5; 
             }
-            else
-            { /* Not long enough yet - continue as non-repeat buf. */
-                itembuf[count] = rleitem;
-                ++count;
+
+            if (outcount > 75) {
+                writeFileChar("\n", 1, "ASCII 85 filter", ofP);
+                outcount = 0;
             }
         }
-        else
-        { /* Broken run. */
-            itembuf[count] = repeatitem = rleitem;
-            ++count;
-            repeatcount = 1;
-        }
     }
 
-    rleitem = 0;
-    rlebitsinitem = 0;
-    rlebitshift = 8 - bitspersample;
-}
+    if (count > 0) { /* EOF, flush */
+        assert (count < 4);
 
+        value <<= (4 - count) * 8;   value/=85;
+        outbuff[3] = value % 85 + 33;  value/=85;
+        outbuff[2] = value % 85 + 33;  value/=85;
+        outbuff[1] = value % 85 + 33;
+        outbuff[0] = value / 85 + 33;
+        outbuff[count + 1] = '\n';
 
+        writeFileChar(outbuff, count + 2, "ASCII 85 filter", ofP);
+    }
 
-static void 
-rleputxelval(xelval const xv) {
-    if (rlebitsinitem == 8)
-        rleputitem();
-    rleitem += xv << rlebitshift;
-    rlebitsinitem += bitspersample;
-    rlebitshift -= bitspersample;
+    fclose(ifP);
+    fclose(ofP);
 }
 
 
 
 static void
-rleflush() {
-    if (rlebitsinitem > 0)
-        rleputitem();
-    if (count > 0)
-        rleputbuffer();
+makePipe(int * const pipeFdArray) {
+
+    int rc;
+    rc = pm_pipe(pipeFdArray);
+    if (rc == -1)
+        pm_error("pipe() failed, errno = %d (%s)", errno, strerror(errno));
 }
 
 
+
 static void
-flushNativeOutput(bool const rle) {
-    if (rle)
-        rleflush();
-    else
-        flushitem();
-    printf("\n");
-}
-        
-/*===========================================================================
-  The BMEPS output encoder.
-===========================================================================*/
+closeAllBut(int const saveFd0,
+            int const saveFd1,
+            int const saveFd2) {
+/*----------------------------------------------------------------------------
+   Close every file descriptor in this process except 'saveFd0',
+   'saveFd1', and 'saveFd2'.
 
-/* This code is just a wrapper around the output encoder that is part of
-   Bmeps, to give it better modularity.
-*/
+   This is helpful because even if this process doesn't touch other file
+   desriptors, its very existence will keep the files open.
+-----------------------------------------------------------------------------*/
+    
+    /* Unix provides no good way to do this; we just assume file descriptors
+       above 9 are not used in this program; Caller must ensure that is true.
+    */
+    int fd;
 
-struct bmepsoe {
-    Output_Encoder * oeP;
-    int * rleBuffer;
-    Byte * flateInBuffer;
-    Byte * flateOutBuffer;
-};
+    for (fd = 0; fd < 10; ++fd) {
+        if (fd != saveFd0 && fd != saveFd1 && fd != saveFd2)
+            close(fd);
+    }
+}
 
 
 
 static void
-createBmepsOutputEncoder(struct bmepsoe ** const bmepsoePP,
-                         FILE *            const ofP,
-                         bool              const rle,
-                         bool              const flate,
-                         bool              const ascii85) {
-
-    unsigned int const FLATE_IN_SIZE = 16384;
-    unsigned int const FLATE_OUT_SIZE = 17408;
-
-    struct bmepsoe * bmepsoeP;
-    int mode;
-
-    MALLOCVAR_NOFAIL(bmepsoeP);
-    MALLOCVAR_NOFAIL(bmepsoeP->oeP);
-    MALLOCARRAY_NOFAIL(bmepsoeP->rleBuffer, 129);
-    MALLOCARRAY_NOFAIL(bmepsoeP->flateInBuffer, FLATE_IN_SIZE);
-    MALLOCARRAY_NOFAIL(bmepsoeP->flateOutBuffer, FLATE_OUT_SIZE);
-
-    mode = 0;
-    if (rle)
-        mode |= OE_RL;
-    if (flate)
-        mode |= OE_FLATE;
-    if (ascii85)
-        mode |= OE_ASC85;
+spawnFilter(FILE *          const ofP,
+            FilterFn *      const filterFn,
+            OutputEncoder * const oeP,
+            FILE **         const feedFilePP,
+            pid_t *         const pidP) {
+/*----------------------------------------------------------------------------
+   Fork a child process to run filter function 'filterFn' and send its
+   output to *ofP.
+
+   Create a pipe for feeding the filter and return as *feedFilePP the
+   stream to which Caller can write to push stuff into the filter.
+
+   *oeP is the parameter to 'filterFn'.
+-----------------------------------------------------------------------------*/
+    int pipeFd[2];
+    pid_t rc;
+
+    makePipe(pipeFd);
+    
+    rc = fork();
+
+    if (rc == (pid_t)-1)
+        pm_error("fork() of filter process failed.  errno=%d (%s)", 
+                 errno, strerror(errno));
+    else if (rc == 0) {
+        /* This is the child process */
+ 
+        FILE * ifP;
+
+        ifP = fdopen(pipeFd[0], "r");
+
+        if (!ifP)
+            pm_error("filter process failed to make "
+                     "file stream (\"FILE\") "
+                     "out of the file descriptor which is input to the "
+                     "filter.  errno=%d (%s)",
+                     errno, strerror(errno));
+
+        closeAllBut(fileno(ifP), fileno(ofP), STDERR_FILENO);
+
+        filterFn(ifP, ofP, oeP);
+
+        exit(EXIT_SUCCESS);
+    } else {
+        /* This is the parent process */
+
+        pid_t const childPid = rc;
+
+        close(pipeFd[0]);
 
-    oe_init(bmepsoeP->oeP, ofP, mode, 9, 
-            bmepsoeP->rleBuffer, 
-            bmepsoeP->flateInBuffer, FLATE_IN_SIZE,
-            bmepsoeP->flateOutBuffer, FLATE_OUT_SIZE);
+        *feedFilePP = fdopen(pipeFd[1], "w");
 
-    *bmepsoePP = bmepsoeP;
+        *pidP = childPid;
+    }
 }
 
 
 
 static void
-destroyBmepsOutputEncoder(struct bmepsoe * const bmepsoeP) {
-    
-    free(bmepsoeP->rleBuffer);
-    free(bmepsoeP->flateInBuffer);
-    free(bmepsoeP->flateOutBuffer);
+addFilter(const char *    const description,
+          FilterFn *      const filter,
+          OutputEncoder * const oeP,
+          FILE **         const feedFilePP,
+          pid_t *         const pidList) {
+/*----------------------------------------------------------------------------
+   Add a filter to the front of the chain.
+
+   Spawn a process to do the filtering, by running function 'filter'.
+
+   *feedFilePP is the present head of the chain.  We make the new filter
+   process write its output to that and get its input from a new pipe.
+   We update *feedFilePP to the sending end of the new pipe.
+
+   Add to the list pidList[] the PID of the process we spawn.
+-----------------------------------------------------------------------------*/
+    FILE * const oldFeedFileP = *feedFilePP;
+
+    FILE * newFeedFileP;
+    pid_t pid;
+
+    spawnFilter(oldFeedFileP, filter, oeP, &newFeedFileP, &pid);
+            
+    if (verbose)
+        pm_message("%s filter spawned: pid %u",
+                   description, (unsigned)pid);
     
-    free(bmepsoeP);
+    if (debug) {
+        int const outFd    = fileno(oldFeedFileP);
+        int const supplyFd = fileno(newFeedFileP);
+        pm_message("PID %u writes to FD %u, its supplier writes to FD %u",
+                   (unsigned)pid, outFd, supplyFd);
+    }
+    fclose(oldFeedFileP);  /* Child keeps this open now */
+
+    addToPidList(pidList, pid);
+
+    *feedFilePP = newFeedFileP;
 }
 
 
 
 static void
-outputBmepsSample(struct bmepsoe * const bmepsoeP,
-                  unsigned int     const sampleValue,
-                  unsigned int     const bitsPerSample) {
+spawnFilters(FILE *          const ofP,
+             OutputEncoder * const oeP,
+             FILE **         const feedFilePP,
+             pid_t *         const pidList) {
+/*----------------------------------------------------------------------------
+   Get all the child processes for the filters running and connected.
+   Return at *feedFileP the file stream to which to write the raw data,
+   with the filtered data going to *ofP.
 
-    if (bitsPerSample == 8)
-        oe_byte_add(bmepsoeP->oeP, sampleValue);
-    else {
-        unsigned int m;
+   Filter according to *oeP.
+-----------------------------------------------------------------------------*/
 
-        for (m = 1 << (bitsPerSample-1); m != 0; m >>= 1)
-            /* depends on oe_bit_add accepting any value !=0 as 1 */
-            oe_bit_add(bmepsoeP->oeP, sampleValue & m); 
-    }
+    /* Build up the pipeline from the final to the initial stage.  The
+       result is one of:
+
+          FEED | convertRow | asciiHexFilter | *ofP
+          FEED | convertRow | ascii85Filter | *ofP
+          FEED | convertRow | rleFilter   | asciiHexFilter | *ofP
+          FEED | convertRow | flateFilter | asciiHexFilter | *ofP
+          FEED | convertRow | flateFilter | rleFilter | asciiHexFilter | *ofP
+    */
+
+    FILE * feedFileP;
+        /* The current head of the filter chain; changes as we add filters */
+
+    initPidList(pidList);
+
+    feedFileP = ofP;  /* Initial state: no filter at all */
+
+    addFilter(
+        "output",
+        oeP->outputType == Ascii85 ? &ascii85Filter : asciiHexFilter,
+        oeP,
+        &feedFileP,
+        pidList);
+
+    if (oeP->compressFlate)
+        addFilter("flate", flateFilter, oeP, &feedFileP, pidList);
+
+    if (oeP->compressRle)
+        addFilter("rle", rleFilter, oeP, &feedFileP, pidList);
+
+    *feedFilePP = feedFileP;
 }
 
 
 
 static void
-flushBmepsOutput(struct bmepsoe * const bmepsoeP) {
-    oe_byte_flush(bmepsoeP->oeP);
+waitForChildren(const pid_t * const pidList) {
+/*----------------------------------------------------------------------------
+   Wait for all child processes with PIDs in pidList[] to exit.
+   In pidList[], end-of-list is marked with a special zero value.
+-----------------------------------------------------------------------------*/
+    /* There's an odd behavior in Unix such that if you have set the
+       action for SIGCHLD to ignore the signal (even though ignoring the
+       signal is the default), the process' children do not become
+       zombies.  Consequently, waitpid() always fails with ECHILD - but
+       nonetheless waits for the child to exit.
+    
+       We expect the process not to have the action for SIGCHLD set that
+       way.
+    */
+    unsigned int i;
+
+    for (i = 0; pidList[i]; ++i) {
+        pid_t rc;
+        int status;
+
+        if (verbose)
+            pm_message("Waiting for PID %u to exit", (unsigned)pidList[i]);
+
+        rc = waitpid(pidList[i], &status, 0);
+        if (rc == -1)
+            pm_error ("waitpid() for child %u failed, errno=%d (%s)",
+                      i, errno, strerror(errno));
+        else if (status != EXIT_SUCCESS)
+            pm_error ("Child process %u terminated abnormally", i);
+    }
+    if (verbose)
+        pm_message("All children have exited");
 }
 
 
+
 /*============================================================================
-   END OF OUTPUT ENCODERS
+  END OF OUTPUT ENCODERS
 ============================================================================*/
 
 
+
+static void
+validateComputableBoundingBox(float const scols, 
+                              float const srows,
+                              float const llx, 
+                              float const lly) {
+
+    float const bbWidth  = llx + scols + 0.5;
+    float const bbHeight = lly + srows + 0.5;
+
+    if (bbHeight < INT_MIN || bbHeight > INT_MAX ||
+        bbWidth  < INT_MIN || bbWidth  > INT_MAX)
+        pm_error("Bounding box dimensions %.1f x %.1f are too large "
+                 "for computations.  "
+                 "This probably means input image width, height, "
+                 "or scale factor is too large", bbWidth, bbHeight);
+}
+
+
+
+static void
+warnUserRescaling(float const scale) {
+
+    const char * const baseMsg = "warning, image too large for page";
+
+    if (pm_have_float_format())
+        pm_message("%s; rescaling to %g", baseMsg, scale);
+    else
+        pm_message("%s; rescaling", baseMsg);
+}
+
+
+
 static void
 computeImagePosition(int     const dpiX, 
                      int     const dpiY, 
@@ -545,39 +1054,41 @@ computeImagePosition(int     const dpiX,
                      float * const llyP,
                      bool *  const turnedP ) {
 /*----------------------------------------------------------------------------
-   Determine where on the page the image is to go.  This means position,
-   dimensions, and orientation.
+  Determine where on the page the image is to go.  This means position,
+  dimensions, and orientation.
 
-   icols/irows are the dimensions of the PNM input in xels.
+  icols/irows are the dimensions of the PNM input in xels.
 
-   'mustturn' means we are required to rotate the image.
+  'mustturn' means we are required to rotate the image.
 
-   'canturn' means we may rotate the image if it fits better, but don't
-   have to.
+  'canturn' means we may rotate the image if it fits better, but don't
+  have to.
 
-   *scolsP, *srowsP are the dimensions of the image in 1/72 inch.
+  *scolsP, *srowsP are the dimensions of the image in 1/72 inch.
 
-   *llxP, *llyP are the coordinates, in 1/72 inch, of the lower left
-   corner of the image on the page.
+  *llxP, *llyP are the coordinates in the Postcript frame, of the lower left
+  corner of the image on the page.  The Postscript frame is different from the
+  Neptbm frame: units are 1/72 inch (1 point) and (0,0) is the lower left
+  corner.
 
-   *turnedP is true iff the image is to be rotated 90 degrees on the page.
+  *turnedP is true iff the image is to be rotated 90 degrees on the page.
 
-   imagewidth/imageheight are the requested dimensions of the image on
-   the page, in 1/72 inch.  Image will be as large as possible within
-   those dimensions.  Zero means unspecified, so 'scale', 'pagewid',
-   'pagehgt', 'irows', and 'icols' determine image size.
+  imagewidth/imageheight are the requested dimensions of the image on
+  the page, in 1/72 inch.  Image will be as large as possible within
+  those dimensions.  Zero means unspecified, so 'scale', 'pagewid',
+  'pagehgt', 'irows', and 'icols' determine image size.
 
-   'equalpixels' means the user wants one printed pixel per input pixel.
-   It is inconsistent with imagewidth or imageheight != 0
+  'equalpixels' means the user wants one printed pixel per input pixel.
+  It is inconsistent with imagewidth or imageheight != 0
 
-   'requestedScale' is meaningful only when imageheight/imagewidth == 0
-   and equalpixels == FALSE.  It tells how many inches the user wants
-   72 pixels of input to occupy, if it fits on the page.
+  'requestedScale' is meaningful only when imageheight/imagewidth == 0
+  and equalpixels == FALSE.  It tells how many inches the user wants
+  72 pixels of input to occupy, if it fits on the page.
 -----------------------------------------------------------------------------*/
     int cols, rows;
-        /* Number of columns, rows of input xels in the output, as
-           rotated if applicable
-        */
+    /* Number of columns, rows of input xels in the output, as
+       rotated if applicable
+    */
     bool shouldturn;  /* The image fits the page better if we turn it */
     
     if (icols > irows && pagehgt > pagewid)
@@ -608,7 +1119,7 @@ computeImagePosition(int     const dpiX,
             scale = (float) imagewidth/cols;
         else
             scale = MIN((float)imagewidth/cols, (float)imageheight/rows);
-        
+    
         *scolsP = cols*scale;
         *srowsP = rows*scale;
     } else {
@@ -617,8 +1128,8 @@ computeImagePosition(int     const dpiX,
         */
         const int devpixX = dpiX / 72.0 + 0.5;        
         const int devpixY = dpiY / 72.0 + 0.5;        
-            /* How many device pixels make up 1/72 inch, rounded to
-               nearest integer */
+        /* How many device pixels make up 1/72 inch, rounded to
+           nearest integer */
         const float pixfacX = 72.0 / dpiX * devpixX;  /* 1, approx. */
         const float pixfacY = 72.0 / dpiY * devpixY;  /* 1, approx. */
         float scale;
@@ -628,10 +1139,9 @@ computeImagePosition(int     const dpiX,
 
         *scolsP = scale * cols * pixfacX;
         *srowsP = scale * rows * pixfacY;
-        
+    
         if (scale != requestedScale)
-            pm_message("warning, image too large for page, rescaling to %g", 
-                       scale );
+            warnUserRescaling(scale);
 
         /* Before May 2001, Pnmtops enforced a 5% margin around the page.
            If the image would be too big to leave a 5% margin, Pnmtops would
@@ -644,6 +1154,7 @@ computeImagePosition(int     const dpiX,
     *llxP = (center) ? ( pagewid - *scolsP ) / 2 : 0;
     *llyP = (center) ? ( pagehgt - *srowsP ) / 2 : 0;
 
+    validateComputableBoundingBox( *scolsP, *srowsP, *llxP, *llyP);
 
     if (verbose)
         pm_message("Image will be %3.2f points wide by %3.2f points high, "
@@ -677,7 +1188,7 @@ determineDictionaryRequirement(bool           const userWantsDict,
 static void
 defineReadstring(bool const rle) {
 /*----------------------------------------------------------------------------
-   Write to Standard Output Postscript statements to define /readstring.
+  Write to Standard Output Postscript statements to define /readstring.
 -----------------------------------------------------------------------------*/
     if (rle) {
         printf("/rlestr1 1 string def\n");
@@ -692,7 +1203,7 @@ defineReadstring(bool const rle) {
         printf("    readhexstring pop\n");            /* s */
         printf("    length\n");               /* nr */
         printf("  } {\n");                    /* c */
-        printf("    256 exch sub dup\n");         /* n n */
+        printf("    257 exch sub dup\n");         /* n n */
         printf("    currentfile rlestr1 readhexstring pop\n");/* n n s1 */
         printf("    0 get\n");                /* n n c */
         printf("    exch 0 exch 1 exch 1 sub {\n");       /* n c 0 1 n-1*/
@@ -725,13 +1236,14 @@ static void
 setupReadstringNative(bool         const rle,
                       bool         const color,
                       unsigned int const icols, 
-                      unsigned int const padright, 
-                      unsigned int const bps) {
+                      unsigned int const bitsPerSample) {
 /*----------------------------------------------------------------------------
-   Write to Standard Output statements to define /readstring and also
-   arguments for it (/picstr or /rpicstr, /gpicstr, and /bpicstr).
+  Write to Standard Output statements to define /readstring and also
+  arguments for it (/picstr or /rpicstr, /gpicstr, and /bpicstr).
 -----------------------------------------------------------------------------*/
-    unsigned int const bytesPerRow = (icols + padright) * bps / 8;
+    unsigned int const bytesPerRow = icols / (8/bitsPerSample) +
+        (icols % (8/bitsPerSample) > 0 ? 1 : 0);
+        /* Size of row buffer, padded up to byte boundary. */
 
     defineReadstring(rle);
     
@@ -754,13 +1266,17 @@ putFilters(unsigned int const postscriptLevel,
 
     assert(postscriptLevel > 1);
     
+    /* We say to decode flate, then rle, so Caller must ensure it encodes
+       rel, then flate.
+    */
+
     if (ascii85)
         printf("/ASCII85Decode filter ");
     else 
         printf("/ASCIIHexDecode filter ");
     if (flate)
         printf("/FlateDecode filter ");
-    if (rle) /* bmeps encodes rle before flate, so must decode after! */
+    if (rle) 
         printf("/RunLengthDecode filter ");
 }
 
@@ -785,10 +1301,9 @@ putSetup(unsigned int const dictSize,
          bool         const rle,
          bool         const color,
          unsigned int const icols,
-         unsigned int const padright,
-         unsigned int const bps) {
+         unsigned int const bitsPerSample) {
 /*----------------------------------------------------------------------------
-   Put the setup section in the Postscript program on Standard Output.
+  Put the setup section in the Postscript program on Standard Output.
 -----------------------------------------------------------------------------*/
     printf("%%%%BeginSetup\n");
 
@@ -797,7 +1312,7 @@ putSetup(unsigned int const dictSize,
         printf("%u dict begin\n", dictSize);
     
     if (!psFilter)
-        setupReadstringNative(rle, color, icols, padright, bps);
+        setupReadstringNative(rle, color, icols, bitsPerSample);
 
     printf("%%%%EndSetup\n");
 }
@@ -808,8 +1323,8 @@ static void
 putImage(bool const psFilter,
          bool const color) {
 /*----------------------------------------------------------------------------
-   Put the image/colorimage statement in the Postscript program on
-   Standard Output.
+  Put the image/colorimage statement in the Postscript program on
+  Standard Output.
 -----------------------------------------------------------------------------*/
     if (color) {
         if (psFilter)
@@ -864,8 +1379,7 @@ putInit(unsigned int const postscriptLevel,
         float        const srows,
         float        const llx, 
         float        const lly,
-        int          const padright, 
-        int          const bps,
+        int          const bitsPerSample,
         int          const pagewid, 
         int          const pagehgt,
         bool         const color, 
@@ -877,8 +1391,8 @@ putInit(unsigned int const postscriptLevel,
         bool         const psFilter,
         unsigned int const dictSize) {
 /*----------------------------------------------------------------------------
-   Write out to Standard Output the headers stuff for the Postscript
-   program (everything up to the raster).
+  Write out to Standard Output the headers stuff for the Postscript
+  program (everything up to the raster).
 -----------------------------------------------------------------------------*/
     /* The numbers in the %! line often confuse people. They are NOT the
        PostScript language level.  The first is the level of the DSC comment
@@ -897,7 +1411,7 @@ putInit(unsigned int const postscriptLevel,
         (int) (llx + scols + 0.5), (int) (lly + srows + 0.5));
     printf("%%%%EndComments\n");
 
-    putSetup(dictSize, psFilter, rle, color, icols, padright, bps);
+    putSetup(dictSize, psFilter, rle, color, icols, bitsPerSample);
 
     printf("%%%%Page: 1 1\n");
     if (setpage)
@@ -908,7 +1422,7 @@ putInit(unsigned int const postscriptLevel,
     printf("%g %g scale\n", scols, srows);
     if (turned)
         printf("0.5 0.5 translate  90 rotate  -0.5 -0.5 translate\n");
-    printf("%d %d %d\n", icols, irows, bps);
+    printf("%d %d %d\n", icols, irows, bitsPerSample);
     printf("[ %d 0 0 -%d 0 %d ]\n", icols, irows, irows);
 
     if (psFilter)
@@ -917,6 +1431,7 @@ putInit(unsigned int const postscriptLevel,
         putInitReadstringNative(color);
 
     printf("\n");
+    fflush(stdout);
 }
 
 
@@ -957,8 +1472,57 @@ putEnd(bool         const showpage,
 
 
 static void
+validateBpsRequest(unsigned int const bitsPerSampleReq,
+                   unsigned int const postscriptLevel,
+                   bool         const psFilter) {
+
+    if (postscriptLevel < 2 && bitsPerSampleReq > 8)
+        pm_error("You requested %u bits per sample, but in Postscript "
+                 "level 1, 8 is the maximum.  You can get 12 with "
+                 "-level 2 and -psfilter", bitsPerSampleReq);
+    else if (!psFilter && bitsPerSampleReq > 8)
+        pm_error("You requested %u bits per sample, but without "
+                 "-psfilter, the maximum is 8", bitsPerSampleReq);
+}
+
+    
+
+static unsigned int
+bpsFromInput(unsigned int const bitsRequiredByMaxval,
+             unsigned int const postscriptLevel,
+             bool         const psFilter) {
+
+    unsigned int retval;
+
+    if (bitsRequiredByMaxval <= 1)
+        retval = 1;
+    else if (bitsRequiredByMaxval <= 2)
+        retval = 2;
+    else if (bitsRequiredByMaxval <= 4)
+        retval = 4;
+    else if (bitsRequiredByMaxval <= 8)
+        retval = 8;
+    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)
+            retval = 12;
+        else
+            retval = 8;
+    }
+    return retval;
+}
+
+
+
+static void
 warnUserAboutReducedDepth(unsigned int const bitsGot,
                           unsigned int const bitsWanted,
+                          bool         const userRequested,
                           unsigned int const postscriptLevel,
                           bool         const psFilter) {
 
@@ -967,15 +1531,19 @@ warnUserAboutReducedDepth(unsigned int const bitsGot,
                    "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.");
+        if (!userRequested) {
+            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.");
+            }
         }
     }
 }
@@ -986,76 +1554,237 @@ static void
 computeDepth(xelval         const inputMaxval,
              unsigned int   const postscriptLevel, 
              bool           const psFilter,
-             unsigned int * const bitspersampleP,
-             unsigned int * const psMaxvalP) {
+             unsigned int   const bitsPerSampleReq,
+             unsigned int * const bitsPerSampleP) {
 /*----------------------------------------------------------------------------
-   Figure out how many bits will represent each sample in the Postscript
-   program, and the maxval of the Postscript program samples.  The maxval
-   is just the maximum value allowable in the number of bits.
+  Figure out how many bits will represent each sample in the Postscript
+  program, and the maxval of the Postscript program samples.  The maxval
+  is just the maximum value allowable in the number of bits.
+
+  'bitsPerSampleReq' is the bits per sample that the user requests, or
+  zero if he made no request.
 -----------------------------------------------------------------------------*/
     unsigned int const bitsRequiredByMaxval = pm_maxvaltobits(inputMaxval);
 
-    if (bitsRequiredByMaxval <= 1)
-        *bitspersampleP = 1;
-    else if (bitsRequiredByMaxval <= 2)
-        *bitspersampleP = 2;
-    else if (bitsRequiredByMaxval <= 4)
-        *bitspersampleP = 4;
-    else if (bitsRequiredByMaxval <= 8)
-        *bitspersampleP = 8;
-    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
-            *bitspersampleP = 8;
+    if (bitsPerSampleReq != 0) {
+        validateBpsRequest(bitsPerSampleReq, postscriptLevel, psFilter);
+        *bitsPerSampleP = bitsPerSampleReq;
+    } else {
+        *bitsPerSampleP = bpsFromInput(bitsRequiredByMaxval,
+                                       postscriptLevel, psFilter);
     }
-
-    warnUserAboutReducedDepth(*bitspersampleP, bitsRequiredByMaxval,
+    warnUserAboutReducedDepth(*bitsPerSampleP, bitsRequiredByMaxval,
+                              bitsPerSampleReq != 0,
                               postscriptLevel, psFilter);
 
-    *psMaxvalP = pm_bitstomaxval(*bitspersampleP);
-
-    if (verbose)
+    if (verbose) {
+        unsigned int const psMaxval = pm_bitstomaxval(*bitsPerSampleP);
         pm_message("Input maxval is %u.  Postscript raster will have "
                    "%u bits per sample, so maxval = %u",
-                   inputMaxval, *bitspersampleP, *psMaxvalP);
+                   inputMaxval, *bitsPerSampleP, psMaxval);
+    }
 }    
 
 
 
+/*===========================================================================
+  The bit accumulator
+===========================================================================*/
+
+typedef struct {
+    unsigned int value;
+    unsigned int consumed;
+} BitAccumulator;
+
+
+
 static void
-convertRowNative(struct pam * const pamP, 
-                 tuple *      const tuplerow, 
-                 unsigned int const psMaxval, 
-                 bool         const rle, 
-                 unsigned int const padright) {
+ba_init(BitAccumulator * const baP) {
+
+    baP->value    = 0;
+    baP->consumed = 0;
+}
+
+
+
+static void
+ba_add12(BitAccumulator * const baP,
+         unsigned int     const new12,
+         FILE           * const fP) {
+/*----------------------------------------------------------------------------
+  Read a 12-bit string into the bit accumulator baP->value.
+  On every other call, combine two 12-bit strings and write out three bytes.
+-----------------------------------------------------------------------------*/
+    assert (baP->consumed == 12 || baP->consumed == 0);
+
+    if (baP->consumed == 12){
+        char const oldHi8 = (baP->value) >> 4;
+        char const oldLo4 = (baP->value) & 0x0f;
+        char const newHi4 = new12 >> 8;
+        char const newLo8 = new12 & 0xff;
+
+        fputc(oldHi8, fP);
+        fputc((oldLo4 << 4) | newHi4 , fP);
+        fputc(newLo8, fP);
+        baP->value = 0; baP->consumed = 0;
+    } else {
+        baP->value = new12;  baP->consumed = 12;
+    }
+}
+
+
+
+static void
+ba_add(BitAccumulator * const baP,
+       unsigned int     const b,
+       unsigned int     const bitsPerSample,
+       FILE           * const fP) {
+/*----------------------------------------------------------------------------
+  Combine bit sequences that do not fit into a byte.
+
+  Used when bitsPerSample =1, 2, 4.  
+  Logic also works for bitsPerSample = 8, 16.
+
+  The accumulator, baP->value is unsigned int (usually 32 bits), but
+  only 8 bits are used.
+-----------------------------------------------------------------------------*/
+    unsigned int const bufSize = 8;
+
+    assert (bitsPerSample == 1 || bitsPerSample == 2 || bitsPerSample == 4);
+
+    baP->value = (baP->value << bitsPerSample) | b ;
+    baP->consumed += bitsPerSample;
+    if (baP->consumed == bufSize) {
+        /* flush */
+        fputc( baP->value, fP);
+        baP->value = 0;
+        baP->consumed = 0;
+    }
+}
+
+
+
+static void
+ba_flush(BitAccumulator * const baP,
+         FILE *           const fP) {
+/*----------------------------------------------------------------------------
+  Flush partial bits in baP->consumed.
+-----------------------------------------------------------------------------*/
+    if (baP->consumed == 12) {
+        char const oldHi8 = (baP->value) >> 4;
+        char const oldLo4 = (baP->value) & 0x0f;
+        fputc(oldHi8, fP);
+        fputc(oldLo4 << 4, fP);
+    } else if (baP->consumed == 8)
+        fputc(baP->value , fP);
+    else if (baP->consumed > 0) {
+        unsigned int const leftShift = 8 - baP->consumed;
+        assert(baP->consumed <= 8);  /* why? */
+        baP->value <<= leftShift;
+        fputc(baP->value , fP);
+    }
+    baP->value = 0;
+    baP->consumed = 0;
+}
+
+
+
+static void
+outputSample(BitAccumulator * const baP,
+             unsigned int     const sampleValue,
+             unsigned int     const bitsPerSample,
+             FILE           * const fP) {
+
+    if (bitsPerSample == 8)
+        fputc(sampleValue, fP);
+    else if (bitsPerSample == 12)
+        ba_add12(baP, sampleValue, fP);
+    else
+        ba_add(baP, sampleValue, bitsPerSample, fP);
+}
+
+
+
+static void
+flushOutput(BitAccumulator * const baP,
+            FILE *           const fP) {
+    ba_flush(baP, fP);
+}
+
+
+
+/*----------------------------------------------------------------------
+  Row converters
+
+  convertRowPbm is a fast routine for PBM images.
+  It is used only when the input is PBM and the user does not specify
+  a -bitspersample value greater than 1.  It is not used when the input
+  image is PGM or PPM and the output resolution is brought down to one
+  bit per pixel by -bitpersample=1 .
+
+  convertRowNative and convertRowPsFilter are the general converters.
+  They are quite similar, the differences being:
+  (1) Native output separates the color planes: 
+  (RRR...RRR GGG...GGG BBB...BBB),
+  whereas psFilter does not:
+  (RGB RGB RGB RGB ......... RGB).
+  (2) Native flushes the run-length encoder at the end of each row if
+  grayscale, at the end of each plane if color.
+
+  Both convertRowNative and convertRowPsFilter can handle PBM, though we
+  don't use them.
+
+  If studying the code, read convertRowPbm first.  convertRowNative and
+  convertRowPsFilter are constructs that pack raster data into a form
+  similar to a binary PBM bitrow.
+  ----------------------------------------------------------------------*/
+
+static void
+convertRowPbm(struct pam *     const pamP,
+              unsigned char  * const bitrow,
+              bool             const psFilter,
+              FILE *           const fP) {
+/*---------------------------------------------------------------------
+  Feed PBM raster data directly to the output encoder.
+  Invert bits: 0 is "white" in PBM, 0 is "black" in postscript.
+----------------------------------------------------------------------*/
+    unsigned int colChar;
+    unsigned int const colChars = pbm_packed_bytes(pamP->width);
+
+    pbm_readpbmrow_packed(pamP->file, bitrow, pamP->width, pamP->format);
+
+    for (colChar = 0; colChar < colChars; ++colChar)
+        bitrow[colChar] =  ~ bitrow[colChar];
+
+    /* Zero clear padding beyond right edge */
+    pbm_cleanrowend_packed(bitrow, pamP->width);
+    writeFile(bitrow, colChars, "PBM reader", fP);
+}
+
+
+
+static void
+convertRowNative(struct pam *     const pamP, 
+                 tuple *                tuplerow, 
+                 unsigned int     const bitsPerSample,
+                 FILE           * const fP) { 
+
+    unsigned int const psMaxval = pm_bitstomaxval(bitsPerSample);
 
     unsigned int plane;
+    BitAccumulator ba;
+
+    ba_init(&ba);
+
+    pnm_readpamrow(pamP, tuplerow);
+    pnm_scaletuplerow(pamP, tuplerow, tuplerow, psMaxval);
 
     for (plane = 0; plane < pamP->depth; ++plane) {
         unsigned int col;
-        for (col= 0; col < pamP->width; ++col) {
-            sample const scaledSample = 
-                pnm_scalesample(tuplerow[col][plane], pamP->maxval, psMaxval);
+        for (col= 0; col < pamP->width; ++col)
+            outputSample(&ba, tuplerow[col][plane], bitsPerSample, fP);
 
-            if (rle)
-                rleputxelval(scaledSample);
-            else
-                putxelval(scaledSample);
-        }
-        for (col = 0; col < padright; ++col)
-            if (rle)
-                rleputxelval(0);
-        else
-            putxelval(0);
-        if (rle)
-            rleflush();
+        flushOutput(&ba, fP);
     }
 }
 
@@ -1063,34 +1792,27 @@ convertRowNative(struct pam * const pamP,
 
 static void
 convertRowPsFilter(struct pam *     const pamP,
-                   tuple *          const tuplerow,
-                   struct bmepsoe * const bmepsoeP,
-                   unsigned int     const psMaxval) {
+                   tuple *                tuplerow,
+                   unsigned int     const bitsPerSample,
+                   FILE           * const fP) { 
 
-    unsigned int const bitsPerSample = pm_maxvaltobits(psMaxval);
-    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 */
+    unsigned int const psMaxval = pm_bitstomaxval(bitsPerSample);
 
     unsigned int col;
-    tuple scaledTuple;
-    
-    scaledTuple = pnm_allocpamtuple(pamP);
+    BitAccumulator ba;
+
+    ba_init(&ba);
+
+    pnm_readpamrow(pamP, tuplerow);
+    pnm_scaletuplerow(pamP, tuplerow, tuplerow, psMaxval);
 
     for (col = 0; col < pamP->width; ++col) {
         unsigned int plane;
-        pnm_scaletuple(pamP, scaledTuple, tuplerow[col], psMaxval);
-        
         for (plane = 0; plane < pamP->depth; ++plane)
-            outputBmepsSample(bmepsoeP, scaledTuple[plane], bitsPerSample);
-    }
-    if (stragglers > 0) {
-        unsigned int i;
-        for (i = stragglers; i < 8; ++i)
-            oe_bit_add(bmepsoeP->oeP, 0);
+            outputSample(&ba, tuplerow[col][plane], bitsPerSample, fP);
     }
-    pnm_freepamtuple(scaledTuple);
+    flushOutput(&ba, fP);
+
 }
 
 
@@ -1146,50 +1868,111 @@ selectPostscriptLevel(bool           const levelIsGiven,
 
 
 static void
-convertPage(FILE * const ifP, 
-            int    const turnflag, 
-            int    const turnokflag, 
-            bool   const psFilter,
-            bool   const rle, 
-            bool   const flate,
-            bool   const ascii85,
-            bool   const setpage,
-            bool   const showpage,
-            bool   const center, 
-            float  const scale,
-            int    const dpiX, 
-            int    const dpiY, 
-            int    const pagewid, 
-            int    const pagehgt,
-            int    const imagewidth, 
-            int    const imageheight, 
-            bool   const equalpixels,
-            char   const name[],
-            bool   const dict,
-            bool   const vmreclaim,
-            bool   const levelIsGiven,
+convertRaster(struct pam * const inpamP,
+              unsigned int const bitsPerSample,
+              bool         const psFilter,
+              FILE *       const fP) {
+/*----------------------------------------------------------------------------
+   Read the raster described by *inpamP, and write a bit stream of samples
+   to *fP.  This stream has to be compressed and converted to text before it
+   can be part of a Postscript program.
+   
+   'psFilter' means to do the conversion using built in Postscript filters, as
+   opposed to our own filters via /readstring.
+
+   'bitsPerSample' is how many bits each sample is to take in the Postscript
+   output.
+-----------------------------------------------------------------------------*/
+    if (PAM_FORMAT_TYPE(inpamP->format) == PBM_TYPE && bitsPerSample == 1)  {
+        unsigned char * bitrow;
+        unsigned int row;
+
+        bitrow = pbm_allocrow_packed(inpamP->width);
+
+        for (row = 0; row < inpamP->height; ++row)
+            convertRowPbm(inpamP, bitrow, psFilter, fP);
+
+        pbm_freerow(bitrow);
+    } else  {
+        tuple *tuplerow;
+        unsigned int row;
+        
+        tuplerow = pnm_allocpamrow(inpamP);
+
+        for (row = 0; row < inpamP->height; ++row) {
+            if (psFilter)
+                convertRowPsFilter(inpamP, tuplerow, bitsPerSample, fP);
+            else
+                convertRowNative(inpamP, tuplerow, bitsPerSample, fP);
+        }
+        pnm_freepamrow(tuplerow);
+    }
+}
+
+
+
+/* FILE MANAGEMENT: File management is pretty hairy here.  A filter, which
+   runs in its own process, needs to be able to cause its output file to
+   close because it might be an internal pipe and the next stage needs to
+   know output is done.  So the forking process must close its copy of the
+   file descriptor.  BUT: if the output of the filter is not an internal
+   pipe but this program's output, then we don't want it closed when the
+   filter terminates because we'll need it to be open for the next image
+   the program converts (with a whole new chain of filters).
+   
+   To prevent the progam output file from getting closed, we pass a
+   duplicate of it to spawnFilters() and keep the original open.
+*/
+
+
+
+static void
+convertPage(FILE *       const ifP, 
+            int          const turnflag, 
+            int          const turnokflag, 
+            bool         const psFilter,
+            bool         const rle, 
+            bool         const flate,
+            bool         const ascii85,
+            bool         const setpage,
+            bool         const showpage,
+            bool         const center, 
+            float        const scale,
+            int          const dpiX, 
+            int          const dpiY, 
+            int          const pagewid, 
+            int          const pagehgt,
+            int          const imagewidth, 
+            int          const imageheight, 
+            bool         const equalpixels,
+            unsigned int const bitsPerSampleReq,
+            char         const name[],
+            bool         const dict,
+            bool         const vmreclaim,
+            bool         const levelIsGiven,
             unsigned int const levelGiven) {
     
     struct pam inpam;
-    tuple* tuplerow;
-    unsigned int padright;
-        /* Number of bits we must add to the right end of each Postscript
-           output line in order to have an integral number of bytes of output.
-           E.g. at 2 bits per sample with 10 columns, this would be 4.
-        */
-    int row;
-    unsigned int psMaxval;
-        /* The maxval of the Postscript program */
     float scols, srows;
     float llx, lly;
     bool turned;
     bool color;
     unsigned int postscriptLevel;
-    struct bmepsoe * bmepsoeP;
-    unsigned int dictSize;
+    unsigned int bitsPerSample;
+    unsigned int dictSize;  
         /* Size of Postscript dictionary we should define */
+    OutputEncoder oe;
+    pid_t filterPidList[MAX_FILTER_CT + 1];
+
+    FILE * feedFileP;
+        /* The file stream which is the head of the filter chain; we write to
+           this and filtered stuff comes out the other end.
+        */
+    FILE * filterChainOfP;
 
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    validateCompDimension(inpam.width, 16, "Input image width");
     
     if (!STRSEQ(inpam.tuple_type, PAM_PBM_TUPLETYPE) &&
         !STRSEQ(inpam.tuple_type, PAM_PGM_TUPLETYPE) &&
@@ -1206,13 +1989,9 @@ convertPage(FILE * const ifP,
     if (color)
         pm_message("generating color Postscript program.");
 
-    computeDepth(inpam.maxval, postscriptLevel, psFilter,
-                 &bitspersample, &psMaxval);
-    {
-        unsigned int const realBitsPerLine = inpam.width * bitspersample;
-        unsigned int const paddedBitsPerLine = ((realBitsPerLine + 7) / 8) * 8;
-        padright = (paddedBitsPerLine - realBitsPerLine) / bitspersample;
-    }
+    computeDepth(inpam.maxval, postscriptLevel, psFilter, bitsPerSampleReq,
+                 &bitsPerSample);
+
     /* In positioning/scaling the image, we treat the input image as if
        it has a density of 72 pixels per inch.
     */
@@ -1225,84 +2004,65 @@ convertPage(FILE * const ifP,
     determineDictionaryRequirement(dict, psFilter, &dictSize);
     
     putInit(postscriptLevel, name, inpam.width, inpam.height, 
-            scols, srows, llx, lly, padright, bitspersample, 
+            scols, srows, llx, lly, bitsPerSample, 
             pagewid, pagehgt, color,
             turned, rle, flate, ascii85, setpage, psFilter, dictSize);
 
-    createBmepsOutputEncoder(&bmepsoeP, stdout, rle, flate, ascii85);
-    initNativeOutputEncoder(rle, bitspersample);
+    initOutputEncoder(&oe, inpam.width, bitsPerSample,
+                      rle, flate, ascii85, psFilter);
 
-    tuplerow = pnm_allocpamrow(&inpam);
-
-    for (row = 0; row < inpam.height; ++row) {
-        pnm_readpamrow(&inpam, tuplerow);
-        if (psFilter)
-            convertRowPsFilter(&inpam, tuplerow, bmepsoeP, psMaxval);
-        else
-            convertRowNative(&inpam, tuplerow, psMaxval, rle, padright);
-    }
+    fflush(stdout);
+    filterChainOfP = fdopen(dup(fileno(stdout)), "w");
+        /* spawnFilters() closes this.  See FILE MANAGEMENT above */
 
-    pnm_freepamrow(tuplerow);
+    spawnFilters(filterChainOfP, &oe, &feedFileP, filterPidList);
+ 
+    convertRaster(&inpam, bitsPerSample, psFilter, feedFileP);
 
-    if (psFilter)
-        flushBmepsOutput(bmepsoeP);
-    else
-        flushNativeOutput(rle);
+    fflush(feedFileP);
+    fclose(feedFileP);
 
-    destroyBmepsOutputEncoder(bmepsoeP);
+    waitForChildren(filterPidList);
 
     putEnd(showpage, psFilter, ascii85, dictSize, vmreclaim);
 }
 
 
 
-static const char *
-basebasename(const char * const filespec) {
-/*----------------------------------------------------------------------------
-    Return filename up to first period
------------------------------------------------------------------------------*/
-    char const dirsep = '/';
-    const char * const lastSlashPos = strrchr(filespec, dirsep);
-
-    char * name;
-    const char * filename;
-
-    if (lastSlashPos)
-        filename = lastSlashPos + 1;
-    else
-        filename = filespec;
-
-    name = strdup(filename);
-    if (name != NULL) {
-        char * const dotPosition = strchr(name, '.');
-
-        if (dotPosition)
-            *dotPosition = '\0';
-    }
-    return name;
-}
-
-
-
 int
-main(int argc, char * argv[]) {
+main(int argc, const char * argv[]) {
 
-    FILE* ifp;
-    const char *name;  /* malloc'ed */
+    FILE * ifP;
+    const char * name;  /* malloc'ed */
     struct cmdlineInfo cmdline;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
+
+    setSignals();
 
     parseCommandLine(argc, argv, &cmdline);
 
-    verbose = cmdline.verbose;
+    verbose = cmdline.verbose || cmdline.debug;
+    debug   = cmdline.debug;
 
-    ifp = pm_openr(cmdline.inputFileName);
+    if (cmdline.flate && !progIsFlateCapable())
+        pm_error("This program cannot do flate compression.  "
+                 "(There are other versions of the program that do, "
+                 "though -- it's a build-time option");
+
+    ifP = pm_openr(cmdline.inputFileName);
 
     if (streq(cmdline.inputFileName, "-"))
         name = strdup("noname");
     else
         name = basebasename(cmdline.inputFileName);
+
+    /* This program manages file descriptors in a way that assumes
+       that new files will get file descriptor numbers less than 10,
+       so we close superfluous files now to make sure that's true.
+    */
+    closeAllBut(fileno(ifP), fileno(stdout), fileno(stderr));
+
     {
         int eof;  /* There are no more images in the input file */
         unsigned int imageSeq;
@@ -1320,7 +2080,7 @@ main(int argc, char * argv[]) {
 
         eof = FALSE;  /* There is always at least one image */
         for (imageSeq = 0; !eof; ++imageSeq) {
-            convertPage(ifp, cmdline.mustturn, cmdline.canturn, 
+            convertPage(ifP, cmdline.mustturn, cmdline.canturn, 
                         cmdline.psfilter,
                         cmdline.rle, cmdline.flate, cmdline.ascii85, 
                         cmdline.setpage, cmdline.showpage,
@@ -1328,15 +2088,17 @@ main(int argc, char * argv[]) {
                         cmdline.dpiX, cmdline.dpiY,
                         cmdline.width, cmdline.height, 
                         cmdline.imagewidth, cmdline.imageheight, 
-                        cmdline.equalpixels, name, 
+                        cmdline.equalpixels,
+                        cmdline.bitspersampleSpec ? cmdline.bitspersample : 0,
+                        name, 
                         cmdline.dict, cmdline.vmreclaim,
                         cmdline.levelSpec, cmdline.level);
-            pnm_nextimage(ifp, &eof);
+            pnm_nextimage(ifP, &eof);
         }
     }
-    strfree(name);
+    pm_strfree(name);
 
-    pm_close(ifp);
+    pm_close(ifP);
 
     return 0;
 }
@@ -1357,4 +2119,9 @@ main(int argc, char * argv[]) {
 ** -nocenter option added November 1993 by Wolfgang Stuerzlinger,
 **  wrzl@gup.uni-linz.ac.at.
 **
+** July 2011 afu
+** row convertors rewritten, fast PBM-only row convertor added,
+** rle compression slightly modified, flate compression added
+** ascii85 output end added.
+**
 */
diff --git a/converter/other/pnmtorast.c b/converter/other/pnmtorast.c
index 605e815c..e11d3cb7 100644
--- a/converter/other/pnmtorast.c
+++ b/converter/other/pnmtorast.c
@@ -19,103 +19,285 @@
 
 
 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;
+allocPrColormap(void) {
+
+    colormap_t * prColormapP;
+
+    MALLOCVAR(prColormapP);
+    if (prColormapP == NULL)
+        pm_error("out of memory");
+    prColormapP->type = RMT_EQUAL_RGB;
+    prColormapP->length = MAXCOLORS;
+    MALLOCARRAY(prColormapP->map[0], MAXCOLORS);
+    MALLOCARRAY(prColormapP->map[1], MAXCOLORS);
+    MALLOCARRAY(prColormapP->map[2], MAXCOLORS);
+    if (prColormapP->map[0] == NULL || 
+        prColormapP->map[1] == NULL ||
+        prColormapP->map[2] == NULL)
+        pm_error("out of memory");
+
+    return prColormapP;
 }
 
 
 
-static colormap_t*
-make_pr_colormap(colorhist_vector const chv,
-                 int              const colors) {
+static colormap_t *
+makePrColormap(colorhist_vector const chv,
+               unsigned int     const colors) {
 
-    colormap_t* pr_colormapP;
-    int i;
+    colormap_t * prColormapP;
+    unsigned int i;
 
-    pr_colormapP = alloc_pr_colormap( );
+    prColormapP = allocPrColormap();
 
-    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 = 0; i < colors; ++i) {
+        prColormapP->map[0][i] = PPM_GETR(chv[i].color);
+        prColormapP->map[1][i] = PPM_GETG(chv[i].color);
+        prColormapP->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;
+    for ( ; i < MAXCOLORS; ++i) {
+        prColormapP->map[0][i] = 0;
+        prColormapP->map[1][i] = 0;
+        prColormapP->map[2][i] = 0;
+    }
+    return prColormapP;
 }
 
 
 
 static colormap_t *
-make_gray_pr_colormap(void) {
+makeGrayPrColormap(void) {
 
-    colormap_t* pr_colormapP;
-    int i;
+    colormap_t * prColormapP;
+    unsigned int i;
 
-    pr_colormapP = alloc_pr_colormap( );
+    prColormapP = allocPrColormap();
 
-    for ( i = 0; i < MAXCOLORS; ++i )
-    {
-        pr_colormapP->map[0][i] = i;
-        pr_colormapP->map[1][i] = i;
-        pr_colormapP->map[2][i] = i;
+    for (i = 0; i < MAXCOLORS; ++i) {
+        prColormapP->map[0][i] = i;
+        prColormapP->map[1][i] = i;
+        prColormapP->map[2][i] = i;
+    }
+
+    return prColormapP;
+}
+
+
+
+static void
+doRowDepth1(const xel *     const xelrow,
+            unsigned char * const rastRow,
+            unsigned int    const cols,
+            int             const format,
+            xelval          const maxval,
+            colorhash_table const cht,
+            unsigned int *  const lenP) {
+                
+    unsigned int col;
+    int bitcount;
+    unsigned int cursor;
+
+    cursor = 0;
+
+    rastRow[cursor] = 0;
+    bitcount = 7;
+
+    for (col = 0; col < cols; ++col) {
+        switch (PNM_FORMAT_TYPE(format)) {
+        case PPM_TYPE: {
+            xel adjustedXel;
+            int color;
+            if (maxval != 255)
+                PPM_DEPTH(adjustedXel, xelrow[col], maxval, 255 );
+            color = ppm_lookupcolor(cht, &adjustedXel);
+            if (color == -1)
+                pm_error("color not found?!?  "
+                         "col=%u  r=%u g=%u b=%u",
+                         col,
+                         PPM_GETR(adjustedXel),
+                         PPM_GETG(adjustedXel),
+                         PPM_GETB(adjustedXel));
+            if (color)
+                rastRow[cursor] |= 1 << bitcount;
+        } break;
+
+        default: {
+            int const color = PNM_GET1(xelrow[col]);
+            if (!color)
+                rastRow[cursor] |= 1 << bitcount;
+            break;
+        }
+        }
+        --bitcount;
+        if (bitcount < 0) {
+            ++cursor;
+            rastRow[cursor] = 0;
+            bitcount = 7;
+        }
+    }
+    *lenP = cursor;
+}
+
+
+
+static void
+doRowDepth8(const xel *     const xelrow,
+            unsigned char * const rastRow,
+            unsigned int    const cols,
+            int             const format,
+            xelval          const maxval,
+            colorhash_table const cht,
+            unsigned int *  const lenP) {
+
+    unsigned int col;
+    unsigned int cursor;
+
+    for (col = 0, cursor = 0; col < cols; ++col) {
+        int color;  /* color index of pixel or -1 if not in 'cht' */
+
+        switch (PNM_FORMAT_TYPE(format)) {
+        case PPM_TYPE: {
+            xel adjustedXel;
+
+            if (maxval == 255)
+                adjustedXel = xelrow[col];
+            else
+                PPM_DEPTH(adjustedXel, xelrow[col], maxval, 255);
+
+            color = ppm_lookupcolor(cht, &adjustedXel);
+            if (color == -1)
+                pm_error("color not found?!?  "
+                         "col=%u  r=%u g=%u b=%u",
+                         col,
+                         PPM_GETR(adjustedXel),
+                         PPM_GETG(adjustedXel),
+                         PPM_GETB(adjustedXel));
+        } break;
+
+        case PGM_TYPE: {
+            int const rawColor = PNM_GET1(xelrow[col]);
+
+            color = maxval == 255 ? rawColor : rawColor * 255 / maxval;
+
+        } break;
+
+        default:
+            color = PNM_GET1(xelrow[col]);
+        }
+        rastRow[cursor++] = color;
     }
+    *lenP = cursor;
+}
+
 
-    return pr_colormapP;
+
+
+static void
+doRowDepth24(const xel *     const xelrow,
+             unsigned char * const rastRow,
+             unsigned int    const cols,
+             int             const format,
+             xelval          const maxval,
+             unsigned int *  const lenP) {
+
+    /* Since depth is 24, we do NOT have a valid cht. */
+
+    unsigned int col;
+    unsigned int cursor;
+
+    for (col = 0, cursor = 0; col < cols; ++col) {
+        xel adjustedXel;
+
+        if (maxval == 255)
+            adjustedXel = xelrow[col];
+        else
+            PPM_DEPTH(adjustedXel, xelrow[col], maxval, 255);
+
+        rastRow[cursor++] = PPM_GETB(adjustedXel);
+        rastRow[cursor++] = PPM_GETG(adjustedXel);
+        rastRow[cursor++] = PPM_GETR(adjustedXel);
+    }
+    *lenP = cursor;
+}
+
+
+
+static void
+computeRaster(unsigned char * const rastRaster,
+              unsigned int    const lineSize,
+              unsigned int    const depth,
+              unsigned int    const cols,
+              unsigned int    const rows,
+              int             const format,
+              xelval          const maxval,
+              xel **          const xels,
+              colorhash_table const cht) {
+                  
+    unsigned int row;
+    unsigned char * rastRow;
+
+    for (row = 0, rastRow = &rastRaster[0]; row < rows; ++row) {
+        xel * const xelrow = xels[row];
+
+        unsigned int len; /* Number of bytes of rast data added to rastRow[] */
+
+        switch (depth) {
+        case 1:
+            doRowDepth1(xelrow, rastRow, cols, format, maxval, cht, &len);
+            break;
+        case 8:
+            doRowDepth8(xelrow, rastRow, cols, format, maxval, cht, &len);
+            break;
+        case 24:
+            doRowDepth24(xelrow, rastRow, cols, format, maxval, &len);
+            break;
+        default:
+            pm_error("INTERNAL ERROR: impossible depth %u", depth);
+        }
+        {
+            /* Pad out the line (which has a rounded size) with zeroes so
+               the resulting file is repeatable.
+            */
+            unsigned int i;
+            for (i = len; i < lineSize; ++i)
+                rastRow[i] = 0;
+        }
+        rastRow += lineSize;
+    }
 }
 
 
 
 int
-main(int argc, char ** argv) {
+main(int argc, const char ** argv) {
 
-    FILE* ifp;
-    xel** xels;
-    xel* xelrow;
+    FILE * ifP;
+    xel ** xels;
     xel p;
-    register xel* xP;
     colorhist_vector chv;
     colorhash_table cht;
-    colormap_t* pr_colormapP;
-    int argn, pr_type, rows, cols, format, i;
-    int depth, colors, linesize, row;
-    register int col, bitcount;
+    colormap_t * prColormapP;
+    int argn;
+    int prType;
+    int rows, cols;
+    int format;
+    unsigned int depth;
+    int colorCt;
     xelval maxval;
-    struct pixrect* pr;
-    unsigned char* data;
-    register unsigned char* byteP;
-    const char* const usage = "[-standard|-rle] [pnmfile]";
+    struct pixrect * prP;
+    const char * const usage = "[-standard|-rle] [pnmfile]";
 
-    pnm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     argn = 1;
-    pr_type = RT_BYTE_ENCODED;
+    prType = RT_BYTE_ENCODED;
 
     while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
     {
         if ( pm_keymatch( argv[argn], "-standard", 2 ) )
-            pr_type = RT_STANDARD;
+            prType = RT_STANDARD;
         else if ( pm_keymatch( argv[argn], "-rle", 2 ) )
-            pr_type = RT_BYTE_ENCODED;
+            prType = RT_BYTE_ENCODED;
         else
             pm_usage( usage );
         ++argn;
@@ -123,191 +305,96 @@ main(int argc, char ** argv) {
 
     if ( argn != argc )
     {
-        ifp = pm_openr( argv[argn] );
+        ifP = pm_openr( argv[argn] );
         ++argn;
     }
     else
-        ifp = stdin;
+        ifP = stdin;
 
     if ( argn != argc )
         pm_usage( usage );
 
-    xels = pnm_readpnm( ifp, &cols, &rows, &maxval, &format );
+    xels = pnm_readpnm(ifP, &cols, &rows, &maxval, &format);
 
-    pm_close( ifp );
+    pm_close(ifP);
 
     /* Figure out the proper depth and colormap. */
-    switch ( PNM_FORMAT_TYPE(format) )
-    {
+    switch (PNM_FORMAT_TYPE(format)) {
     case PPM_TYPE:
-        pm_message( "computing colormap..." );
-        chv = ppm_computecolorhist( xels, cols, rows, MAXCOLORS, &colors );
-        if ( chv == (colorhist_vector) 0 )
-        {
+        pm_message("computing colormap...");
+        chv = ppm_computecolorhist(xels, cols, rows, MAXCOLORS, &colorCt);
+        if (!chv) {
             pm_message(
-                "Too many colors - proceeding to write a 24-bit non-mapped" );
+                "Too many colors - proceeding to write a 24-bit non-mapped");
             pm_message(
                 "rasterfile.  If you want 8 bits, try doing a 'pnmquant %d'.",
-                MAXCOLORS );
+                MAXCOLORS);
             depth = 24;
-            pr_type = RT_STANDARD;
-            pr_colormapP = (colormap_t*) 0;
-        }
-        else
-        {
-            pm_message( "%d colors found", colors );
-
-            if ( maxval != 255 )
-                for ( i = 0; i < colors; ++i )
-                    PPM_DEPTH( chv[i].color, chv[i].color, maxval, 255 );
-
+            prType = RT_STANDARD;
+            prColormapP = NULL;
+        } else {
+            pm_message("%u colors found", colorCt);
+
+            if (maxval != 255) {
+                unsigned int i;
+                for (i = 0; i < colorCt; ++i)
+                    PPM_DEPTH(chv[i].color, chv[i].color, maxval, 255);
+            }
             /* Force white to slot 0 and black to slot 1, if possible. */
-            PPM_ASSIGN( p, 255, 255, 255 );
-            ppm_addtocolorhist( chv, &colors, MAXCOLORS, &p, 0, 0 );
-            PPM_ASSIGN( p, 0, 0, 0 );
-            ppm_addtocolorhist( chv, &colors, MAXCOLORS, &p, 0, 1 );
-
-            if ( colors == 2 )
-            {
-                /* Monochrome. */
+            PPM_ASSIGN(p, 255, 255, 255);
+            ppm_addtocolorhist(chv, &colorCt, MAXCOLORS, &p, 0, 0);
+            PPM_ASSIGN(p, 0, 0, 0);
+            ppm_addtocolorhist(chv, &colorCt, MAXCOLORS, &p, 0, 1);
+
+            if (colorCt == 2) {
+                /* Monochrome */
                 depth = 1;
-                pr_colormapP = (colormap_t*) 0;
-            }
-            else
-            {
+                prColormapP = NULL;
+            } else {
                 /* Turn the ppm colormap into the appropriate Sun colormap. */
                 depth = 8;
-                pr_colormapP = make_pr_colormap( chv, colors );
+                prColormapP = makePrColormap(chv, colorCt);
             }
-            cht = ppm_colorhisttocolorhash( chv, colors );
-            ppm_freecolorhist( chv );
+            cht = ppm_colorhisttocolorhash(chv, colorCt);
+            ppm_freecolorhist(chv);
         }
 
         break;
 
     case PGM_TYPE:
         depth = 8;
-        pr_colormapP = make_gray_pr_colormap( );
+        prColormapP = makeGrayPrColormap();
         break;
 
     default:
         depth = 1;
-        pr_colormapP = (colormap_t*) 0;
+        prColormapP = NULL;
         break;
     }
 
-    if ( maxval > 255 && depth != 1 )
+    if (maxval > 255 && depth != 1)
         pm_message(
-            "maxval is not 255 - automatically rescaling colors" );
+            "maxval is not 255 - automatically rescaling colors");
     
     /* Allocate space for the Sun-format image. */
-    if ( (pr = mem_create(cols, rows, depth)) == (struct pixrect*) 0 )
-        pm_error( "unable to create new pixrect" );
-    data = ( (struct mpr_data*) pr->pr_data )->md_image;
-    linesize = ( (struct mpr_data*) pr->pr_data )->md_linebytes;
-
-    /* And compute the Sun image.  The variables at this point are:
-    **   cht is null or not
-    **   depth is 1, 8, or 24
-    */
-    for ( row = 0; row < rows; ++row )
-    {
-        xelrow = xels[row];
-        byteP = data;
-        switch ( depth )
-        {
-        case 1:
-            *byteP = 0;
-            bitcount = 7;
-            for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-            {
-                register int color;
-
-                switch ( PNM_FORMAT_TYPE(format) )
-                {
-                case PPM_TYPE:
-                    if ( maxval != 255 )
-                        PPM_DEPTH( *xP, *xP, maxval, 255 );
-                    color = ppm_lookupcolor( cht, xP );
-                    if ( color == -1 )
-                        pm_error(
-                            "color not found?!?  row=%d col=%d  r=%d g=%d b=%d",
-                            row, col, PPM_GETR(*xP), PPM_GETG(*xP),
-                            PPM_GETB(*xP) );
-                    if ( color )
-                        *byteP |= 1 << bitcount;
-                    break;
-
-                default:
-                    color = PNM_GET1( *xP );
-                    if ( ! color )
-                        *byteP |= 1 << bitcount;
-                    break;
-                }
-                --bitcount;
-                if ( bitcount < 0 )
-                {
-                    ++byteP;
-                    *byteP = 0;
-                    bitcount = 7;
-                }
-            }
-            break;
-
-        case 8:
-            for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-            {
-                register int color;
-
-                switch ( PNM_FORMAT_TYPE(format) )
-                {
-                case PPM_TYPE:
-                    if ( maxval != 255 )
-                        PPM_DEPTH( *xP, *xP, maxval, 255 );
-                    color = ppm_lookupcolor( cht, xP );
-                    if ( color == -1 )
-                        pm_error(
-                            "color not found?!?  row=%d col=%d  r=%d g=%d b=%d",
-                            row, col, PPM_GETR(*xP), PPM_GETG(*xP),
-                            PPM_GETB(*xP) );
-                    break;
-
-                case PGM_TYPE:
-                    color = PNM_GET1( *xP );
-                    if ( maxval != 255 )
-                        color = color * 255 / maxval;
-                    break;
-
-                default:
-                    color = PNM_GET1( *xP );
-                }
-                *byteP++ = color;
-            }
-            break;
+    prP = mem_create(cols, rows, depth);
+    if (!prP)
+        pm_error("unable to create new pixrect");
 
-        case 24:
-            /* If depth is 24, we do NOT have a valid cht. */
-            for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-            {
-                if ( maxval != 255 )
-                    PPM_DEPTH( *xP, *xP, maxval, 255 );
-                *byteP++ = PPM_GETB( *xP );
-                *byteP++ = PPM_GETG( *xP );
-                *byteP++ = PPM_GETR( *xP );
-            }
-            break;
+    computeRaster(prP->pr_data->md_image,
+                  prP->pr_data->md_linebytes,
+                  depth,
+                  cols, rows, format, maxval, xels, cht);
 
-        default:
-            pm_error( "can't happen" );
-        }
-        data += linesize;
-    }
-    pnm_freearray( xels, rows );
+    pnm_freearray(xels, rows);
 
-    /* Finally, write the sucker out. */
-    if ( pr_dump( pr, stdout, pr_colormapP, pr_type, 0 ) == PIX_ERR )
-        pm_error( "error writing rasterfile" );
+    {
+        int rc;
 
-    exit( 0 );
+        rc = pr_dump(prP, stdout, prColormapP, prType, 0);
+        if (rc == PIX_ERR )
+            pm_error("error writing rasterfile");
+    }
+    return 0;
 }
 
diff --git a/converter/other/pnmtosgi.c b/converter/other/pnmtosgi.c
index 472b5197..cc57349f 100644
--- a/converter/other/pnmtosgi.c
+++ b/converter/other/pnmtosgi.c
@@ -13,342 +13,349 @@
 ** implied warranty.
 **
 ** 29Jan94: first version
+
+** Feb 2010 afu
+** Added dimension check to prevent short int from overflowing
 */
+
+#include <assert.h>
+
 #include "pnm.h"
 #include "sgi.h"
 #include "mallocvar.h"
+#include "runlength.h"
+
 
 /*#define DEBUG*/
 
-typedef short       ScanElem;
+typedef uint16_t       ScanElem;
 typedef struct {
     ScanElem *  data;
     long        length;
 } ScanLine;
 
-/* prototypes */
-static void put_big_short ARGS((short s));
-static void put_big_long ARGS((long l));
-#define put_byte(b)     (void)(putc((unsigned char)(b), stdout))
-static void put_short_as_byte ARGS((short s));
-static void write_table  ARGS((long *table, int tabsize));
-static void write_channels ARGS((int cols, int rows, int channels, void (*put) ARGS((short)) ));
-static long * build_channels ARGS((FILE *ifp, int cols, int rows, xelval maxval, int format, int bpc, int channels));
-static ScanElem *compress ARGS((ScanElem *temp, int row, int rows, int cols, int chan_no, long *table, int bpc));
-static int rle_compress ARGS((ScanElem *inbuf, int cols));
-
-#define WORSTCOMPR(x)   (2*(x) + 2)
-
-
 #define MAXVAL_BYTE     255
 #define MAXVAL_WORD     65535
+#define INT16MAX        32767
 
 static char storage = STORAGE_RLE;
 static ScanLine * channel[3];
-static ScanElem * rletemp;
 static xel * pnmrow;
 
 
-static void
-write_header(int const cols, 
-             int const rows, 
-             xelval const maxval, 
-             int const bpc, 
-             int const dimensions, 
-             int const channels, 
-             const char * const imagename)
-{
-    int i;
-
-#ifdef DEBUG
-    pm_message("writing header");
-#endif
-
-    put_big_short(SGI_MAGIC);
-    put_byte(storage);
-    put_byte((char)bpc);
-    put_big_short(dimensions);
-    put_big_short(cols);
-    put_big_short(rows);
-    put_big_short(channels);
-    put_big_long(0);                /* PIXMIN */
-    put_big_long(maxval);           /* PIXMAX */
-    for( i = 0; i < 4; i++ )
-        put_byte(0);
-    for( i = 0; i < 79 && imagename[i] != '\0'; i++ )
-        put_byte(imagename[i]);
-    for(; i < 80; i++ )
-        put_byte(0);
-    put_big_long(CMAP_NORMAL);
-    for( i = 0; i < 404; i++ )
-        put_byte(0);
-}
 
+#define putByte(b) (void)(putc((unsigned char)(b), stdout))
 
 
-int
-main(argc, argv)
-    int argc;
-    char *argv[];
-{
-    FILE *ifp;
-    int argn;
-    const char * const usage = "[-verbatim|-rle] [-imagename <name>] [pnmfile]";
-    int cols, rows, format;
-    xelval maxval, newmaxval;
-    const char *imagename = "no name";
-    int bpc, dimensions, channels;
-    long *table = NULL;
+static void
+putBigShort(short const s) {
 
-    pnm_init(&argc, argv);
+    if (pm_writebigshort(stdout, s ) == -1)
+        pm_error( "write error" );
+}
 
-    argn = 1;
-    while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) {
-        if( pm_keymatch(argv[argn], "-verbatim", 2) )
-            storage = STORAGE_VERBATIM;
-        else
-        if( pm_keymatch(argv[argn], "-rle", 2) )
-            storage = STORAGE_RLE;
-        else
-        if( pm_keymatch(argv[argn], "-imagename", 2) ) {
-            if( ++argn >= argc )
-                pm_usage(usage);
-            imagename = argv[argn];
-        }
-        else
-            pm_usage(usage);
-        ++argn;
-    }
 
-    if( argn < argc ) {
-        ifp = pm_openr( argv[argn] );
-        argn++;
-    }
-    else
-        ifp = stdin;
 
-    if( argn != argc )
-        pm_usage(usage);
+static void
+putBigLong(long const l) {
 
-    pnm_readpnminit(ifp, &cols, &rows, &maxval, &format);
-    pnmrow = pnm_allocrow(cols);
+    if (pm_writebiglong( stdout, l ) == -1)
+        pm_error( "write error" );
+}
 
-    switch( PNM_FORMAT_TYPE(format) ) {
-        case PBM_TYPE:
-            newmaxval = PGM_MAXMAXVAL;
-            pm_message("promoting PBM to PGM");
-        case PGM_TYPE:
-            newmaxval = maxval;
-            dimensions = 2; channels = 1;
-            break;
-        case PPM_TYPE:
-            newmaxval = maxval;
-            dimensions = 3; channels = 3;
-            break;
-        default:
-            pm_error("can\'t happen");
-    }
-    if( newmaxval <= MAXVAL_BYTE )
-        bpc = 1;
-    else if( newmaxval <= MAXVAL_WORD )
-        bpc = 2;
-    else
-        pm_error("maxval too large - try using \"pnmdepth %d\"", MAXVAL_WORD);
 
-    table = build_channels(ifp, cols, rows, newmaxval, format, bpc, channels);
-    pnm_freerow(pnmrow);
-    pm_close(ifp);
 
-    write_header(cols, rows, newmaxval, bpc, dimensions, channels, imagename);
-    if( table )
-        write_table(table, rows * channels);
-    if( bpc == 1 )
-        write_channels(cols, rows, channels, put_short_as_byte);
-    else
-        write_channels(cols, rows, channels, put_big_short);
+static void
+putShortAsByte(short const s) {
 
-    exit(0);
+    putByte((unsigned char)s);
 }
 
 
+
 static void
-write_table(table, tabsize)
-    long *table;
-    int tabsize;
-{
-    int i;
-    long offset;
+writeTable(long *       const table,
+           unsigned int const tabsize) {
 
-#ifdef DEBUG
-    pm_message("writing table");
-#endif
+    unsigned int i;
+    unsigned long offset;
 
     offset = HeaderSize + tabsize * 8;
-    for( i = 0; i < tabsize; i++ ) {
-        put_big_long(offset);
+
+    for (i = 0; i < tabsize; ++i) {
+        putBigLong(offset);
         offset += table[i];
     }
-    for( i = 0; i < tabsize; i++ )
-        put_big_long(table[i]);
+    for (i = 0; i < tabsize; ++i)
+        putBigLong(table[i]);
 }
 
 
+
 static void
-write_channels(cols, rows, channels, put)
-    int cols, rows, channels;
-    void (*put) ARGS((short));
-{
-    int i, row, col;
-
-#ifdef DEBUG
-    pm_message("writing image data");
-#endif
-
-    for( i = 0; i < channels; i++ ) {
-        for( row = 0; row < rows; row++ ) {
-            for( col = 0; col < channel[i][row].length; col++ ) {
+writeChannels(unsigned int const cols,
+              unsigned int const rows,
+              unsigned int const channels,
+              void (*put) (short)) {
+
+    unsigned int i;
+
+    for (i = 0; i < channels; ++i) {
+        unsigned int row;
+        for (row = 0; row < rows; ++row) {
+            unsigned int col;
+            for (col = 0; col < channel[i][row].length; ++col) {
                 (*put)(channel[i][row].data[col]);
             }
+            pm_rlenc_freebuf((unsigned char *) channel[i][row].data);
         }
     }
 }
 
-static void
-put_big_short(short s)
-{
-    if ( pm_writebigshort( stdout, s ) == -1 )
-        pm_error( "write error" );
-}
 
 
-static void
-put_big_long(l)
-    long l;
-{
-    if ( pm_writebiglong( stdout, l ) == -1 )
-        pm_error( "write error" );
+static ScanElem *
+compress(ScanElem *   const tempArg,
+         unsigned int const row,
+         unsigned int const rows,
+         unsigned int const cols,
+         unsigned int const chanNum,
+         long *       const table,
+         unsigned int const bpc) {
+/*----------------------------------------------------------------------------
+   Compress a row, putting results in global 'channel' array, in newly
+   allocated storage (which Caller must free).
+
+   Except that if the compression is null compression, we make 'channel'
+   point to existing storage, which Caller must not free.  Yuck.
+-----------------------------------------------------------------------------*/
+    ScanElem * retval;
+
+    switch (storage) {
+    case STORAGE_VERBATIM:
+        channel[chanNum][row].length = cols;
+        channel[chanNum][row].data = tempArg;
+        MALLOCARRAY_NOFAIL(retval, cols);
+        break;
+    case STORAGE_RLE: {
+        unsigned int const tabrow = chanNum * rows + row;
+
+        unsigned int len;
+        size_t lenBytes;
+        ScanElem * p;
+
+        pm_rlenc_allocoutbuf((unsigned char **) &p, cols, PM_RLE_SGI16);
+
+        pm_rlenc_compressword(tempArg,(unsigned char *) p, PM_RLE_SGI16,
+                              cols, &lenBytes);
+
+        assert((unsigned)lenBytes == lenBytes);
+            /* Guaranteed by pm_rlenc_compressword() */
+
+        len = lenBytes / 2;  /* sizeof(ScanElem) */
+        channel[chanNum][row].length = len;
+        REALLOCARRAY(p, len);   /* reclaim some space */
+        if (p == NULL)
+            pm_error("realloc failure while reclaiming memory space "
+                     "for output");
+        channel[chanNum][row].data = p;
+        table[tabrow] = len * bpc;
+        retval = tempArg;
+    } break;
+    default:
+        pm_error("unknown storage type - can't happen");
+    }
+    return retval;
 }
 
 
-static void
-put_short_as_byte(short s)
-{
-    put_byte((unsigned char)s);
-}
-
 
 static long *
-build_channels(FILE *ifp, int cols, int rows, xelval maxval, 
-               int format, int bpc, int channels)
-{
-    int i, row, col, sgirow;
-    long *table = NULL;
-    ScanElem *temp;
-
-#ifdef DEBUG
-    pm_message("building channels");
-#endif
-
-    if( storage != STORAGE_VERBATIM ) {
+buildChannels(FILE *       const ifP,
+              unsigned int const cols,
+              unsigned int const rows,
+              xelval       const maxval,
+              int          const format,
+              unsigned int const bpc,
+              unsigned int const channels) {
+
+    unsigned int row;
+    unsigned int sgirow;
+    long * table;
+    ScanElem * temp;
+
+    if (storage != STORAGE_VERBATIM) {
         MALLOCARRAY_NOFAIL(table, channels * rows);
-        MALLOCARRAY_NOFAIL(rletemp, WORSTCOMPR(cols));
-    }
+    } else
+        table = NULL;
+
     MALLOCARRAY_NOFAIL(temp, cols);
 
-    for( i = 0; i < channels; i++ )
-        MALLOCARRAY_NOFAIL(channel[i], rows);
+    {
+        unsigned int i;
+        for (i = 0; i < channels; ++i)
+            MALLOCARRAY_NOFAIL(channel[i], rows);
+    }
 
-    for( row = 0, sgirow = rows-1; row < rows; row++, sgirow-- ) {
-        pnm_readpnmrow(ifp, pnmrow, cols, maxval, format);
-        if( channels == 1 ) {
-            for( col = 0; col < cols; col++ )
+    for (row = 0, sgirow = rows-1; row < rows; ++row, --sgirow) {
+        pnm_readpnmrow(ifP, pnmrow, cols, maxval, format);
+        if (channels == 1) {
+            unsigned int col;
+            for (col = 0; col < cols; ++col)
                 temp[col] = (ScanElem)PNM_GET1(pnmrow[col]);
             temp = compress(temp, sgirow, rows, cols, 0, table, bpc);
-        }
-        else {
-            for( col = 0; col < cols; col++ )
+        } else {
+            unsigned int col;
+            for (col = 0; col < cols; ++col)
                 temp[col] = (ScanElem)PPM_GETR(pnmrow[col]);
             temp = compress(temp, sgirow, rows, cols, 0, table, bpc);
-            for( col = 0; col < cols; col++ )
+            for (col = 0; col < cols; ++col)
                 temp[col] = (ScanElem)PPM_GETG(pnmrow[col]);
             temp = compress(temp, sgirow, rows, cols, 1, table, bpc);
-            for( col = 0; col < cols; col++ )
+            for (col = 0; col < cols; ++col)
                 temp[col] = (ScanElem)PPM_GETB(pnmrow[col]);
             temp = compress(temp, sgirow, rows, cols, 2, table, bpc);
         }
     }
 
     free(temp);
-    if( table )
-        free(rletemp);
     return table;
 }
 
 
-static ScanElem *
-compress(temp, row, rows, cols, chan_no, table, bpc)
-    ScanElem *temp;
-    int row, rows, cols, chan_no;
-    long *table;
-    int bpc;
-{
-    int len, i, tabrow;
-    ScanElem *p;
-
-    switch( storage ) {
-        case STORAGE_VERBATIM:
-            channel[chan_no][row].length = cols;
-            channel[chan_no][row].data = temp;
-            MALLOCARRAY_NOFAIL(temp, cols);
+
+static void
+writeHeader(unsigned int const cols, 
+            unsigned int const rows, 
+            xelval       const maxval, 
+            unsigned int const bpc, 
+            unsigned int const dimensions, 
+            unsigned int const channels, 
+            const char * const imagename) {
+
+    unsigned int i;
+
+    putBigShort(SGI_MAGIC);
+    putByte(storage);
+    putByte((char)bpc);
+    putBigShort(dimensions);
+    putBigShort(cols);
+    putBigShort(rows);
+    putBigShort(channels);
+    putBigLong(0);                /* PIXMIN */
+    putBigLong(maxval);           /* PIXMAX */
+
+    for(i = 0; i < 4; ++i)
+        putByte(0);
+
+    for (i = 0; i < 79 && imagename[i] != '\0'; ++i)
+        putByte(imagename[i]);
+
+    for(; i < 80; ++i)
+        putByte(0);
+
+    putBigLong(CMAP_NORMAL);
+
+    for (i = 0; i < 404; ++i)
+        putByte(0);
+}
+
+
+
+int
+main(int argc,char * argv[]) {
+
+    FILE * ifP;
+    int argn;
+    const char * const usage = "[-verbatim|-rle] [-imagename <name>] [pnmfile]";
+    int cols, rows;
+    int format;
+    xelval maxval, newmaxval;
+    const char * imagename;
+    unsigned int bpc;
+    unsigned int dimensions;
+    unsigned int channels;
+    long * table;
+
+    pnm_init(&argc, argv);
+
+    imagename = "no name";  /* default value */
+    argn = 1;
+    while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) {
+        if( pm_keymatch(argv[argn], "-verbatim", 2) )
+            storage = STORAGE_VERBATIM;
+        else
+        if( pm_keymatch(argv[argn], "-rle", 2) )
+            storage = STORAGE_RLE;
+        else
+        if( pm_keymatch(argv[argn], "-imagename", 2) ) {
+            if( ++argn >= argc )
+                pm_usage(usage);
+            imagename = argv[argn];
+        }
+        else
+            pm_usage(usage);
+        ++argn;
+    }
+
+    if( argn < argc ) {
+        ifP = pm_openr( argv[argn] );
+        argn++;
+    }
+    else
+        ifP = stdin;
+
+    if( argn != argc )
+        pm_usage(usage);
+
+    pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
+
+    if (rows > INT16MAX || cols > INT16MAX)
+        pm_error ("Input image is too large.");
+
+    pnmrow = pnm_allocrow(cols);
+    
+    switch (PNM_FORMAT_TYPE(format)) {
+        case PBM_TYPE:
+            pm_message("promoting PBM to PGM");
+            newmaxval = PGM_MAXMAXVAL;
+        case PGM_TYPE:
+            newmaxval = maxval;
+            dimensions = 2;
+            channels = 1;
             break;
-        case STORAGE_RLE:
-            tabrow = chan_no * rows + row;
-            len = rle_compress(temp, cols);    /* writes result into rletemp */
-            channel[chan_no][row].length = len;
-            MALLOCARRAY(p, len);
-            channel[chan_no][row].data = p;
-            for( i = 0; i < len; i++, p++ )
-                *p = rletemp[i];
-            table[tabrow] = len * bpc;
+        case PPM_TYPE:
+            newmaxval = maxval;
+            dimensions = 3;
+            channels = 3;
             break;
         default:
-            pm_error("unknown storage type - can\'t happen");
+            pm_error("can\'t happen");
     }
-    return temp;
-}
+    if (newmaxval <= MAXVAL_BYTE)
+        bpc = 1;
+    else if (newmaxval <= MAXVAL_WORD)
+        bpc = 2;
+    else
+        pm_error("maxval too large - try using \"pnmdepth %u\"", MAXVAL_WORD);
 
+    table = buildChannels(ifP, cols, rows, newmaxval, format, bpc, channels);
 
-/*
-slightly modified RLE algorithm from ppmtoilbm.c
-written by Robert A. Knop (rknop@mop.caltech.edu)
-*/
-static int
-rle_compress(inbuf, size)
-    ScanElem *inbuf;
-    int size;
-{
-    int in, out, hold, count;
-    ScanElem *outbuf = rletemp;
-
-    in=out=0;
-    while( in<size ) {
-        if( (in<size-1) && (inbuf[in]==inbuf[in+1]) ) {     /*Begin replicate run*/
-            for( count=0,hold=in; in<size && inbuf[in]==inbuf[hold] && count<127; in++,count++)
-                ;
-            outbuf[out++]=(ScanElem)(count);
-            outbuf[out++]=inbuf[hold];
-        }
-        else {  /*Do a literal run*/
-            hold=out; out++; count=0;
-            while( ((in>=size-2)&&(in<size)) || ((in<size-2) && ((inbuf[in]!=inbuf[in+1])||(inbuf[in]!=inbuf[in+2]))) ) {
-                outbuf[out++]=inbuf[in++];
-                if( ++count>=127 )
-                    break;
-            }
-            outbuf[hold]=(ScanElem)(count | 0x80);
-        }
-    }
-    outbuf[out++] = (ScanElem)0;     /* terminator */
-    return(out);
+    pnm_freerow(pnmrow);
+
+    pm_close(ifP);
+
+    writeHeader(cols, rows, newmaxval, bpc, dimensions, channels, imagename);
+
+    if (table)
+        writeTable(table, rows * channels);
+
+    if (bpc == 1)
+        writeChannels(cols, rows, channels, putShortAsByte);
+    else
+        writeChannels(cols, rows, channels, putBigShort);
+
+    return 0;
 }
 
+
diff --git a/converter/other/pnmtoxwd.c b/converter/other/pnmtoxwd.c
index b6439d28..eda2ee8f 100644
--- a/converter/other/pnmtoxwd.c
+++ b/converter/other/pnmtoxwd.c
@@ -20,11 +20,11 @@
 #include "x11wd.h"
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* Filespecs of input file */
+    const char * inputFilename;  /* Filename of input file */
     unsigned int pseudodepth;
     unsigned int directcolor;
 };
@@ -34,7 +34,7 @@ struct cmdlineInfo {
 static void 
 parseCommandLine(int argc, 
                  char ** argv, 
-                 struct cmdlineInfo  * const cmdlineP) {
+                 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.  
@@ -46,7 +46,7 @@ parseCommandLine(int argc,
    was passed to us as the argv array.  We also trash *argv.
 --------------------------------------------------------------------------*/
     optEntry *option_def;
-    /* Instructions to optParseOptions3 on how to parse our options. */
+    /* Instructions to pm_optParseOptions3 on how to parse our options. */
     optStruct3 opt;
 
     unsigned int option_def_index;
@@ -63,7 +63,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 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
     /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!depthSpec)
@@ -78,12 +78,12 @@ parseCommandLine(int argc,
     }
 
     if (argc-1 == 0) 
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFilename = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
                  "specified %d", argc-1);
     else
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFilename = argv[1];
 }
 
 
@@ -410,7 +410,7 @@ writeRaster(FILE *           const ofP,
 int
 main(int argc, char * argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE* ifP;
     xel ** xels;
     int rows, cols, format, colors;
@@ -426,7 +426,7 @@ main(int argc, char * argv[]) {
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFilename);
 
     xels = pnm_readpnm(ifP, &cols, &rows, &maxval, &format);
     xmaxval = (1 << cmdline.pseudodepth) - 1;
@@ -473,13 +473,13 @@ main(int argc, char * argv[]) {
         }
     }
 
-    if (streq(cmdline.inputFilespec, "-"))
+    if (streq(cmdline.inputFilename, "-"))
         dumpname = "stdin";
     else {
-        if (strlen(cmdline.inputFilespec) > XWDVAL_MAX - sizeof(h11) - 1)
+        if (strlen(cmdline.inputFilename) > XWDVAL_MAX - sizeof(h11) - 1)
             pm_error("Input file name is ridiculously long.");
         else
-            dumpname = cmdline.inputFilespec;
+            dumpname = cmdline.inputFilename;
     }
 
     setupX11Header(&h11, dumpname, cols, rows, format, 
diff --git a/converter/other/ppmtopgm.c b/converter/other/ppmtopgm.c
index 86e7ae6a..e20c5660 100644
--- a/converter/other/ppmtopgm.c
+++ b/converter/other/ppmtopgm.c
@@ -36,9 +36,9 @@ convertRaster(FILE *       const ifP,
                 outputRow[col] = (gray) ppm_fastlumin(inputRow[col]);
         } else {
             /* Can't use fast approximation, so fall back on floats. */
-            int col;
+            unsigned int col;
             for (col = 0; col < cols; ++col) 
-                outputRow[col] = (gray) (PPM_LUMIN(inputRow[col]) + 0.5);
+                outputRow[col] = ppm_luminosity(inputRow[col]);
         }
         pgm_writepgmrow(ofP, outputRow, cols, maxval, 0);
     }
diff --git a/converter/other/pstopnm.c b/converter/other/pstopnm.c
index 0e91ad9c..19b1630a 100644
--- a/converter/other/pstopnm.c
+++ b/converter/other/pstopnm.c
@@ -18,6 +18,7 @@
 #define _XOPEN_SOURCE 500  
     /* Make sure fdopen() is in stdio.h and strdup() is in string.h */
 
+#include <assert.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdlib.h>
@@ -27,12 +28,15 @@
 #include <sys/stat.h>
 
 #include "pm_c_util.h"
+#include "mallocvar.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "nstring.h"
 
-enum orientation {PORTRAIT, LANDSCAPE, UNSPECIFIED};
-struct box {
+static bool verbose;
+
+enum Orientation {PORTRAIT, LANDSCAPE, UNSPECIFIED};
+struct Box {
     /* Description of a rectangle within an image; all coordinates 
        measured in points (1/72") with lower left corner of page being the 
        origin.
@@ -44,15 +48,28 @@ struct box {
     int ury;  /* upper right Y coord */
 };
 
-struct cmdlineInfo {
+struct Dimensions {
+/*----------------------------------------------------------------------------
+  Horizontal and vertical dimensions of something, both in pixels and
+  spatial distance (points).
+
+  Sizes are in pixels.  Resolutions are in dots per inch (pixels per inch);
+-----------------------------------------------------------------------------*/
+    unsigned int xsize;
+    unsigned int ysize;
+    unsigned int xres;
+    unsigned int yres;
+};
+
+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;  /* Filespecs of input files */
+    const char * inputFileName;  /* Names of input files */
     unsigned int forceplain;
-    struct box extract_box;
+    struct Box extractBox;
     unsigned int nocrop;
-    unsigned int format_type;
+    unsigned int formatType;
     unsigned int verbose;
     float xborder;
     unsigned int xmax;
@@ -61,30 +78,34 @@ struct cmdlineInfo {
     unsigned int ymax;
     unsigned int ysize;  /* zero means unspecified */
     unsigned int dpi;    /* zero means unspecified */
-    enum orientation orientation;
-    unsigned int goto_stdout;
+    enum Orientation orientation;
+    unsigned int stdoutSpec;
+    unsigned int textalphabits;
 };
 
 
 static void
 parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-        /* Instructions to optParseOptions3 on how to parse our options.
+    optEntry * option_def;
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
     unsigned int option_def_index;
 
-    unsigned int pbm_opt, pgm_opt, ppm_opt;
-    unsigned int portrait_opt, landscape_opt;
+    unsigned int pbmOpt, pgmOpt, ppmOpt;
+    unsigned int portraitOpt, landscapeOpt;
     float llx, lly, urx, ury;
     unsigned int llxSpec, llySpec, urxSpec, urySpec;
     unsigned int xmaxSpec, ymaxSpec, xsizeSpec, ysizeSpec, dpiSpec;
+    unsigned int textalphabitsSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
     
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0, "forceplain", OPT_FLAG,  NULL, &cmdlineP->forceplain,     0);
@@ -93,9 +114,9 @@ parseCommandLine(int argc, char ** argv,
     OPTENT3(0, "urx",        OPT_FLOAT, &urx, &urxSpec,                  0);
     OPTENT3(0, "ury",        OPT_FLOAT, &ury, &urySpec,                  0);
     OPTENT3(0, "nocrop",     OPT_FLAG,  NULL, &cmdlineP->nocrop,         0);
-    OPTENT3(0, "pbm",        OPT_FLAG,  NULL, &pbm_opt,                  0);
-    OPTENT3(0, "pgm",        OPT_FLAG,  NULL, &pgm_opt,                  0);
-    OPTENT3(0, "ppm",        OPT_FLAG,  NULL, &ppm_opt,                  0);
+    OPTENT3(0, "pbm",        OPT_FLAG,  NULL, &pbmOpt ,                  0);
+    OPTENT3(0, "pgm",        OPT_FLAG,  NULL, &pgmOpt,                   0);
+    OPTENT3(0, "ppm",        OPT_FLAG,  NULL, &ppmOpt,                   0);
     OPTENT3(0, "verbose",    OPT_FLAG,  NULL, &cmdlineP->verbose,        0);
     OPTENT3(0, "xborder",    OPT_FLOAT, &cmdlineP->xborder, NULL,        0);
     OPTENT3(0, "xmax",       OPT_UINT,  &cmdlineP->xmax, &xmaxSpec,      0);
@@ -104,9 +125,11 @@ parseCommandLine(int argc, char ** argv,
     OPTENT3(0, "ymax",       OPT_UINT,  &cmdlineP->ymax, &ymaxSpec,      0);
     OPTENT3(0, "ysize",      OPT_UINT,  &cmdlineP->ysize, &ysizeSpec,    0);
     OPTENT3(0, "dpi",        OPT_UINT,  &cmdlineP->dpi, &dpiSpec,        0);
-    OPTENT3(0, "portrait",   OPT_FLAG,  NULL, &portrait_opt,             0);
-    OPTENT3(0, "landscape",  OPT_FLAG,  NULL, &landscape_opt,            0);
-    OPTENT3(0, "stdout",     OPT_FLAG,  NULL, &cmdlineP->goto_stdout,    0);
+    OPTENT3(0, "portrait",   OPT_FLAG,  NULL, &portraitOpt,              0);
+    OPTENT3(0, "landscape",  OPT_FLAG,  NULL, &landscapeOpt,             0);
+    OPTENT3(0, "stdout",     OPT_FLAG,  NULL, &cmdlineP->stdoutSpec,     0);
+    OPTENT3(0, "textalphabits", OPT_UINT,
+            &cmdlineP->textalphabits,  &textalphabitsSpec, 0);
 
     /* Set the defaults */
     cmdlineP->xborder = cmdlineP->yborder = 0.1;
@@ -115,7 +138,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (xmaxSpec) {
@@ -142,38 +165,38 @@ parseCommandLine(int argc, char ** argv,
     } else 
         cmdlineP->ysize = 0;
 
-    if (portrait_opt & !landscape_opt)
+    if (portraitOpt && !landscapeOpt)
         cmdlineP->orientation = PORTRAIT;
-    else if (!portrait_opt & landscape_opt)
+    else if (!portraitOpt && landscapeOpt)
         cmdlineP->orientation = LANDSCAPE;
-    else if (!portrait_opt & !landscape_opt)
+    else if (!portraitOpt && !landscapeOpt)
         cmdlineP->orientation = UNSPECIFIED;
     else
         pm_error("Cannot specify both -portrait and -landscape options");
 
-    if (pbm_opt)
-        cmdlineP->format_type = PBM_TYPE;
-    else if (pgm_opt)
-        cmdlineP->format_type = PGM_TYPE;
-    else if (ppm_opt)
-        cmdlineP->format_type = PPM_TYPE;
+    if (pbmOpt)
+        cmdlineP->formatType = PBM_TYPE;
+    else if (pgmOpt)
+        cmdlineP->formatType = PGM_TYPE;
+    else if (ppmOpt)
+        cmdlineP->formatType = PPM_TYPE;
     else
-        cmdlineP->format_type = PPM_TYPE;
+        cmdlineP->formatType = PPM_TYPE;
 
     /* If any one of the 4 bounding box coordinates is given on the
        command line, we default any of the 4 that aren't.  
     */
     if (llxSpec || llySpec || urxSpec || urySpec) {
-        if (!llxSpec) cmdlineP->extract_box.llx = 72;
-        else cmdlineP->extract_box.llx = llx * 72;
-        if (!llySpec) cmdlineP->extract_box.lly = 72;
-        else cmdlineP->extract_box.lly = lly * 72;
-        if (!urxSpec) cmdlineP->extract_box.urx = 540;
-        else cmdlineP->extract_box.urx = urx * 72;
-        if (!urySpec) cmdlineP->extract_box.ury = 720;
-        else cmdlineP->extract_box.ury = ury * 72;
+        if (!llxSpec) cmdlineP->extractBox.llx = 72;
+        else cmdlineP->extractBox.llx = llx * 72;
+        if (!llySpec) cmdlineP->extractBox.lly = 72;
+        else cmdlineP->extractBox.lly = lly * 72;
+        if (!urxSpec) cmdlineP->extractBox.urx = 540;
+        else cmdlineP->extractBox.urx = urx * 72;
+        if (!urySpec) cmdlineP->extractBox.ury = 720;
+        else cmdlineP->extractBox.ury = ury * 72;
     } else {
-        cmdlineP->extract_box.llx = -1;
+        cmdlineP->extractBox.llx = -1;
     }
 
     if (dpiSpec) {
@@ -185,79 +208,93 @@ parseCommandLine(int argc, char ** argv,
     if (dpiSpec && xsizeSpec + ysizeSpec + xmaxSpec + ymaxSpec > 0)
         pm_error("You may not specify both size options and -dpi");
 
+    if (textalphabitsSpec) {
+        if (cmdlineP->textalphabits != 1 && cmdlineP->textalphabits != 2
+            && cmdlineP->textalphabits != 4) {
+            /* Pstopnm won't take this value, and we don't want to inflict
+               a Pstopnm failure error message on the user.
+            */
+            pm_error("Valid values for -textalphabits are 1, 2, and 4.  "
+                     "You specified %u", cmdlineP->textalphabits );
+        }
+    } else
+        cmdlineP->textalphabits = 4;
+
     if (argc-1 == 0)
-        cmdlineP->input_filespec = "-";  /* stdin */
+        cmdlineP->inputFileName = "-";  /* stdin */
     else if (argc-1 == 1)
-        cmdlineP->input_filespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
     else 
         pm_error("Too many arguments (%d).  "
-                 "Only need one: the Postscript filespec", argc-1);
+                 "Only need one: the Postscript file name", argc-1);
+
+    free(option_def);
 }
 
 
 
 static void
-addPsToFilespec(char          const orig_filespec[],
-                const char ** const new_filespec_p,
-                bool          const verbose) {
+addPsToFileName(char          const origFileName[],
+                const char ** const newFileNameP) {
 /*----------------------------------------------------------------------------
-   If orig_filespec[] does not name an existing file, but the same
+   If origFileName[] does not name an existing file, but the same
    name with ".ps" added to the end does, return the name with the .ps
-   attached.  Otherwise, just return orig_filespec[].
+   attached.  Otherwise, just return origFileName[].
 
    Return the name in newly malloc'ed storage, pointed to by
-   *new_filespec_p.
+   *newFileNameP.
 -----------------------------------------------------------------------------*/
     struct stat statbuf;
-    int stat_rc;
+    int statRc;
 
-    stat_rc = lstat(orig_filespec, &statbuf);
+    statRc = lstat(origFileName, &statbuf);
     
-    if (stat_rc == 0)
-        *new_filespec_p = strdup(orig_filespec);
+    if (statRc == 0)
+        *newFileNameP = strdup(origFileName);
     else {
-        const char *filespec_plus_ps;
+        const char * fileNamePlusPs;
 
-        asprintfN(&filespec_plus_ps, "%s.ps", orig_filespec);
+        pm_asprintf(&fileNamePlusPs, "%s.ps", origFileName);
 
-        stat_rc = lstat(filespec_plus_ps, &statbuf);
-        if (stat_rc == 0)
-            *new_filespec_p = strdup(filespec_plus_ps);
+        statRc = lstat(fileNamePlusPs, &statbuf);
+        if (statRc == 0)
+            *newFileNameP = strdup(fileNamePlusPs);
         else
-            *new_filespec_p = strdup(orig_filespec);
-        strfree(filespec_plus_ps);
+            *newFileNameP = strdup(origFileName);
+        pm_strfree(fileNamePlusPs);
     }
     if (verbose)
-        pm_message("Input file is %s", *new_filespec_p);
+        pm_message("Input file is %s", *newFileNameP);
 }
 
 
 
 static void
-computeSizeResFromSizeSpec(unsigned int   const requestedXsize,
-                           unsigned int   const requestedYsize,
-                           unsigned int   const imageWidth,
-                           unsigned int   const imageHeight,
-                           unsigned int * const xsizeP,
-                           unsigned int * const ysizeP,
-                           unsigned int * const xresP,
-                           unsigned int * const yresP) {
+computeSizeResFromSizeSpec(unsigned int        const requestedXsize,
+                           unsigned int        const requestedYsize,
+                           unsigned int        const imageWidth,
+                           unsigned int        const imageHeight,
+                           struct Dimensions * const imageDimP) {
 
     if (requestedXsize) {
-        *xsizeP = requestedXsize;
-        *xresP = (unsigned int) (requestedXsize * 72 / imageWidth + 0.5);
+        imageDimP->xsize = requestedXsize;
+        imageDimP->xres = (unsigned int)
+            (requestedXsize * 72 / imageWidth + 0.5);
         if (!requestedYsize) {
-            *yresP = *xresP;
-            *ysizeP = (unsigned int) (imageHeight * (float)*yresP/72 + 0.5);
+            imageDimP->yres = imageDimP->xres;
+            imageDimP->ysize = (unsigned int)
+                (imageHeight * (float)imageDimP->yres/72 + 0.5);
             }
         }
 
     if (requestedYsize) {
-        *ysizeP = requestedYsize;
-        *yresP = (unsigned int) (requestedYsize * 72 / imageHeight + 0.5);
+        imageDimP->ysize = requestedYsize;
+        imageDimP->yres = (unsigned int)
+            (requestedYsize * 72 / imageHeight + 0.5);
         if (!requestedXsize) {
-            *xresP = *yresP;
-            *xsizeP = (unsigned int) (imageWidth * (float)*xresP/72 + 0.5);
+            imageDimP->xres = imageDimP->yres;
+            imageDimP->xsize = (unsigned int)
+                (imageWidth * (float)imageDimP->xres/72 + 0.5);
         }
     } 
 }
@@ -265,46 +302,40 @@ computeSizeResFromSizeSpec(unsigned int   const requestedXsize,
 
 
 static void
-computeSizeResBlind(unsigned int   const xmax,
-                    unsigned int   const ymax,
-                    unsigned int   const imageWidth,
-                    unsigned int   const imageHeight,
-                    bool           const nocrop,
-                    unsigned int * const xsizeP,
-                    unsigned int * const ysizeP,
-                    unsigned int * const xresP,
-                    unsigned int * const yresP) {
+computeSizeResBlind(unsigned int        const xmax,
+                    unsigned int        const ymax,
+                    unsigned int        const imageWidth,
+                    unsigned int        const imageHeight,
+                    bool                const nocrop,
+                    struct Dimensions * const imageDimP) {
 
     if (imageWidth == 0 || imageHeight == 0) {
-        *xresP = *yresP = 72;
+        imageDimP->xres = imageDimP->yres = 72;
     } else {
-        *xresP = *yresP = MIN(xmax * 72 / imageWidth,
-                              ymax * 72 / imageHeight);
+        imageDimP->xres = imageDimP->yres = MIN(xmax * 72 / imageWidth,
+                                                ymax * 72 / imageHeight);
     }
 
     if (nocrop) {
-        *xsizeP = xmax;
-        *ysizeP = ymax;
+        imageDimP->xsize = xmax;
+        imageDimP->ysize = ymax;
     } else {
-        *xsizeP = (unsigned int) (imageWidth * (float)*xresP / 72 + 0.5);
-        *ysizeP = (unsigned int) (imageHeight * (float)*yresP / 72 + 0.5);
+        imageDimP->xsize = (unsigned int)
+            (imageWidth * (float)imageDimP->xres / 72 + 0.5);
+        imageDimP->ysize = (unsigned int)
+            (imageHeight * (float)imageDimP->yres / 72 + 0.5);
     }
 }
 
 
 
 static void
-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) {
+computeSizeRes(struct CmdlineInfo  const cmdline, 
+               struct Box          const borderedBox,
+               struct Dimensions * const imageDimP) {
 /*----------------------------------------------------------------------------
-  Figure out how big the output image should be (return as
-  *xsizeP and *ysizeP) and what output device resolution Ghostscript
-  should assume (return as *xresP, *yresP).
+  Figure out how big the output image should be and what output device
+  resolution Ghostscript should assume (return as *imageDimP).
 
   A resolution number is the number of pixels per inch that the a
   printer prints.  Since we're emulating a printed page with a PNM
@@ -319,59 +350,53 @@ computeSizeRes(struct cmdlineInfo const cmdline,
   tell Ghostscript that our horizontal output device resolution is 500
   pixels per inch.
   
-  *xresP and *yresP are in dots per inch.
+  X and Y in all returned values is with respect to the image, not the
+  page.  Note that the image might be placed sideways on the page, so that
+  page X and Y would be reversed from image X and Y.
 -----------------------------------------------------------------------------*/
-    unsigned int sx, sy;
-        /* The horizontal and vertical sizes of the input image, in points
-           (1/72 inch)
-        */
-
-    if (orientation == LANDSCAPE) {
-        sx = bordered_box.ury - bordered_box.lly;
-        sy = bordered_box.urx - bordered_box.llx;
-    } else {
-        sx = bordered_box.urx - bordered_box.llx;
-        sy = bordered_box.ury - bordered_box.lly;
-    }
+    /* The horizontal and vertical sizes of the input image, in points
+       (1/72 inch)
+    */
+    unsigned int const sx = borderedBox.urx - borderedBox.llx;
+    unsigned int const sy = borderedBox.ury - borderedBox.lly;
 
     if (cmdline.dpi) {
         /* User gave resolution; we figure out output image size */
-        *xresP = *yresP = cmdline.dpi;
-        *xsizeP = (int) (cmdline.dpi * sx / 72 + 0.5);
-        *ysizeP = (int) (cmdline.dpi * sy / 72 + 0.5);
+        imageDimP->xres = imageDimP->yres = cmdline.dpi;
+        imageDimP->xsize = ROUNDU(cmdline.dpi * sx / 72.0);
+        imageDimP->ysize = ROUNDU(cmdline.dpi * sy / 72.0);
     } else  if (cmdline.xsize || cmdline.ysize) {
         if (sx == 0 || sy == 0)
             pm_error("Input image is zero size; we cannot satisfy your "
                      "produce your requested output dimensions");
         computeSizeResFromSizeSpec(cmdline.xsize, cmdline.ysize, sx, sy,
-                                   xsizeP, ysizeP, xresP, yresP);
+                                   imageDimP);
     } else
         computeSizeResBlind(cmdline.xmax, cmdline.ymax, sx, sy, cmdline.nocrop,
-                            xsizeP, ysizeP, xresP, yresP);
+                            imageDimP);
 
     if (cmdline.verbose) {
         pm_message("output is %u pixels wide X %u pixels high",
-                   *xsizeP, *ysizeP);
+                   imageDimP->xsize, imageDimP->ysize);
         pm_message("output device resolution is %u dpi horiz, %u dpi vert",
-                   *xresP, *yresP);
+                   imageDimP->xres, imageDimP->yres);
     }
 }
 
 
 
-enum postscript_language {COMMON_POSTSCRIPT, ENCAPSULATED_POSTSCRIPT};
+enum PostscriptLanguage {COMMON_POSTSCRIPT, ENCAPSULATED_POSTSCRIPT};
 
-static enum postscript_language
-languageDeclaration(char const input_filespec[],
-                    bool const verbose) {
+static enum PostscriptLanguage
+languageDeclaration(char const inputFileName[]) {
 /*----------------------------------------------------------------------------
   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
   a languages, just say it is Common Postscript).
 -----------------------------------------------------------------------------*/
-    enum postscript_language language;
+    enum PostscriptLanguage language;
 
-    if (streq(input_filespec, "-"))
+    if (streq(inputFileName, "-"))
         /* Can't read stdin, because we need it to remain positioned for the 
            Ghostscript interpreter to read it.
         */
@@ -380,14 +405,14 @@ languageDeclaration(char const input_filespec[],
         FILE *infile;
         char line[80];
 
-        infile = pm_openr(input_filespec);
+        infile = pm_openr(inputFileName);
 
         if (fgets(line, sizeof(line), infile) == NULL)
             language = COMMON_POSTSCRIPT;
         else {
-            const char eps_header[] = " EPSF-";
+            const char epsHeader[] = " EPSF-";
 
-            if (strstr(line, eps_header))
+            if (strstr(line, epsHeader))
                 language = ENCAPSULATED_POSTSCRIPT;
             else
                 language = COMMON_POSTSCRIPT;
@@ -404,61 +429,63 @@ languageDeclaration(char const input_filespec[],
 
 
 
-static struct box
-computeBoxToExtract(struct box const cmdline_extract_box,
-                    char       const input_filespec[],
-                    bool       const verbose) {
+static struct Box
+computeBoxToExtract(struct Box const cmdlineExtractBox,
+                    char       const inputFileName[]) {
 
-    struct box retval;
+    struct Box retval;
 
-    if (cmdline_extract_box.llx != -1)
+    if (cmdlineExtractBox.llx != -1)
         /* User told us what box to extract, so that's what we'll do */
-        retval = cmdline_extract_box;
+        retval = cmdlineExtractBox;
     else {
         /* Try to get the bounding box from the DSC %%BoundingBox
            statement (A Postscript comment) in the input.
         */
-        struct box ps_bb;  /* Box described by %%BoundingBox stmt in input */
+        struct Box psBb;  /* Box described by %%BoundingBox stmt in input */
 
-        if (streq(input_filespec, "-"))
+        if (streq(inputFileName, "-"))
             /* Can't read stdin, because we need it to remain
                positioned for the Ghostscript interpreter to read it.  
             */
-            ps_bb.llx = -1;
+            psBb.llx = -1;
         else {
-            FILE *infile;
-            int found_BB, eof;  /* logical */
-            infile = pm_openr(input_filespec);
+            FILE * ifP;
+            bool foundBb;
+            bool eof;
+
+            ifP = pm_openr(inputFileName);
             
-            found_BB = FALSE;
-            eof = FALSE;
-            while (!eof && !found_BB) {
+            for (foundBb = FALSE, eof = FALSE; !foundBb && !eof; ) {
                 char line[200];
-                
-                if (fgets(line, sizeof(line), infile) == NULL)
+                char * fgetsRc;
+
+                fgetsRc = fgets(line, sizeof(line), ifP);
+
+                if (fgetsRc == NULL)
                     eof = TRUE;
                 else {
                     int rc;
                     rc = sscanf(line, "%%%%BoundingBox: %d %d %d %d",
-                                &ps_bb.llx, &ps_bb.lly, 
-                                &ps_bb.urx, &ps_bb.ury);
+                                &psBb.llx, &psBb.lly, 
+                                &psBb.urx, &psBb.ury);
                     if (rc == 4) 
-                        found_BB = TRUE;
+                        foundBb = TRUE;
                 }
             }
-            fclose(infile);
+            fclose(ifP);
 
-            if (!found_BB) {
-                ps_bb.llx = -1;
+            if (!foundBb) {
+                psBb.llx = -1;
                 pm_message("Warning: no %%%%BoundingBox statement "
-                           "in the input or command line.\n"
+                           "in the input or command line.  "
                            "Will use defaults");
             }
         }
-        if (ps_bb.llx != -1) {
+        if (psBb.llx != -1) {
             if (verbose)
                 pm_message("Using %%%%BoundingBox statement from input.");
-            retval = ps_bb;
+            retval = psBb;
         } else { 
             /* Use the center of an 8.5" x 11" page with 1" border all around*/
             retval.llx = 72;
@@ -475,44 +502,75 @@ computeBoxToExtract(struct box const cmdline_extract_box,
 
 
 
-static enum orientation
-computeOrientation(struct cmdlineInfo const cmdline, 
-                   struct box         const extract_box) {
+static enum Orientation
+computeOrientation(struct CmdlineInfo const cmdline, 
+                   struct Box         const extractBox) {
+/*----------------------------------------------------------------------------
+   The proper orientation of the image on the page, given the user's
+   parameters 'cmdline' and the image dimensions 'extractBox'.
+-----------------------------------------------------------------------------*/
+    /* We're putting an _image_ on a _page_.  Either one can have portrait or
+       landscape aspect ratio.  In our return value, orientation just means
+       whether the image is rotated on the page: Portrait means it isn't
+       Landscape means it is.  The result can be confusing: Consider an image
+       which is a landscape, wider than it is tall, being printed on a page
+       which is also wider than it is tall.  The orientation we would return
+       for that case is Portrait.
+
+       The decision is simple: if the user didn't request a particular
+       orientation, we return the value that makes the image orientation match
+       the page orientation.  If both possibilities match equally (because the
+       image or the page is square), we return Portrait.
+    */
 
-    enum orientation retval;
-    unsigned int const input_width  = extract_box.urx - extract_box.llx;
-    unsigned int const input_height = extract_box.ury - extract_box.lly;
+    enum Orientation retval;
 
     if (cmdline.orientation != UNSPECIFIED)
         retval = cmdline.orientation;
     else {
-        if ((!cmdline.xsize || !cmdline.ysize) &
-            (cmdline.xsize || cmdline.ysize)) {
-            /* User specified one output dimension, but not the other,
-               so we can't use output dimensions to make the decision.  So
-               just use the input dimensions.
-            */
-            if (input_height > input_width) retval = PORTRAIT;
-            else retval = LANDSCAPE;
+        /* Dimensions of image to print, in points */
+        unsigned int const imageWidPt = extractBox.urx - extractBox.llx;
+        unsigned int const imageHgtPt = extractBox.ury - extractBox.lly;
+        
+        /* Dimensions of image to print, in pixels (possibly of assumed
+           resolution)
+        */
+        unsigned int imageWidXel;
+        unsigned int imageHgtXel;
+
+        /* We have to deal with the awkward case that the printed pixels are
+           not square.  We match up the aspect ratio of the image in _pixels_
+           and the aspect ratio of the page in _pixels_.  But only the ratio
+           matters; we don't care what the absolute size of the pixels is.
+           And that's good, because if the user didn't specify xsize/ysize, we
+           don't know the absolute size in pixels.  In that case, fortunately,
+           the pixels are guaranteed to be square so we can just pretend it is
+           one point per pixel and get the right result.
+        */
+
+        if (cmdline.xsize && cmdline.ysize) {
+            imageWidXel = cmdline.xsize;
+            imageHgtXel = cmdline.ysize;
         } else {
-            int output_width, output_height;
-            if (cmdline.xsize) {
-                /* He gave xsize and ysize, so that's the output size */
-                output_width = cmdline.xsize;
-                output_height = cmdline.ysize;
-            } else {
-                /* Well then we'll just use his (or default) xmax, ymax */
-                output_width = cmdline.xmax;
-                output_height = cmdline.ymax;
-            }
+            /* Pixels are square, so it doesn't matter what the resolution
+               is; just call it one pixel per point.
+            */
+            imageWidXel = imageWidPt;
+            imageHgtXel = imageHgtPt;
+        }
 
-            if (input_height > input_width && output_height > output_width)
-                retval = PORTRAIT;
-            else if (input_height < input_width && 
-                     output_height < output_width)
-                retval = PORTRAIT;
-            else 
-                retval = LANDSCAPE;
+        if (imageHgtXel >= imageWidXel && cmdline.ymax >= cmdline.xmax) {
+            /* Both image and page are higher than wide, so no rotation */
+            retval = PORTRAIT;
+        } else if (imageHgtXel < imageWidXel &&
+                   cmdline.ymax < cmdline.xmax) {
+            /* Both image and page are wider than high, so no rotation */
+            retval = PORTRAIT;
+        } else {
+            /* Image and pixel have opposite aspect ratios, so rotate
+               for best fit.
+            */
+            retval = LANDSCAPE;
         }
     }
     return retval;
@@ -520,29 +578,32 @@ computeOrientation(struct cmdlineInfo const cmdline,
 
 
 
-static struct box
-addBorders(struct box const input_box, 
-           float      const xborder_scale,
-           float      const yborder_scale,
-           bool       const verbose) {
+static struct Box
+addBorders(struct Box const inputBox, 
+           float      const xborderScale,
+           float      const yborderScale) {
 /*----------------------------------------------------------------------------
-   Return a box which is 'input_box' plus some borders.
+   Return a box which is 'inputBox' plus some borders.
 
-   Add left and right borders that are the fraction 'xborder_scale' of the
+   Add left and right borders that are the fraction 'xborderScale' of the
    width of the input box; likewise for top and bottom borders with 
-   'yborder_scale'.
+   'yborderScale'.
 -----------------------------------------------------------------------------*/
-    struct box retval;
+    unsigned int const leftRightBorderSize = 
+        ROUNDU((inputBox.urx - inputBox.llx) * xborderScale);
+    unsigned int const topBottomBorderSize = 
+        ROUNDU((inputBox.ury - inputBox.lly) * yborderScale);
+
+    struct Box retval;
+
 
-    const int left_right_border_size = 
-        (int) ((input_box.urx - input_box.llx) * xborder_scale + 0.5);
-    const int top_bottom_border_size = 
-        (int) ((input_box.ury - input_box.lly) * yborder_scale + 0.5);
+    assert(inputBox.urx >= inputBox.llx);
+    assert(inputBox.ury >= inputBox.lly);
 
-    retval.llx = input_box.llx - left_right_border_size;
-    retval.lly = input_box.lly - top_bottom_border_size;
-    retval.urx = input_box.urx + left_right_border_size;
-    retval.ury = input_box.ury + top_bottom_border_size;
+    retval.llx = inputBox.llx - (int)leftRightBorderSize;
+    retval.lly = inputBox.lly - (int)topBottomBorderSize;
+    retval.urx = inputBox.urx + (int)leftRightBorderSize;
+    retval.ury = inputBox.ury + (int)topBottomBorderSize;
 
     if (verbose)
         pm_message("With borders, extracted box is ((%d,%d),(%d,%d))",
@@ -553,65 +614,87 @@ addBorders(struct box const input_box,
 
 
 
-static const char *
-computePstrans(struct box       const box,
-               enum orientation const orientation,
-               int              const xsize,
-               int              const ysize, 
-               int              const xres,
-               int              const yres) {
+static void
+writePstrans(struct Box        const box,
+             struct Dimensions const d,
+             enum Orientation  const orientation,
+             FILE *            const pipeToGsP) {
 
-    const char * retval;
+    int const xsize = d.xsize;
+    int const ysize = d.ysize;
+    int const xres  = d.xres;
+    int const yres  = d.yres;
 
-    if (orientation == PORTRAIT) {
+    const char * pstrans;
+
+    switch (orientation) {
+    case PORTRAIT: {
         int llx, lly;
         llx = box.llx - (xsize * 72 / xres - (box.urx - box.llx)) / 2;
         lly = box.lly - (ysize * 72 / yres - (box.ury - box.lly)) / 2;
-        asprintfN(&retval, "%d neg %d neg translate", llx, lly);
-    } else {
+        pm_asprintf(&pstrans, "%d neg %d neg translate", llx, lly);
+    } break;
+    case LANDSCAPE: {
         int llx, ury;
-        llx = box.llx - (ysize * 72 / yres - (box.urx - box.llx)) / 2;
-        ury = box.ury + (xsize * 72 / xres - (box.ury - box.lly)) / 2;
-        asprintfN(&retval, "90 rotate %d neg %d neg translate", llx, ury);
+        llx = box.llx - (xsize * 72 / xres - (box.urx - box.llx)) / 2;
+        ury = box.ury + (ysize * 72 / yres - (box.ury - box.lly)) / 2;
+        pm_asprintf(&pstrans, "90 rotate %d neg %d neg translate", llx, ury);
+    } break;
+    case UNSPECIFIED:
+        assert(false);
     }
 
-    if (retval == NULL)
+    if (pstrans == pm_strsol)
         pm_error("Unable to allocate memory for pstrans");
 
-    return retval;
+    if (verbose) 
+        pm_message("Postscript prefix command: '%s'", pstrans);
+
+    fprintf(pipeToGsP, "%s\n", pstrans);
+
+    pm_strfree(pstrans);
 }
 
 
 
 static const char *
-computeOutfileArg(struct cmdlineInfo const cmdline) {
-
-    const char *retval;  /* malloc'ed */
+computeOutfileArg(struct CmdlineInfo const cmdline) {
+/*----------------------------------------------------------------------------
+   Determine the value for the "OutputFile" variable to pass to Ghostscript,
+   which is what tells Ghostscript where to put its output.  This is either
+   a pattern such as "foo%03d.ppm" or "-" to indicate Standard Output.
+
+   We go with "-" if, according to 'cmdline', the user asked for
+   Standard Output or is giving his input on Standard Input.  Otherwise,
+   we go with the pattern, based on the name of the input file and output
+   format type the user requested.
+-----------------------------------------------------------------------------*/
+    const char * retval;  /* malloc'ed */
 
-    if (cmdline.goto_stdout)
+    if (cmdline.stdoutSpec)
         retval = strdup("-");
-    else if (streq(cmdline.input_filespec, "-"))
+    else if (streq(cmdline.inputFileName, "-"))
         retval = strdup("-");
     else {
         char * basename;
         const char * suffix;
         
-        basename  = strdup(cmdline.input_filespec);
+        basename  = strdup(cmdline.inputFileName);
         if (strlen(basename) > 3 && 
             streq(basename+strlen(basename)-3, ".ps")) 
-            /* The input filespec ends in ".ps".  Chop it off. */
+            /* The input file name ends in ".ps".  Chop it off. */
             basename[strlen(basename)-3] = '\0';
 
-        switch (cmdline.format_type) {
+        switch (cmdline.formatType) {
         case PBM_TYPE: suffix = "pbm"; break;
         case PGM_TYPE: suffix = "pgm"; break;
         case PPM_TYPE: suffix = "ppm"; break;
-        default: pm_error("Internal error: invalid value for format_type: %d",
-                          cmdline.format_type);
+        default: pm_error("Internal error: invalid value for formatType: %d",
+                          cmdline.formatType);
         }
-        asprintfN(&retval, "%s%%03d.%s", basename, suffix);
+        pm_asprintf(&retval, "%s%%03d.%s", basename, suffix);
 
-        strfree(basename);
+        pm_strfree(basename);
     }
     return(retval);
 }
@@ -619,22 +702,22 @@ computeOutfileArg(struct cmdlineInfo const cmdline) {
 
 
 static const char *
-computeGsDevice(int  const format_type,
+computeGsDevice(int  const formatType,
                 bool const forceplain) {
 
     const char * basetype;
     const char * retval;
 
-    switch (format_type) {
+    switch (formatType) {
     case PBM_TYPE: basetype = "pbm"; break;
     case PGM_TYPE: basetype = "pgm"; break;
     case PPM_TYPE: basetype = "ppm"; break;
-    default: pm_error("Internal error: invalid value format_type");
+    default: pm_error("Internal error: invalid value formatType");
     }
     if (forceplain)
         retval = strdup(basetype);
     else
-        asprintfN(&retval, "%sraw", basetype);
+        pm_asprintf(&retval, "%sraw", basetype);
 
     if (retval == NULL)
         pm_error("Unable to allocate memory for gs device");
@@ -652,7 +735,7 @@ findGhostscriptProg(const char ** const retvalP) {
         *retvalP = strdup(getenv("GHOSTSCRIPT"));
     if (*retvalP == NULL) {
         if (getenv("PATH") != NULL) {
-            char *pathwork;  /* malloc'ed */
+            char * pathwork;  /* malloc'ed */
             const char * candidate;
 
             pathwork = strdup(getenv("PATH"));
@@ -665,7 +748,7 @@ findGhostscriptProg(const char ** const retvalP) {
                 const char * filename;
                 int rc;
 
-                asprintfN(&filename, "%s/gs", candidate);
+                pm_asprintf(&filename, "%s/gs", candidate);
                 rc = stat(filename, &statbuf);
                 if (rc == 0) {
                     if (S_ISREG(statbuf.st_mode))
@@ -674,7 +757,7 @@ findGhostscriptProg(const char ** const retvalP) {
                     pm_error("Error looking for Ghostscript program.  "
                              "stat(\"%s\") returns errno %d (%s)",
                              filename, errno, strerror(errno));
-                strfree(filename);
+                pm_strfree(filename);
 
                 candidate = strtok(NULL, ":");
             }
@@ -688,22 +771,26 @@ findGhostscriptProg(const char ** const retvalP) {
 
 
 static void
-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) {
-    
+execGhostscript(int               const inputPipeFd,
+                char              const ghostscriptDevice[],
+                char              const outfileArg[], 
+                struct Dimensions const pageDim,
+                unsigned int      const textalphabits) {
+/*----------------------------------------------------------------------------
+   Exec the Ghostscript program and have it execute the Postscript program
+   that it receives on 'inputPipeFd', then exit.
+
+   'pageDim' describes the print area.  X and Y in 'pageDim' are with respect
+   to the page, independent of whether the program we receive on 'inputPipeFd'
+   puts an image in there sideways.
+-----------------------------------------------------------------------------*/
     const char * arg0;
     const char * ghostscriptProg;
     const char * deviceopt;
     const char * outfileopt;
     const char * gopt;
     const char * ropt;
+    const char * textalphabitsopt;
     int rc;
 
     findGhostscriptProg(&ghostscriptProg);
@@ -712,11 +799,12 @@ execGhostscript(int  const inputPipeFd,
     rc = dup2(inputPipeFd, STDIN_FILENO);
     close(inputPipeFd);
 
-    asprintfN(&arg0, "gs");
-    asprintfN(&deviceopt, "-sDEVICE=%s", ghostscript_device);
-    asprintfN(&outfileopt, "-sOutputFile=%s", outfile_arg);
-    asprintfN(&gopt, "-g%dx%d", xsize, ysize);
-    asprintfN(&ropt, "-r%dx%d", xres, yres);
+    pm_asprintf(&arg0, "gs");
+    pm_asprintf(&deviceopt, "-sDEVICE=%s", ghostscriptDevice);
+    pm_asprintf(&outfileopt, "-sOutputFile=%s", outfileArg);
+    pm_asprintf(&gopt, "-g%dx%d", pageDim.xsize, pageDim.ysize);
+    pm_asprintf(&ropt, "-r%dx%d", pageDim.xres, pageDim.yres);
+    pm_asprintf(&textalphabitsopt, "-dTextAlphaBits=%u", textalphabits);
 
     /* -dSAFER causes Postscript to disable %pipe and file operations,
        which are almost certainly not needed here.  This prevents our
@@ -726,14 +814,15 @@ execGhostscript(int  const inputPipeFd,
 
     if (verbose) {
         pm_message("execing '%s' with args '%s' (arg 0), "
-                   "'%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s'",
+                   "'%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s'",
                    ghostscriptProg, arg0,
-                   deviceopt, outfileopt, gopt, ropt, "-q", "-dNOPAUSE", 
+                   deviceopt, outfileopt, gopt, ropt, textalphabitsopt,
+                   "-q", "-dNOPAUSE", 
                    "-dSAFER", "-");
     }
 
-    execl(ghostscriptProg, arg0, deviceopt, outfileopt, gopt, ropt, "-q",
-          "-dNOPAUSE", "-dSAFER", "-", NULL);
+    execl(ghostscriptProg, arg0, deviceopt, outfileopt, gopt, ropt, 
+	  textalphabitsopt, "-q", "-dNOPAUSE", "-dSAFER", "-", NULL);
     
     pm_error("execl() of Ghostscript ('%s') failed, errno=%d (%s)",
              ghostscriptProg, errno, strerror(errno));
@@ -742,28 +831,126 @@ execGhostscript(int  const inputPipeFd,
 
 
 static void
-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 */
-    FILE *infile;
+feedPsToGhostScript(const char *            const inputFileName,
+                    struct Box              const borderedBox,
+                    struct Dimensions       const imageDim,
+                    enum Orientation        const orientation,
+                    int                     const pipeToGhostscriptFd,
+                    enum PostscriptLanguage const language) {
+/*----------------------------------------------------------------------------
+   Send a Postscript program to the Ghostscript process running on the
+   other end of the pipe 'pipeToGhostscriptFd'.  That program is mostly
+   the contents of file 'inputFileName' (special value "-" means Standard
+   Input), but we may add a little to it.
+
+   The image has dimensions 'imageDim' and is oriented on the page according
+   to 'orientation' ('imageDim' X and Y are with respect to the image itself,
+   without regard to how it is oriented on the page).
+-----------------------------------------------------------------------------*/
+    FILE * pipeToGsP;  /* Pipe to Ghostscript's standard input */
+    FILE * ifP;
+    bool eof;  /* End of file on input */
+
+    pipeToGsP = fdopen(pipeToGhostscriptFd, "w");
+    if (pipeToGsP == NULL) 
+        pm_error("Unable to open stream on pipe to Ghostscript process.");
+    
+    ifP = pm_openr(inputFileName);
+    /*
+      In encapsulated Postscript, we the encapsulator are supposed to
+      handle showing the page (which we do by passing a showpage
+      statement to Ghostscript).  Any showpage statement in the 
+      input must be defined to have no effect.
+          
+      See "Enscapsulated PostScript Format File Specification",
+      v. 3.0, 1 May 1992, in particular Example 2, p. 21.  I found
+      it at 
+      http://partners.adobe.com/asn/developer/pdfs/tn/5002.EPSF_Spec.pdf
+      The example given is a much fancier solution than we need
+      here, I think, so I boiled it down a bit.  JM 
+    */
+    if (language == ENCAPSULATED_POSTSCRIPT)
+        fprintf(pipeToGsP, "\n/b4_Inc_state save def /showpage { } def\n");
+ 
+    writePstrans(borderedBox, imageDim, orientation, pipeToGsP);
+
+    /* If our child dies, it closes the pipe and when we next write to it,
+       we get a SIGPIPE.  We must survive that signal in order to report
+       on the fate of the child.  So we ignore SIGPIPE:
+    */
+    signal(SIGPIPE, SIG_IGN);
+
+    eof = FALSE;
+    while (!eof) {
+        char buffer[4096];
+        size_t readCt;
+            
+        readCt = fread(buffer, 1, sizeof(buffer), ifP);
+        if (readCt == 0) 
+            eof = TRUE;
+        else 
+            fwrite(buffer, 1, readCt, pipeToGsP);
+    }
+    pm_close(ifP);
+
+    if (language == ENCAPSULATED_POSTSCRIPT)
+        fprintf(pipeToGsP, "\nb4_Inc_state restore showpage\n");
+
+    fclose(pipeToGsP);
+}        
+
+
+
+static struct Dimensions
+pageDimFromImageDim(struct Dimensions const imageDim,
+                    enum Orientation  const orientation) {
+/*----------------------------------------------------------------------------
+   The dimensions of the page of an image whose dimensions are
+   'imageDim', if we place it on the page with orientation 'orientation'.
+
+   (I.e. swap and X and Y if landscape orientation).
+
+   'orientation' must not be UNSPECIFIED.
+-----------------------------------------------------------------------------*/
+    struct Dimensions retval;
+
+    switch (orientation) {
+    case PORTRAIT:
+        retval = imageDim;
+        break;
+    case LANDSCAPE:
+        retval.xsize = imageDim.ysize;
+        retval.ysize = imageDim.xsize;
+        retval.xres  = imageDim.yres;
+        retval.yres  = imageDim.xres;
+        break;
+    case UNSPECIFIED:
+        assert(false);
+        break;
+    }
+
+    return retval;
+}
+
+
+
+static void
+executeGhostscript(char                    const inputFileName[],
+                   struct Box              const borderedBox,
+                   struct Dimensions       const imageDim,
+                   enum Orientation        const orientation,
+                   char                    const ghostscriptDevice[],
+                   char                    const outfileArg[], 
+                   unsigned int            const textalphabits,
+                   enum PostscriptLanguage const language) {
+
     int rc;
-    int eof;  /* End of file on input */
     int pipefd[2];
 
-    if (strlen(outfile_arg) > 80)
+    if (strlen(outfileArg) > 80)
         pm_error("output file spec too long.");
     
-    rc = pipe(pipefd);
+    rc = pm_pipe(pipefd);
     if (rc < 0)
         pm_error("Unable to create pipe to talk to Ghostscript process.  "
                  "errno = %d (%s)", errno, strerror(errno));
@@ -775,79 +962,38 @@ executeGhostscript(char                     const pstrans[],
     else if (rc == 0) {
         /* Child process */
         close(pipefd[1]);
-        execGhostscript(pipefd[0], ghostscript_device, outfile_arg,
-                        xsize, ysize, xres, yres, input_filespec, verbose);
+        execGhostscript(pipefd[0], ghostscriptDevice, outfileArg,
+                        pageDimFromImageDim(imageDim, orientation),
+                        textalphabits);
     } else {
+        /* parent process */
         pid_t const ghostscriptPid = rc;
         int const pipeToGhostscriptFd = pipefd[1];
-        /* parent process */
-        close(pipefd[0]);
-
-        gs = fdopen(pipeToGhostscriptFd, "w");
-        if (gs == NULL) 
-            pm_error("Unable to open stream on pipe to Ghostscript process.");
-    
-        infile = pm_openr(input_filespec);
-        /*
-          In encapsulated Postscript, we the encapsulator are supposed to
-          handle showing the page (which we do by passing a showpage
-          statement to Ghostscript).  Any showpage statement in the 
-          input must be defined to have no effect.
-          
-          See "Enscapsulated PostScript Format File Specification",
-          v. 3.0, 1 May 1992, in particular Example 2, p. 21.  I found
-          it at 
-          http://partners.adobe.com/asn/developer/pdfs/tn/5002.EPSF_Spec.pdf
-          The example given is a much fancier solution than we need
-          here, I think, so I boiled it down a bit.  JM 
-        */
-        if (language == ENCAPSULATED_POSTSCRIPT)
-            fprintf(gs, "\n/b4_Inc_state save def /showpage { } def\n");
- 
-        if (verbose) 
-            pm_message("Postscript prefix command: '%s'", pstrans);
-
-        fprintf(gs, "%s\n", pstrans);
 
-        /* If our child dies, it closes the pipe and when we next write to it,
-           we get a SIGPIPE.  We must survive that signal in order to report
-           on the fate of the child.  So we ignore SIGPIPE:
-        */
-        signal(SIGPIPE, SIG_IGN);
+        int gsTermStatus;  /* termination status of Ghostscript process */
+        pid_t rc;
 
-        eof = FALSE;
-        while (!eof) {
-            char buffer[4096];
-            int bytes_read;
-            
-            bytes_read = fread(buffer, 1, sizeof(buffer), infile);
-            if (bytes_read == 0) 
-                eof = TRUE;
-            else 
-                fwrite(buffer, 1, bytes_read, gs);
-        }
-        pm_close(infile);
+        close(pipefd[0]);
 
-        if (language == ENCAPSULATED_POSTSCRIPT)
-            fprintf(gs, "\nb4_Inc_state restore showpage\n");
+        feedPsToGhostScript(inputFileName, borderedBox,
+                            imageDim, orientation,
+                            pipeToGhostscriptFd, language);
 
-        fclose(gs);
-        
-        waitpid(ghostscriptPid, &gs_exit, 0);
+        rc = waitpid(ghostscriptPid, &gsTermStatus, 0);
         if (rc < 0)
             pm_error("Wait for Ghostscript process to terminated failed.  "
                      "errno = %d (%s)", errno, strerror(errno));
 
-        if (gs_exit != 0) {
-            if (WIFEXITED(gs_exit))
+        if (gsTermStatus != 0) {
+            if (WIFEXITED(gsTermStatus))
                 pm_error("Ghostscript failed.  Exit code=%d\n", 
-                         WEXITSTATUS(gs_exit));
-            else if (WIFSIGNALED(gs_exit))
-                pm_error("Ghostscript process died due to a signal %d.",
-                         WTERMSIG(gs_exit));
+                         WEXITSTATUS(gsTermStatus));
+            else if (WIFSIGNALED(gsTermStatus))
+                pm_error("Ghostscript process died because of a signal %d.",
+                         WTERMSIG(gsTermStatus));
             else 
                 pm_error("Ghostscript process died with exit code %d", 
-                         gs_exit);
+                         gsTermStatus);
         }
     }
 }
@@ -857,65 +1003,59 @@ executeGhostscript(char                     const pstrans[],
 int
 main(int argc, char ** argv) {
 
-    struct cmdlineInfo cmdline;
-    const char * input_filespec;  /* malloc'ed */
+    struct CmdlineInfo cmdline;
+    const char * inputFileName;  /* 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 */
-    struct box extract_box;
+    struct Dimensions imageDim;
+        /* Size and resolution of the input image */
+    struct Box extractBox;
         /* coordinates of the box within the input we are to extract; i.e.
            that will become the output. 
            */
-    struct box bordered_box;
+    struct Box borderedBox;
         /* Same as above, but expanded to include borders */
 
-    enum postscript_language language;
-    enum orientation orientation;
-    const char * ghostscript_device;
-    const char * outfile_arg;
-    const char * pstrans;
+    enum PostscriptLanguage language;
+    enum Orientation orientation;
+    const char * ghostscriptDevice;
+    const char * outfileArg;
 
     pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    addPsToFilespec(cmdline.input_filespec, &input_filespec, cmdline.verbose);
-
-    extract_box = computeBoxToExtract(cmdline.extract_box, input_filespec, 
-                                      cmdline.verbose);
+    verbose = cmdline.verbose;
 
-    language = languageDeclaration(input_filespec, cmdline.verbose);
-    
-    orientation = computeOrientation(cmdline, extract_box);
+    addPsToFileName(cmdline.inputFileName, &inputFileName);
 
-    bordered_box = addBorders(extract_box, cmdline.xborder, cmdline.yborder,
-                              cmdline.verbose);
+    extractBox = computeBoxToExtract(cmdline.extractBox, inputFileName);
 
-    computeSizeRes(cmdline, orientation, bordered_box, 
-                   &xsize, &ysize, &xres, &yres);
+    language = languageDeclaration(inputFileName);
+    
+    orientation = computeOrientation(cmdline, extractBox);
 
-    if (xres == 0)
-        xres = 1;
-    if (yres == 0)
-        yres = 1;
+    borderedBox = addBorders(extractBox, cmdline.xborder, cmdline.yborder);
 
-    pstrans = computePstrans(bordered_box, orientation,
-                             xsize, ysize, xres, yres);
+    computeSizeRes(cmdline, borderedBox, &imageDim);
 
-    outfile_arg = computeOutfileArg(cmdline);
+    if (imageDim.xres == 0)
+        imageDim.xres = 1;
+    if (imageDim.yres == 0)
+        imageDim.yres = 1;
+    
+    outfileArg = computeOutfileArg(cmdline);
 
-    ghostscript_device = 
-        computeGsDevice(cmdline.format_type, cmdline.forceplain);
+    ghostscriptDevice = 
+        computeGsDevice(cmdline.formatType, cmdline.forceplain);
     
-    pm_message("Writing %s file", ghostscript_device);
+    pm_message("Writing %s format", ghostscriptDevice);
     
-    executeGhostscript(pstrans, ghostscript_device, outfile_arg, 
-                       xsize, ysize, xres, yres, input_filespec,
-                       language, cmdline.verbose);
+    executeGhostscript(inputFileName, borderedBox, imageDim, orientation,
+                       ghostscriptDevice, outfileArg, cmdline.textalphabits,
+                       language);
 
-    strfree(ghostscript_device);
-    strfree(outfile_arg);
-    strfree(pstrans);
+    pm_strfree(ghostscriptDevice);
+    pm_strfree(outfileArg);
     
     return 0;
 }
diff --git a/converter/other/rast.c b/converter/other/rast.c
index 91c50ccd..1c787089 100644
--- a/converter/other/rast.c
+++ b/converter/other/rast.c
@@ -269,32 +269,71 @@ pr_dump( p, out, colormap, type, copy_flag )
     return 0;
 }
 
+
+
 int
-pr_load_header( in, hP )
-    FILE* in;
-    struct rasterfile* hP;
-{
-    if ( pm_readbiglong( in, &(hP->ras_magic) ) == -1 )
-        return PIX_ERR;
-    if ( hP->ras_magic != RAS_MAGIC )
-        return PIX_ERR;
-    if ( pm_readbiglong( in, &(hP->ras_width) ) == -1 )
-        return PIX_ERR;
-    if ( pm_readbiglong( in, &(hP->ras_height) ) == -1 )
-        return PIX_ERR;
-    if ( pm_readbiglong( in, &(hP->ras_depth) ) == -1 )
-        return PIX_ERR;
-    if ( pm_readbiglong( in, &(hP->ras_length) ) == -1 )
-        return PIX_ERR;
-    if ( pm_readbiglong( in, &(hP->ras_type) ) == -1 )
-        return PIX_ERR;
-    if ( pm_readbiglong( in, &(hP->ras_maptype) ) == -1 )
-        return PIX_ERR;
-    if ( pm_readbiglong( in, &(hP->ras_maplength) ) == -1 )
-        return PIX_ERR;
+pr_load_header(FILE * const ifP, struct rasterfile * const headerP) {
+
+    {
+        long magic;
+
+        pm_readbiglong(ifP, &magic);
+        if (magic != RAS_MAGIC)
+            pm_error("Wrong magic number for a RAST file");
+    }
+    {
+        long width;
+        pm_readbiglong(ifP, &width);
+
+        if (width < 0)
+            pm_error("Negative width in RAST header");
+        else
+            headerP->ras_width = width;
+    }
+    {
+        long height;
+        pm_readbiglong(ifP, &height);
+
+        if (height < 0)
+            pm_error("Negative height in RAST header");
+        else
+            headerP->ras_height = height;
+    }
+    {
+        long depth;
+        pm_readbiglong(ifP, &depth);
+
+        if (depth < 0)
+            pm_error("Negative depth in RAST header");
+        else
+            headerP->ras_depth = depth;
+    }
+    {
+        long length;
+        pm_readbiglong(ifP, &length);
+
+        if (length < 0)
+            pm_error("Negative length in RAST header");
+        else
+            headerP->ras_length = length;
+    }
+    pm_readbiglong(ifP, &headerP->ras_type);
+
+    pm_readbiglong(ifP, &headerP->ras_maptype);
+    {
+        long mapLength;
+        pm_readbiglong(ifP, &mapLength);
+
+        if (mapLength < 0)
+            pm_error("Negative map length in RAST header");
+        else
+            headerP->ras_maplength = mapLength;
+    }
     return 0;
 }
 
+
+
 int
 pr_load_colormap( in, hP, colormap )
     FILE* in;
diff --git a/converter/other/rast.h b/converter/other/rast.h
index e79896ea..eb6f4ec4 100644
--- a/converter/other/rast.h
+++ b/converter/other/rast.h
@@ -49,7 +49,7 @@ struct rasterfile {
 #define RMT_EQUAL_RGB	1
 #define RMT_RAW		2
     long ras_maplength;
-    };
+};
 
 struct pixrectops {
     int	(*pro_rop)();
@@ -65,21 +65,22 @@ struct pixrectops {
     int	(*pro_getcolormap)();
     int	(*pro_putattributes)();
     int	(*pro_getattributes)();
-    };
+};
 
 struct pr_size {
     int x, y;
-    };
+};
+
 struct pr_pos {
     int x, y;
-    };
+};
 
 struct pixrect {
     struct pixrectops* pr_ops;
     struct pr_size pr_size;
     int pr_depth;
     struct mpr_data* pr_data;	/* work-alike only handles memory pixrects */
-    };
+};
 
 struct mpr_data {
     int md_linebytes;
@@ -87,13 +88,13 @@ struct mpr_data {
     struct pr_pos md_offset;
     short md_primary;
     short md_flags;
-    };
+};
 
 typedef struct {
     int type;
     int length;
     unsigned char* map[3];
-    } colormap_t;
+} colormap_t;
 
 /* And the routine definitions. */
 
diff --git a/converter/other/rasttopnm.c b/converter/other/rasttopnm.c
index aa55850b..285fc5e0 100644
--- a/converter/other/rasttopnm.c
+++ b/converter/other/rasttopnm.c
@@ -10,254 +10,489 @@
 ** implied warranty.
 */
 
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
 #include "pnm.h"
 #include "rast.h"
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    struct rasterfile header;
-    colormap_t pr_colormap;
-    int grayscale;
-    struct pixrect* pr;
-    xel* xelrow;
-    register xel* xP;
-    int argn, rows, cols, format, depth, i, row, mask;
-    register int col;
-    xelval maxval;
-    xel zero, one;
-    int linesize;
-    unsigned char* data;
-    unsigned char* byteP;
 
-    pnm_init( &argc, argv );
 
-    argn = 1;
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+    unsigned int index;
+    unsigned int dumpheader;
+    unsigned int dumpcolormap;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions2 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
 
-    if ( argn != argc )
-	{
-	ifp = pm_openr( argv[argn] );
-	++argn;
-	}
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+ 
+    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 */
+
+    OPTENT3(0,   "index",        OPT_FLAG,   NULL,
+            &cmdlineP->index,          0);
+    OPTENT3(0,   "dumpheader",   OPT_FLAG,   NULL,
+            &cmdlineP->dumpheader,     0);
+    OPTENT3(0,   "dumpcolormap", OPT_FLAG,   NULL,
+            &cmdlineP->dumpcolormap,   0);
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
     else
-	ifp = stdin;
-
-    if ( argn != argc )
-	pm_usage( "[rastfile]" );
-
-    /* Read in the rasterfile.  First the header. */
-    if ( pr_load_header( ifp, &header ) != 0 )
-	pm_error( "unable to read in rasterfile header" );
-
-    cols = header.ras_width;
-    rows = header.ras_height;
-    depth = header.ras_depth;
-
-    if ( cols <= 0 )
-	pm_error( "invalid cols: %d", cols );
-    if ( rows <= 0 )
-	pm_error( "invalid rows: %d", rows );
-
-    /* If there is a color map, read it. */
-    grayscale = 1;
-    if ( header.ras_maplength != 0 )
-	{
-	if ( pr_load_colormap( ifp, &header, &pr_colormap ) != 0 )
-	    pm_error( "unable to skip colormap data" );
-	for ( i = 0; i < header.ras_maplength / 3; ++i )
-	    if ( pr_colormap.map[0][i] != pr_colormap.map[1][i] ||
-		 pr_colormap.map[1][i] != pr_colormap.map[2][i] )
-		{
-		grayscale = 0;
-		break;
-		}
-	}
-
-    /* Check the depth and color map. */
-    switch ( depth )
-	{
-	case 1:
-	if ( header.ras_maptype == RMT_NONE && header.ras_maplength == 0 )
-	    {
-	    maxval = 1;
-	    format = PBM_TYPE;
-	    PNM_ASSIGN1( zero, maxval );
-	    PNM_ASSIGN1( one, 0 );
-	    }
-	else if ( header.ras_maptype == RMT_EQUAL_RGB &&
-		  header.ras_maplength == 6 )
-	    {
-	    if ( grayscale )
-		{
-		maxval = 255;
-		format = PGM_TYPE;
-		PNM_ASSIGN1( zero, pr_colormap.map[0][0] );
-		PNM_ASSIGN1( one, pr_colormap.map[0][1] );
-		}
-	    else
-		{
-		maxval = 255;
-		format = PPM_TYPE;
-		PPM_ASSIGN(
-		    zero, pr_colormap.map[0][0], pr_colormap.map[1][0],
-		    pr_colormap.map[2][0] );
-		PPM_ASSIGN(
-		    one, pr_colormap.map[0][1], pr_colormap.map[1][1],
-		    pr_colormap.map[2][1] );
-		}
-	    }
-	else
-	    pm_error(
-            "this depth-1 rasterfile has a non-standard colormap - "
-            "type %ld length %ld",
-            header.ras_maptype, header.ras_maplength );
-	break;
-
-	case 8:
-	if ( grayscale )
-	    {
-	    maxval = 255;
-	    format = PGM_TYPE;
-	    }
-	else if ( header.ras_maptype == RMT_EQUAL_RGB )
-	    {
-	    maxval = 255;
-	    format = PPM_TYPE;
-	    }
-	else
-	    pm_error(
-            "this depth-8 rasterfile has a non-standard colormap - "
-            "type %ld length %ld",
-            header.ras_maptype, header.ras_maplength );
-	break;
-
-	case 24:
-	case 32:
-	if ( header.ras_maptype == RMT_NONE && header.ras_maplength == 0 )
-	    ;
-	else if ( header.ras_maptype == RMT_RAW || header.ras_maplength == 768 )
-	    ;
-	else
-	    pm_error(
-            "this depth-%d rasterfile has a non-standard colormap - "
-            "type %ld length %ld",
-            depth, header.ras_maptype, header.ras_maplength );
-	maxval = 255;
-	format = PPM_TYPE;
-	break;
-
-	default:
-	pm_error(
-	    "invalid depth: %d.  Can only handle depth 1, 8, 24, or 32.",
-	    depth );
-	}
-
-    /* Now load the data.  The pixrect returned is a memory pixrect. */
-    if ( ( pr = pr_load_image( ifp, &header, NULL ) ) == NULL )
-	pm_error(
-	    "unable to read in the image from the rasterfile" );
-
-    linesize = ( (struct mpr_data*) pr->pr_data )->md_linebytes;
-    data = ( (struct mpr_data*) pr->pr_data )->md_image;
-
-    pm_close( ifp );
-
-    /* Now write out the anymap. */
-    pnm_writepnminit( stdout, cols, rows, maxval, format, 0 );
-    xelrow = pnm_allocrow( cols );
-    switch ( PNM_FORMAT_TYPE(format) )
-        {
-        case PBM_TYPE:
-        pm_message( "writing PBM file" );
+        cmdlineP->inputFileName = argv[1];
+}
+
+
+
+static bool
+colorMapIsGrayscale(colormap_t const colorMap) {
+/*----------------------------------------------------------------------------
+   The color map contains only gray.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    bool grayscale;
+
+    for (i = 0, grayscale = true; i < colorMap.length; ++i) {
+        if (colorMap.map[0][i] != colorMap.map[1][i] ||
+            colorMap.map[1][i] != colorMap.map[2][i]) {
+            grayscale = false;
+        }
+    }
+    return grayscale;
+}
+
+
+
+static void
+analyzeImage(struct rasterfile const header,
+             colormap_t        const colorMap,
+             int *             const formatP,
+             xelval *          const maxvalP,
+             bool *            const grayscaleP,
+             xel *             const zeroP,
+             xel *             const oneP) {
+
+    bool const grayscale =
+        header.ras_maplength == 0 || colorMapIsGrayscale(colorMap);
+
+    *grayscaleP = grayscale;
+
+    switch (header.ras_depth) {
+    case 1:
+        if (header.ras_maptype == RMT_NONE && header.ras_maplength == 0) {
+            *maxvalP = 1;
+            *formatP = PBM_TYPE;
+            PNM_ASSIGN1(*zeroP, 1);
+            PNM_ASSIGN1(*oneP, 0);
+        } else if (header.ras_maptype == RMT_EQUAL_RGB &&
+                   header.ras_maplength == 6) {
+            if (grayscale) {
+                *maxvalP = 255;
+                *formatP = PGM_TYPE;
+                PNM_ASSIGN1(*zeroP, colorMap.map[0][0]);
+                PNM_ASSIGN1(*oneP, colorMap.map[0][1]);
+            } else {
+                *maxvalP = 255;
+                *formatP = PPM_TYPE;
+                PPM_ASSIGN(
+                    *zeroP, colorMap.map[0][0], colorMap.map[1][0],
+                    colorMap.map[2][0]);
+                PPM_ASSIGN(
+                    *oneP, colorMap.map[0][1], colorMap.map[1][1],
+                    colorMap.map[2][1]);
+            }
+        } else
+            pm_error(
+                "this depth-1 rasterfile has a non-standard colormap - "
+                "type %ld length %ld",
+                header.ras_maptype, header.ras_maplength);
+        break;
+
+    case 8:
+        if (grayscale) {
+            *maxvalP = 255;
+            *formatP = PGM_TYPE;
+        } else if (header.ras_maptype == RMT_EQUAL_RGB) {
+            *maxvalP = 255;
+            *formatP = PPM_TYPE;
+        } else
+            pm_error(
+                "this depth-8 rasterfile has a non-standard colormap - "
+                "type %ld length %ld",
+                header.ras_maptype, header.ras_maplength);
         break;
 
-        case PGM_TYPE:
-        pm_message( "writing PGM file" );
+    case 24:
+    case 32:
+        if (header.ras_maptype == RMT_NONE && header.ras_maplength == 0);
+        else if (header.ras_maptype == RMT_RAW || header.ras_maplength == 768);
+        else
+            pm_error(
+                "this depth-%ld rasterfile has a non-standard colormap - "
+                "type %ld length %ld",
+                header.ras_depth, header.ras_maptype, header.ras_maplength);
+        *maxvalP = 255;
+        *formatP = PPM_TYPE;
         break;
 
-        case PPM_TYPE:
-        pm_message( "writing PPM file" );
+    default:
+        pm_error("invalid depth: %ld.  Can handle only depth 1, 8, 24, or 32.",
+                 header.ras_depth);
+    }
+}
+
+
+
+static void
+reportOutputType(int const format) {
+
+    switch (PNM_FORMAT_TYPE(format)) {
+    case PBM_TYPE:
+        pm_message("writing PBM file");
+        break;
+    case PGM_TYPE:
+        pm_message("writing PGM file");
+        break;
+    case PPM_TYPE:
+        pm_message("writing PPM file");
         break;
+    default:
+        abort();
+    }
+}
+
+
+
+static void
+convertRowDepth1(const unsigned char * const rastLine,
+                 unsigned int          const cols,
+                 xel                   const zeroXel,
+                 xel                   const oneXel,
+                 xel *                 const xelrow) {
+
+    const unsigned char * byteP;
+     unsigned int col;
+    unsigned char mask;
+
+    byteP = rastLine;  /* initial value */
+
+    for (col = 0, mask = 0x80; col < cols; ++col) {
+        if (mask == 0x00) {
+            ++byteP;
+            mask = 0x80;
+        }
+        xelrow[col] = (*byteP & mask) ? oneXel : zeroXel;
+        mask = mask >> 1;
+    }
+}
+
+
+
+static void
+convertRowDepth8(const unsigned char * const lineStart,
+                 unsigned int          const cols,
+                 bool                  const colorMapped,
+                 bool                  const useIndexForColor,
+                 bool                  const grayscale,
+                 colormap_t            const colorMap,
+                 xel *                 const xelrow) {
+/*----------------------------------------------------------------------------
+   Convert a line of raster data from the RAST input to a row of raster
+   data for the PNM output.
+
+   'lineStart' is where the RAST row starts.   'xelrow' is where to put the
+   PNM row.  'cols' is the number of pixels in the row.
+
+   'colorMapped' means the RAST image is colormapped.  If so, 'colorMap'
+   is the color map from the RAST file and 'useIndexForColor' means not
+   to use that map but instead to create a PGM row of the colormap
+   _indices_.
+
+   'grayscale' means it is a grayscale image; the output is PGM.
+-----------------------------------------------------------------------------*/
+    const unsigned char * byteP;
+    unsigned int col;
+
+    byteP = lineStart; /* initial value */
+
+    for (col = 0; col < cols; ++col) {
+        if (colorMapped && !useIndexForColor) {
+            if (grayscale)
+                PNM_ASSIGN1(xelrow[col], colorMap.map[0][*byteP]);
+            else
+                PPM_ASSIGN(xelrow[col],
+                           colorMap.map[0][*byteP],
+                           colorMap.map[1][*byteP],
+                           colorMap.map[2][*byteP]);
+        } else
+            PNM_ASSIGN1(xelrow[col], *byteP);
+
+        ++byteP;
+    }
+} 
+
+
+
+static void
+convertRowRgb(const unsigned char * const lineStart,
+              unsigned int          const cols,
+              unsigned int          const depth,
+              long                  const rastType,
+              bool                  const colorMapped,
+              bool                  const useIndexForColor,
+              colormap_t            const colorMap,
+              xel *                 const xelrow) {
+
+    const unsigned char * byteP;
+    unsigned int col;
+
+    byteP = lineStart; /* initial value */
+
+    for (col = 0; col < cols; ++col) {
+        xelval r, g, b;
 
+        if (depth == 32)
+            ++byteP;
+        if (rastType == RT_FORMAT_RGB) {
+            r = *byteP++;
+            g = *byteP++;
+            b = *byteP++;
+        } else {
+            b = *byteP++;
+            g = *byteP++;
+            r = *byteP++;
+        }
+        if (colorMapped && !useIndexForColor)
+            PPM_ASSIGN(xelrow[col],
+                       colorMap.map[0][r],
+                       colorMap.map[1][g],
+                       colorMap.map[2][b]);
+        else
+            PPM_ASSIGN(xelrow[col], r, g, b);
+    }
+} 
+
+
+
+static void
+writePnm(FILE *                 const ofP,
+         const struct pixrect * const pixRectP,
+         unsigned int           const cols,
+         unsigned int           const rows,
+         xelval                 const maxval,
+         int                    const format,
+         unsigned int           const depth,
+         long                   const rastType,
+         bool                   const grayscale,
+         bool                   const colorMapped,
+         colormap_t             const colorMap,
+         xel                    const zeroXel,
+         xel                    const oneXel,
+         bool                   const useIndexForColor) {
+
+    struct mpr_data const mprData = *pixRectP->pr_data;
+
+    xel * xelrow;
+    unsigned int row;
+    unsigned char * lineStart;
+
+    pnm_writepnminit(ofP, cols, rows, maxval, format, 0);
+
+    xelrow = pnm_allocrow(cols);
+
+    reportOutputType(format);
+
+    for (row = 0, lineStart = mprData.md_image;
+         row < rows;
+         ++row, lineStart += mprData.md_linebytes) {
+
+        switch (depth) {
+        case 1:
+            convertRowDepth1(lineStart, cols, zeroXel, oneXel, xelrow);
+            break;
+        case 8:
+            convertRowDepth8(lineStart, cols, colorMapped, useIndexForColor,
+                             grayscale, colorMap, xelrow);
+            break;
+        case 24:
+        case 32:
+            convertRowRgb(lineStart, cols, depth, rastType, colorMapped,
+                          useIndexForColor, colorMap, xelrow);
+            break;
         default:
-        pm_error( "shouldn't happen" );
+            pm_error("Invalid depth value: %u", depth);
         }
+        pnm_writepnmrow(ofP, xelrow, cols, maxval, format, 0);
+    }
+}
+
+
+
+static void
+dumpHeader(struct rasterfile const header) {
+
+    const char * typeName;
+
+    switch (header.ras_type) {
+    case RT_OLD:            typeName = "old";           break;
+    case RT_STANDARD:       typeName = "standard";      break;
+    case RT_BYTE_ENCODED:   typeName = "byte encoded";  break;
+    case RT_FORMAT_RGB:     typeName = "format rgb";    break;
+    case RT_FORMAT_TIFF:    typeName = "format_tiff";   break;
+    case RT_FORMAT_IFF:     typeName = "format_iff";    break;
+    case RT_EXPERIMENTAL:   typeName = "experimental";  break;
+    default:                typeName = "???";
+    }
+
+    pm_message("type: %s (%lu)", typeName, (unsigned long)header.ras_type);
+    pm_message("%luw x %lul x %lud",
+               (unsigned long)header.ras_width,
+               (unsigned long)header.ras_height,
+               (unsigned long)header.ras_depth);
+    pm_message("raster length: %lu", (unsigned long)header.ras_length);
+
+    if (header.ras_maplength)
+        pm_message("Has color map");
+}
+
+
+
+static void
+dumpHeaderAnalysis(bool         const grayscale, 
+                   unsigned int const depth,
+                   xel          const zero, 
+                   xel          const one) {
+
+    pm_message("grayscale: %s", grayscale ? "YES" : "NO");
+
+    if (depth == 1) {
+        pm_message("Zero color: (%u,%u,%u)",
+                   PNM_GETR(zero),
+                   PNM_GETG(zero),
+                   PNM_GETB(zero));
+
+        pm_message("One color: (%u,%u,%u)",
+                   PNM_GETR(one),
+                   PNM_GETG(one),
+                   PNM_GETB(one));
+    }
+}
+
 
-    for ( row = 0; row < rows; ++row )
-	{
-	byteP = data;
-	switch ( depth )
-	    {
-	    case 1:
-	    mask = 0x80;
-	    for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-		{
-		if ( mask == 0 )
-		    {
-		    ++byteP;
-		    mask = 0x80;
-		    }
-		*xP = ( *byteP & mask ) ? one : zero;
-		mask = mask >> 1;
-		}
-	    break;
-
-	    case 8:
-	    for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-		{
-		if ( header.ras_maplength == 0 )
-		    PNM_ASSIGN1( *xP, *byteP );
-		else if ( grayscale )
-		    PNM_ASSIGN1( *xP, pr_colormap.map[0][*byteP] );
-		else
-		    PPM_ASSIGN(
-			*xP, pr_colormap.map[0][*byteP],
-			pr_colormap.map[1][*byteP],
-			pr_colormap.map[2][*byteP] );
-		++byteP;
-		}
-	    break;
-
-	    case 24:
-	    case 32:
-	    for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-		{
-		register xelval r, g, b;
-
-		if ( depth == 32 )
-		    ++byteP;
-		if ( header.ras_type == RT_FORMAT_RGB )
-		    {
-		    r = *byteP++;
-		    g = *byteP++;
-		    b = *byteP++;
-		    }
-		else
-		    {
-		    b = *byteP++;
-		    g = *byteP++;
-		    r = *byteP++;
-		    }
-		if ( header.ras_maplength == 0 )
-		    PPM_ASSIGN( *xP, r, g, b );
-		else
-		    PPM_ASSIGN(
-			*xP, pr_colormap.map[0][r], pr_colormap.map[1][g],
-			pr_colormap.map[2][b] );
-		}
-	    break;
-
-	    default:
-	    pm_error( "can't happen" );
-	    }
-	data += linesize;
-	pnm_writepnmrow( stdout, xelrow, cols, maxval, format, 0 );
-	}
-
-    pm_close( stdout );
-
-    exit( 0 );
+
+static void
+dumpColorMap(colormap_t const colorMap) {
+
+    unsigned int i;
+    const char * typeName;
+
+    switch (colorMap.type) {
+    case RMT_NONE:      typeName = "NONE";      break;
+    case RMT_EQUAL_RGB: typeName = "EQUAL_RGB"; break;
+    case RMT_RAW:       typeName = "RAW";       break;
+    default:            typeName = "???";
     }
+
+    pm_message("color map type = %s (%u)", typeName, colorMap.type);
+
+    pm_message("color map size = %u", colorMap.length);
+
+    for (i = 0; i < colorMap.length; ++i)
+        pm_message("color %u: (%u, %u, %u)", i,
+                   (unsigned char)colorMap.map[0][i],
+                   (unsigned char)colorMap.map[1][i],
+                   (unsigned char)colorMap.map[2][i]);
+}
+
+
+
+int
+main(int argc, const char ** const argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    struct rasterfile header;
+    colormap_t colorMap;
+    bool grayscale;
+    struct pixrect * pr;
+    int format;
+    xelval maxval;
+    xel zero, one;
+    int rc;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    rc = pr_load_header(ifP, &header);
+    if (rc != 0 )
+        pm_error("unable to read in rasterfile header");
+
+    if (cmdline.dumpheader)
+        dumpHeader(header);
+
+    if (header.ras_maplength != 0) {
+        int rc;
+        
+        rc = pr_load_colormap(ifP, &header, &colorMap);
+        
+        if (rc != 0 )
+            pm_error("unable to read colormap from RAST file");
+
+        if (cmdline.dumpcolormap)
+            dumpColorMap(colorMap);
+    }
+
+    analyzeImage(header, colorMap, &format, &maxval, &grayscale, &zero, &one);
+
+    if (cmdline.dumpheader)
+        dumpHeaderAnalysis(grayscale, header.ras_depth, zero, one);
+
+    pr = pr_load_image(ifP, &header, NULL);
+    if (pr == NULL )
+        pm_error("unable to read in the image from the rasterfile" );
+
+    if (cmdline.index && header.ras_maplength == 0)
+        pm_error("You requested to use color map indices as colors (-index), "
+                 "but this is not a color mapped image");
+
+    writePnm(stdout, pr, header.ras_width, header.ras_height, maxval, format,
+             header.ras_depth, header.ras_type, grayscale, 
+             header.ras_maplength > 0, colorMap, zero, one, cmdline.index);
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/converter/other/rletopnm.c b/converter/other/rletopnm.c
index 83ada51b..99959141 100644
--- a/converter/other/rletopnm.c
+++ b/converter/other/rletopnm.c
@@ -61,19 +61,19 @@
 /*
  * Utah type declarations.
  */
-rle_hdr     hdr;
-rle_map     *colormap;
+static rle_hdr hdr;
+static rle_map * colormap;
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    char *input_filename;
+    char *       inputFilename;
     unsigned int headerdump;
     unsigned int verbose;
-    char *alpha_filename;
-    bool alpha_stdout;
+    char *       alphaout;
+    bool         alphaStdout;
 };
 
 
@@ -81,15 +81,14 @@ struct cmdlineInfo {
 /*
  * Other declarations.
  */
-int     visual, maplen;
-int     width, height;
-
+static int visual, maplen;
+static int width, height;
 
 
 
 static void
 parseCommandLine(int argc, char ** argv,
-                   struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
    *cmdlineP structure are actually in the supplied argv array.  And
@@ -110,34 +109,34 @@ parseCommandLine(int argc, char ** argv,
     OPTENT3('v', "verbose",    OPT_FLAG,   
             NULL,                      &cmdlineP->verbose,        0);
     OPTENT3(0,   "alphaout",   OPT_STRING, 
-            &cmdlineP->alpha_filename, &alphaoutSpec,             0);
+            &cmdlineP->alphaout, &alphaoutSpec,                   0);
 
     opt.opt_table = option_def;
     opt.short_allowed = TRUE;  /* We have short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and all of *cmdlineP. */
 
     if (!alphaoutSpec)
-        cmdlineP->alpha_filename = NULL;
+        cmdlineP->alphaout = NULL;
 
     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 */
+            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 "
                  "is the input file specification");
 
-    if (cmdlineP->alpha_filename && 
-        streq(cmdlineP->alpha_filename, "-"))
-        cmdlineP->alpha_stdout = TRUE;
+    if (cmdlineP->alphaout && 
+        streq(cmdlineP->alphaout, "-"))
+        cmdlineP->alphaStdout = TRUE;
     else 
-        cmdlineP->alpha_stdout = FALSE;
+        cmdlineP->alphaStdout = FALSE;
 }
 
 
@@ -167,15 +166,14 @@ reportRleGetSetupError(int const rleGetSetupRc) {
 }
 
 
-/*-----------------------------------------------------------------------------
- *                                                         Read the rle header.
- */
+
 static void 
-read_rle_header(FILE * const ifp,
-                bool   const headerDump) {
+readRleHeader(FILE * const ifP,
+              bool   const headerDump) {
+
     int rc;
     int  i;
-    hdr.rle_file = ifp;
+    hdr.rle_file = ifP;
     rc = rle_get_setup(&hdr);
     if (rc != 0)
         reportRleGetSetupError(rc);
@@ -254,12 +252,12 @@ read_rle_header(FILE * const ifp,
         for (i = 0; hdr.comments[i] != NULL; i++)
             HMSG("%s", hdr.comments[i]);
 }
-/*-----------------------------------------------------------------------------
- *                                                    Write the ppm image data.
- */
+
+
+
 static void 
-write_ppm_data(FILE * const imageout_file,
-               FILE * const alpha_file) {
+writePpmRaster(FILE * const imageoutFileP,
+               FILE * const alphaFileP) {
 
     rle_pixel ***scanlines, **scanline;
     pixval r, g, b;
@@ -341,17 +339,17 @@ write_ppm_data(FILE * const imageout_file,
         /*
          * Write the scan line.
          */
-        if (imageout_file ) 
-            ppm_writeppmrow(imageout_file, pixelrow, width, RLE_MAXVAL, 0);
-        if (alpha_file)
-            pgm_writepgmrow(alpha_file, alpharow, width, RLE_MAXVAL, 0);
+        if (imageoutFileP) 
+            ppm_writeppmrow(imageoutFileP, pixelrow, width, RLE_MAXVAL, 0);
+        if (alphaFileP)
+            pgm_writepgmrow(alphaFileP, alpharow, width, RLE_MAXVAL, 0);
 
     } /* end of for scan = 0 to height */
 
     /* Free scanline memory. */
-    for ( scan = 0; scan < height; scan++ )
-        rle_row_free( &hdr, scanlines[scan] );
-    free( scanlines );
+    for (scan = 0; scan < height; ++scan)
+        rle_row_free(&hdr, scanlines[scan]);
+    free (scanlines);
     ppm_freerow(pixelrow);
     pgm_freerow(alpharow);
 }
@@ -359,8 +357,8 @@ write_ppm_data(FILE * const imageout_file,
 
 
 static void 
-write_pgm_data(FILE * const imageout_file,
-               FILE * const alpha_file) {
+writePgmRaster(FILE * const imageoutFileP,
+               FILE * const alphaFileP) {
 /*----------------------------------------------------------------------------
    Write the PGM image data
 -----------------------------------------------------------------------------*/
@@ -397,10 +395,10 @@ write_pgm_data(FILE * const imageout_file,
             else
                 alpharow[x] = 0;
         }
-        if (imageout_file) 
-            pgm_writepgmrow(imageout_file, pixelrow, width, RLE_MAXVAL, 0);
-        if (alpha_file)
-            pgm_writepgmrow(alpha_file, alpharow, width, RLE_MAXVAL, 0);
+        if (imageoutFileP) 
+            pgm_writepgmrow(imageoutFileP, pixelrow, width, RLE_MAXVAL, 0);
+        if (alphaFileP)
+            pgm_writepgmrow(alphaFileP, alpharow, width, RLE_MAXVAL, 0);
 
         }   /* end of for scan = 0 to height */
 
@@ -414,59 +412,59 @@ write_pgm_data(FILE * const imageout_file,
 
 
 
-/*-----------------------------------------------------------------------------
- *                               Convert a Utah rle file to a pbmplus pnm file.
- */
 int
 main(int argc, char ** argv) {
 
-    struct cmdlineInfo cmdline;
-    FILE        *ifp;
-    FILE *imageout_file, *alpha_file;
-    char *fname = NULL;
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    FILE * imageoutFileP;
+    FILE * alphaFileP;
+    char * fname;
 
     pnm_init( &argc, argv );
 
     parseCommandLine(argc, argv, &cmdline);
 
-    if ( cmdline.input_filename != NULL ) 
-        ifp = pm_openr( cmdline.input_filename );
+    fname = NULL;  /* initial value */
+
+    if (cmdline.inputFilename != NULL ) 
+        ifP = pm_openr(cmdline.inputFilename);
     else
-        ifp = stdin;
+        ifP = stdin;
 
-    if (cmdline.alpha_stdout)
-        alpha_file = stdout;
-    else if (cmdline.alpha_filename == NULL) 
-        alpha_file = NULL;
+    if (cmdline.alphaStdout)
+        alphaFileP = stdout;
+    else if (cmdline.alphaout == NULL) 
+        alphaFileP = NULL;
     else {
-        alpha_file = pm_openw(cmdline.alpha_filename);
+        alphaFileP = pm_openw(cmdline.alphaout);
     }
 
-    if (cmdline.alpha_stdout) 
-        imageout_file = NULL;
+    if (cmdline.alphaStdout) 
+        imageoutFileP = NULL;
     else
-        imageout_file = stdout;
+        imageoutFileP = stdout;
 
 
     /*
      * Open the file.
      */
     /* Initialize header. */
-    hdr = *rle_hdr_init( (rle_hdr *)NULL );
-    rle_names( &hdr, cmd_name( argv ), fname, 0 );
+    hdr = *rle_hdr_init(NULL);
+    rle_names(&hdr, cmd_name( argv ), fname, 0);
 
     /*
      * Read the rle file header.
      */
-    read_rle_header(ifp, cmdline.headerdump || cmdline.verbose);
+    readRleHeader(ifP, cmdline.headerdump || cmdline.verbose);
     if (cmdline.headerdump)
         exit(0);
 
     /* 
      * Write the alpha file header
      */
-    if (alpha_file)
-        pgm_writepgminit(alpha_file, width, height, RLE_MAXVAL, 0);
+    if (alphaFileP)
+        pgm_writepgminit(alphaFileP, width, height, RLE_MAXVAL, 0);
 
     /*
      * Write the pnm file header.
@@ -475,24 +473,24 @@ main(int argc, char ** argv) {
     case GRAYSCALE:   /* 8 bits without colormap -> pgm */
         if (cmdline.verbose)
             pm_message("Writing pgm file.");
-        if (imageout_file)
-            pgm_writepgminit(imageout_file, width, height, RLE_MAXVAL, 0);
-        write_pgm_data(imageout_file, alpha_file);
+        if (imageoutFileP)
+            pgm_writepgminit(imageoutFileP, width, height, RLE_MAXVAL, 0);
+        writePgmRaster(imageoutFileP, alphaFileP);
         break;
     default:      /* anything else -> ppm */
         if (cmdline.verbose)
             pm_message("Writing ppm file.");
-        if (imageout_file)
-            ppm_writeppminit(imageout_file, width, height, RLE_MAXVAL, 0);
-        write_ppm_data(imageout_file, alpha_file);
+        if (imageoutFileP)
+            ppm_writeppminit(imageoutFileP, width, height, RLE_MAXVAL, 0);
+        writePpmRaster(imageoutFileP, alphaFileP);
         break;
     }
    
-    pm_close(ifp);
-    if (imageout_file) 
-        pm_close( imageout_file );
-    if (alpha_file)
-        pm_close( alpha_file );
+    pm_close(ifP);
+    if (imageoutFileP) 
+        pm_close(imageoutFileP);
+    if (alphaFileP)
+        pm_close(alphaFileP);
 
     return 0;
 }
diff --git a/converter/other/sgi.h b/converter/other/sgi.h
index 3700d356..2f57f52d 100644
--- a/converter/other/sgi.h
+++ b/converter/other/sgi.h
@@ -5,7 +5,7 @@
 
 typedef struct {
     short           magic;
-    char            storage;
+    unsigned char   storage;
     char            bpc;            /* pixel size: 1 = bytes, 2 = shorts */
     unsigned short  dimension;      /* 1 = single row, 2 = B/W, 3 = RGB */
     unsigned short  xsize,          /* width in pixels */
@@ -25,9 +25,9 @@ typedef struct {
 #define STORAGE_RLE         1
 
 #define CMAP_NORMAL         0
-#define CMAP_DITHERED       1   /* not supported */
-#define CMAP_SCREEN         2   /* not supported */
-#define CMAP_COLORMAP       3   /* not supported */
+#define CMAP_DITHERED       1   /* can't handle this */
+#define CMAP_SCREEN         2   /* can't handle this */
+#define CMAP_COLORMAP       3   /* can't handle this */
 
 #endif
 
diff --git a/converter/other/sgitopnm.c b/converter/other/sgitopnm.c
index 0d26bb9c..6fd4efcf 100644
--- a/converter/other/sgitopnm.c
+++ b/converter/other/sgitopnm.c
@@ -5,6 +5,11 @@
 ** Based on the SGI image description v0.9 by Paul Haeberli (paul@sgi.comp)
 ** Available via ftp from sgi.com:graphics/SGIIMAGESPEC
 **
+** The definitive document describing the SGI image file format,
+** SGI Image File Format Version 1.00 is available from
+** ftp://ftp.sgi.com/graphics/grafica/sgiimage.html
+**
+**
 ** 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
@@ -12,314 +17,451 @@
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.
 **
-** 29Jan94: first version
-** 08Feb94: minor bugfix
-** 29Jul00: added -channel option (smar@reptiles.org)
 */
+
+
+#include <unistd.h>
+#include <limits.h>
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
 #include "pnm.h"
 #include "sgi.h"
-#include "mallocvar.h"
-#ifndef VMS
-#include <unistd.h>
-#endif
 
-/*#define DEBUG*/
+#define MAX_ZSIZE 256
 
-#ifndef SEEK_SET
-#define SEEK_SET    0
-#endif
 
+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 */
+    unsigned int verbose;
+    unsigned int channelSpec;
+    unsigned int channel;
+};
 
-/* entry in RLE offset table */
-typedef struct {
-    long start;     /* offset in file */
-    long length;    /* length of compressed scanline */
-} TabEntry;
 
-typedef short       ScanElem;
-typedef ScanElem *  ScanLine;
-
-/* prototypes */
-static unsigned char get_byte ARGS(( FILE* f ));
-static long get_big_long ARGS((FILE *f));
-static short get_big_short ARGS((FILE *f));
-static short get_byte_as_short ARGS((FILE *f));
-static void readerr ARGS((FILE *f));
-static const char *
-compression_name(char compr);
-static void       read_bytes ARGS((FILE *ifp, int n, char *buf));
-static Header *   read_header ARGS((FILE *ifp, int channel));
-static TabEntry * read_table ARGS((FILE *ifp, int tablen));
-static ScanLine * read_channels ARGS((FILE *ifp, Header *head, TabEntry *table, short (*func) ARGS((FILE *)), int ochan ));
-static void       image_to_pnm ARGS((Header *head, ScanLine *image, xelval maxval, int channel));
-static void       rle_decompress ARGS((ScanElem *src, int srclen, ScanElem *dest, int destlen));
 
-#define WORSTCOMPR(x)   (2*(x) + 2)
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "channel",      OPT_UINT,
+            &cmdlineP->channel,
+            &cmdlineP->channelSpec,            0);
+    OPTENT3(0, "verbose",             OPT_FLAG,      NULL,
+            &cmdlineP->verbose,       0);
+    OPTENT3(0, "noverbose",           OPT_FLAG,      NULL,
+            NULL,       0);  /* backward compatibility */
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    free(option_def);
+
+    if (!cmdlineP->channelSpec)
+        cmdlineP->channel = MAX_ZSIZE + 1;
+            /* Invalid value; to suppress Valgrind error */
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFileName = argv[1];
+    else
+        pm_error("Program takes at most one argument:  input file name");
+}
 
 
-static short verbose = 0;
 
+/* basic I/O functions, taken from ilbmtoppm.c */
 
-int
-main(argc, argv)
-    int argc;
-    char *argv[];
-{
-    FILE *ifp;
-    int argn;
-    const char * const usage = "[-verbose] [-channel n] [sgifile]";
-    TabEntry *table = NULL;
-    ScanLine *image;
-    Header *head;
-    pixval maxval;
-    int channel = -1;
-
-    pnm_init(&argc, argv);
-
-    argn = 1;
-    while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) {
-        if( pm_keymatch(argv[argn], "-verbose", 2) )
-            verbose++;
-        else
-        if( pm_keymatch(argv[argn], "-noverbose", 4) )
-            verbose = 0;
-        else
-        if( pm_keymatch(argv[argn], "-channel", 2) ) {
-            char *s;
-
-            ++argn;
-            if( argn >= argc )
-                pm_usage(usage);
-
-            s = argv[argn];
-            channel = strtol(argv[argn], &s, 10);
-            if( s == argv[argn] || channel < 0)
-                pm_usage(usage);
-        }
-        else
-            pm_usage(usage);
-        ++argn;
-    }
+static void
+readerr(FILE * const f) {
 
-    if( argn < argc ) {
-        ifp = pm_openr( argv[argn] );
-        argn++;
-    }
+    if (ferror(f))
+        pm_error("read error");
     else
-        ifp = stdin;
+        pm_error("premature EOF");
+}
 
-    if( argn != argc )
-        pm_usage(usage);
 
-    head = read_header(ifp, channel);
-    maxval = head->pixmax - head->pixmin;
-    if( maxval > PNM_OVERALLMAXVAL )
-        pm_error("Maximum sample value in input image (%d) is too large.  "
-                 "This program's limit is %d.",
-                 maxval, PNM_OVERALLMAXVAL);
-    if (channel >= head->zsize)
-        pm_error("channel out of range - only %d channels in image",
-            head->zsize);
-    if( head->storage != STORAGE_VERBATIM )
-        table = read_table(ifp, head->ysize * head->zsize);
-    if( head->bpc == 1 )
-        image = read_channels(ifp, head, table, get_byte_as_short, channel);
-    else
-        image = read_channels(ifp, head, table, get_big_short, channel);
 
-    image_to_pnm(head, image, (xelval)maxval, channel);
-    pm_close(ifp);
+static short
+getBigShort(FILE * const ifP) {
+
+    short s;
+
+    if (pm_readbigshort(ifP, &s) == -1)
+        readerr(ifP);
+
+    return s;
+}
+
+
+
+static long
+getBigLong(FILE * const ifP) {
+
+    long l;
+
+    if (pm_readbiglong(ifP, &l) == -1)
+        readerr(ifP);
 
-    exit(0);
+    return l;
 }
 
 
+
+static unsigned char
+getByte(FILE * const ifP) {
+
+    int i;
+
+    i = getc(ifP);
+    if (i == EOF)
+        readerr(ifP);
+
+    return (unsigned char) i;
+}
+
+
+
+static void
+readBytes(FILE * const ifP,
+          int    const n,
+          char * const buf) {
+
+    int r;
+
+    r = fread((void *)buf, 1, n, ifP);
+
+    if (r != n)
+        readerr(ifP);
+}
+
+
+
+static short
+getByteAsShort(FILE * const ifP) {
+
+    return (short)getByte(ifP);
+}
+
+
+
+static const char *
+compressionName(unsigned char const storageCode) {
+
+    switch (storageCode) {
+    case STORAGE_VERBATIM:
+        return "none";
+    case STORAGE_RLE:
+        return "RLE";
+    default:
+        return "unknown";
+    }
+}
+
+
+
+/* entry in RLE offset table */
+typedef struct {
+    long start;     /* offset in file */
+    long length;    /* length of compressed scanline */
+} TabEntry;
+
+typedef short       ScanElem;
+typedef ScanElem *  ScanLine;
+
+#define WORSTCOMPR(x)   (2*(x) + 2)
+
+
+
 static Header *
-read_header(ifp, channel)
-    FILE *ifp;
-    int channel;
-{
-    Header *head;
-
-    MALLOCVAR_NOFAIL(head);
-
-    head->magic     = get_big_short(ifp);
-    head->storage   = get_byte(ifp);
-    head->bpc       = get_byte(ifp);
-    head->dimension = get_big_short(ifp);
-    head->xsize     = get_big_short(ifp);
-    head->ysize     = get_big_short(ifp);
-    head->zsize     = get_big_short(ifp);
-    head->pixmin    = get_big_long(ifp);
-    head->pixmax    = get_big_long(ifp);
-    read_bytes(ifp, 4, head->dummy1);
-    read_bytes(ifp, 80, head->name);
-    head->colormap  = get_big_long(ifp);
-    read_bytes(ifp, 404, head->dummy2);
-
-    if( head->magic != SGI_MAGIC )
+readHeader(FILE *       const ifP,
+           bool         const outChannelSpec,
+           bool         const verbose) {
+
+    Header * headP;
+
+    MALLOCVAR_NOFAIL(headP);
+
+    headP->magic     = getBigShort(ifP);
+    headP->storage   = getByte(ifP);
+    headP->bpc       = getByte(ifP);
+    headP->dimension = getBigShort(ifP);
+    headP->xsize     = getBigShort(ifP);
+    headP->ysize     = getBigShort(ifP);
+    headP->zsize     = getBigShort(ifP);
+    if (headP->zsize > MAX_ZSIZE)
+        pm_error("Too many channels in input image: %u",
+                 (unsigned int) headP->zsize );
+    headP->pixmin    = getBigLong(ifP);
+    headP->pixmax    = getBigLong(ifP);
+    if (headP->pixmin >= headP->pixmax)
+        pm_error("Invalid sgi image header: pixmin larger than pixmax");
+    readBytes(ifP, 4, headP->dummy1);
+    readBytes(ifP, 80, headP->name);
+    headP->colormap  = getBigLong(ifP);
+    readBytes(ifP, 404, headP->dummy2);
+
+    if (headP->magic != SGI_MAGIC)
         pm_error("bad magic number - not an SGI image");
-    if( head->storage != STORAGE_VERBATIM && head->storage != STORAGE_RLE )
+    if (headP->storage != STORAGE_VERBATIM && headP->storage != STORAGE_RLE)
         pm_error("unknown compression type");
-    if( head->bpc < 1 || head->bpc > 2 )
-        pm_error("illegal precision value %d (only 1-2 allowed)", head->bpc );
-    if( head->colormap != CMAP_NORMAL )
+    if (headP->bpc < 1 || headP->bpc > 2)
+        pm_error("illegal precision value %d (only 1-2 allowed)", headP->bpc);
+    if (headP->colormap != CMAP_NORMAL)
         pm_error("non-normal pixel data of a form we don't recognize");
 
     /* adjust ysize/zsize to dimension, just to be sure */
-    switch( head->dimension ) {
+    switch (headP->dimension) {
+    case 1:
+        headP->ysize = 1;
+        break;
+    case 2:
+        headP->zsize = 1;
+        break;
+    case 3:
+        switch (headP->zsize) {
         case 1:
-            head->ysize = 1;
+            headP->dimension = 2;
             break;
         case 2:
-            head->zsize = 1;
+            if (!outChannelSpec)
+                pm_message("2-channel image, using only first channel.  "
+                           "Extract alpha channel with -channel=1");
             break;
         case 3:
-            switch( head->zsize ) {
-                case 1:
-                    head->dimension = 2;
-                    break;
-                case 2:
-                    pm_error("don\'t know how to interpret 2-channel image");
-                    break;
-                case 3:
-                    break;
-                default:
-                    if (channel < 0)
-                        pm_message("%d-channel image, using only first 3 channels", head->zsize);
-                    break;
-            }
             break;
         default:
-            pm_error("illegal dimension value %d (only 1-3 allowed)", head->dimension);
+            if (!outChannelSpec)
+                pm_message("%u-channel image, using only first 3 channels  "
+                           "Extract %s with -channel=%c",
+                            headP->zsize,
+                            headP->zsize==4 ?
+                                "alpha channel" : "additional channels",
+                            headP->zsize==4 ? '3' : 'N');
+            break;
+        }
+        break;
+    default:
+        pm_error("illegal dimension value %u (only 1-3 allowed)",
+                 headP->dimension);
     }
 
-    if( verbose ) {
-        pm_message("raster size %dx%d, %d channels", head->xsize, head->ysize, head->zsize);
-        pm_message("compression: %d = %s", head->storage, compression_name(head->storage));
-        head->name[79] = '\0';  /* just to be safe */
-        pm_message("Image name: \"%s\"", head->name);
-#ifdef DEBUG
-        pm_message("bpc: %d    dimension: %d    zsize: %d", head->bpc, head->dimension, head->zsize);
-        pm_message("pixmin: %ld    pixmax: %ld    colormap: %ld", head->pixmin, head->pixmax, head->colormap);
-#endif
+    if (verbose) {
+        pm_message("raster size %ux%u, %u channels",
+                   headP->xsize, headP->ysize, headP->zsize);
+        pm_message("compression: 0x%02x = %s",
+                   headP->storage, compressionName(headP->storage));
+        headP->name[79] = '\0';  /* just to be safe */
+        pm_message("Image name: '%s'", headP->name);
     }
 
-    return head;
+    return headP;
 }
 
 
+
 static TabEntry *
-read_table(ifp, tablen)
-    FILE *ifp;
-    int tablen;
-{
-    TabEntry *table;
-    int i;
+readTable(FILE * const ifP,
+          int    const tablen) {
 
-    MALLOCARRAY_NOFAIL(table, tablen);
+    TabEntry * table;
+    unsigned int i;
 
-#ifdef DEBUG
-    pm_message("reading offset table");
-#endif
+    MALLOCARRAY_NOFAIL(table, tablen);
 
-    for( i = 0; i < tablen; i++ )
-        table[i].start = get_big_long(ifp);
-    for( i = 0; i < tablen; i++ )
-        table[i].length = get_big_long(ifp);
+    for (i = 0; i < tablen; ++i)
+        table[i].start = getBigLong(ifP);
+    for (i = 0; i < tablen; ++i)
+        table[i].length = getBigLong(ifP);
 
     return table;
 }
 
 
 
+static void
+rleDecompress(ScanElem * const srcStart,
+              int        const srcleftStart,
+              ScanElem * const destStart,
+              int        const destleftStart) {
+
+    ScanElem * src;
+    int srcleft;
+    ScanElem * dest;
+    int destleft;
+
+    for (src = srcStart,
+             srcleft = srcleftStart,
+             dest = destStart,
+             destleft = destleftStart; srcleft; ) {
+
+        unsigned char const el = (unsigned char)(*src++ & 0xff);
+        unsigned int const count = (unsigned int)(el & 0x7f);
+
+        --srcleft;
+
+        if (count == 0)
+            return;
+        if (destleft < count)
+            pm_error("RLE error: too much input data "
+                     "(space left %d, need %d)", destleft, count);
+        destleft -= count;
+        if (el & 0x80) {
+            unsigned int i;
+            if (srcleft < count)
+                pm_error("RLE error: not enough data for literal run "
+                         "(data left %d, need %d)", srcleft, count);
+            srcleft -= count;
+            for (i = 0; i < count; ++i)
+                *dest++ = *src++;
+        } else {
+            unsigned int i;
+            if (srcleft == 0)
+                pm_error("RLE error: not enough data for replicate run");
+            for (i = 0; i < count; ++i)
+                *dest++ = *src;
+            ++src;
+            --srcleft;
+        }
+    }
+    pm_error("RLE error: no terminating 0-byte");
+}
+
+
+
 static ScanLine *
-read_channels(ifp, head, table, func, ochan)
-    FILE *ifp;
-    Header *head;
-    TabEntry *table;
-    short (*func) ARGS((FILE *));
-    int ochan;
-{
-    ScanLine *image;
-    ScanElem *temp;
-    int channel, maxchannel, row, sgi_index, i;
-    long offset, length;
-
-#ifdef DEBUG
-    pm_message("reading channels");
-#endif
-
-    if (ochan < 0) {
-        maxchannel = (head->zsize < 3) ? head->zsize : 3;
-        MALLOCARRAY_NOFAIL(image, head->ysize * maxchannel);
-    } else {
-        maxchannel = ochan + 1;
+readChannels(FILE *       const ifP,
+             Header *     const head,
+             TabEntry *   const table,
+             bool         const outChannelSpec,
+             unsigned int const outChannel) {
+
+    ScanLine * image;
+    ScanElem * temp;
+    unsigned int channel;
+    unsigned int maxchannel;
+
+    if (outChannelSpec) {
+        maxchannel = outChannel + 1;
+        MALLOCARRAY_NOFAIL(image, head->ysize);
+    } else if (head->zsize <= 2) {
+        maxchannel = 1;
         MALLOCARRAY_NOFAIL(image, head->ysize);
+    } else {
+        maxchannel = 3;
+        MALLOCARRAY_NOFAIL(image, head->ysize * maxchannel);
     }
-    if ( table ) 
+    if (table)
         MALLOCARRAY_NOFAIL(temp, WORSTCOMPR(head->xsize));
 
-    for( channel = 0; channel < maxchannel;  channel++ ) {
-#ifdef DEBUG
-        pm_message("    channel %d", channel);
-#endif
-        for( row = 0; row < head->ysize; row++ ) {
-            int iindex;
+    for (channel = 0; channel < maxchannel; ++channel) {
+        unsigned int row;
+        for (row = 0; row < head->ysize; ++row) {
+            int const sgiIndex = channel * head->ysize + row;
+
+            unsigned long int iindex;
 
-            sgi_index = channel * head->ysize + row;
-            iindex = (ochan < 0) ? sgi_index : row;
-            if (ochan < 0 || ochan == channel)
+            iindex = outChannelSpec ? row : sgiIndex;
+            if (!outChannelSpec || outChannel == channel)
                 MALLOCARRAY_NOFAIL(image[iindex], head->xsize);
 
-            if( table ) {
-                if (channel < ochan)
-                    continue;
-
-                offset = table[sgi_index].start;
-                length = table[sgi_index].length;
-                if( head->bpc == 2 )
-                    length /= 2;   
-                    /* doc says length is in bytes, we are reading words */
-                if( fseek(ifp, offset, SEEK_SET) != 0 )
-                    pm_error("seek error for offset %ld", offset);
-
-                for( i = 0; i < length; i++ )
-                    temp[i] = (*func)(ifp);
-                rle_decompress(temp, length, image[iindex], head->xsize);
-            }
-            else {
-                for( i = 0; i < head->xsize; i++ )
-                    image[iindex][i] = (*func)(ifp);
+            if (table) {
+                if (!outChannelSpec || channel >= outChannel) {
+                    pm_filepos const offset = (pm_filepos)
+                        table[sgiIndex].start;
+                    long const length = head->bpc == 2 ?
+                        table[sgiIndex].length / 2 :
+                        table[sgiIndex].length;
+
+                    unsigned int i;
+
+                    /* Note: (offset < currentPosition) can happen */
+
+                    pm_seek2(ifP, &offset, sizeof(offset));
+
+                    for (i = 0; i < length; ++i)
+                        if (head->bpc == 1)
+                            temp[i] = getByteAsShort(ifP);
+                        else
+                            temp[i] = getBigShort(ifP);
+                    rleDecompress(temp, length, image[iindex], head->xsize);
+                }
+            } else {
+                unsigned int i;
+                for (i = 0; i < head->xsize; ++i) {
+                    ScanElem p;
+                    if (head->bpc == 1)
+                        p = getByteAsShort(ifP);
+                    else
+                        p = getBigShort(ifP);
+
+                    if (!outChannelSpec || outChannel == channel)
+                        image[iindex][i] = p;
+                }
             }
         }
     }
-
-    if( table ) free(temp);
+    if (table)
+        free(temp);
     return image;
 }
 
 
+
 static void
-image_to_pnm(Header *head, ScanLine *image, xelval maxval, int channel)
-{
-    int col, row, format;
-    xel *pnmrow = pnm_allocrow(head->xsize);
-    int sub = head->pixmin;
+imageToPnm(Header   *   const head,
+           ScanLine *   const image,
+           xelval       const maxval,
+           bool         const outChannelSpec,
+           unsigned int const outChannel) {
+
+    int const sub = head->pixmin;
+    xel * const pnmrow = pnm_allocrow(head->xsize);
+
+    int row;
+    int format;
 
-    if( head->zsize == 1 || channel >= 0) {
+    if (head->zsize <= 2 || outChannelSpec) {
         pm_message("writing PGM image");
         format = PGM_TYPE;
-    }
-    else {
+    } else {
         pm_message("writing PPM image");
         format = PPM_TYPE;
     }
 
-    pnm_writepnminit(stdout, head->xsize, head->ysize, (xelval)maxval, format, 0);
-    for( row = head->ysize-1; row >= 0; row-- ) {
-        for( col = 0; col < head->xsize; col++ ) {
-            if( format == PGM_TYPE )
+    pnm_writepnminit(stdout, head->xsize, head->ysize, maxval, format, 0);
+    for (row = head->ysize-1; row >= 0; --row) {
+        unsigned int col;
+        for (col = 0; col < head->xsize; ++col) {
+            if (format == PGM_TYPE)
                 PNM_ASSIGN1(pnmrow[col], image[row][col] - sub);
             else {
                 pixval r, g, b;
@@ -329,135 +471,54 @@ image_to_pnm(Header *head, ScanLine *image, xelval maxval, int channel)
                 PPM_ASSIGN(pnmrow[col], r, g, b);
             }
         }
-        pnm_writepnmrow(stdout, pnmrow, head->xsize, (xelval)maxval, format, 0);
+        pnm_writepnmrow(stdout, pnmrow, head->xsize, maxval, format, 0);
     }
     pnm_freerow(pnmrow);
 }
 
 
-static void
-rle_decompress(src, srcleft, dest, destleft)
-    ScanElem *src;
-    int srcleft;
-    ScanElem *dest;
-    int destleft;
-{
-    int count;
-    unsigned char el;
-
-    while( srcleft ) {
-        el = (unsigned char)(*src++ & 0xff);
-        --srcleft;
-        count = (int)(el & 0x7f);
 
-        if( count == 0 )
-            return;
-        if( destleft < count )
-            pm_error("RLE error: too much input data (space left %d, need %d)", destleft, count);
-        destleft -= count;
-        if( el & 0x80 ) {
-            if( srcleft < count )
-                pm_error("RLE error: not enough data for literal run (data left %d, need %d)", srcleft, count);
-            srcleft -= count;
-            while( count-- )
-                *dest++ = *src++;
-        }
-        else {
-            if( srcleft == 0 )
-                pm_error("RLE error: not enough data for replicate run");
-            while( count-- )
-                *dest++ = *src;
-            ++src;
-            --srcleft;
-        }
-    }
-    pm_error("RLE error: no terminating 0-byte");
-}
-
-
-/* basic I/O functions, taken from ilbmtoppm.c */
-
-static short
-get_big_short(ifp)
-    FILE *ifp;
-{
-    short s;
-
-    if( pm_readbigshort(ifp, &s) == -1 )
-        readerr(ifp);
-
-    return s;
-}
+int
+main(int argc, const char * argv[]) {
 
-static long
-get_big_long(ifp)
-    FILE *ifp;
-{
-    long l;
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    TabEntry * table;
+    ScanLine * image;
+    Header * headP;
+    xelval maxval;
 
-    if( pm_readbiglong(ifp, &l) == -1 )
-        readerr(ifp);
+    pm_proginit(&argc, argv);
 
-    return l;
-}
+    parseCommandLine(argc, argv, &cmdline);
 
-static unsigned char
-get_byte(ifp)
-    FILE* ifp;
-{
-    int i;
+    ifP = pm_openr_seekable(cmdline.inputFileName);
 
-    i = getc(ifp);
-    if( i == EOF )
-        readerr(ifp);
+    headP = readHeader(ifP, cmdline.channelSpec, cmdline.verbose);
 
-    return (unsigned char) i;
-}
+    maxval = headP->pixmax - headP->pixmin;
+    if (maxval > PNM_OVERALLMAXVAL)
+        pm_error("Maximum sample value in input image (%d) is too large.  "
+                 "This program's limit is %d.",
+                 maxval, PNM_OVERALLMAXVAL);
 
+    if (cmdline.channelSpec && cmdline.channel >= headP->zsize)
+        pm_error("channel out of range - only %d channels in image",
+                 headP->zsize);
 
-static void
-readerr(f)
-    FILE *f;
-{
-    if( ferror(f) )
-        pm_error("read error");
+    if (headP->storage != STORAGE_VERBATIM)
+        table = readTable(ifP, headP->ysize * headP->zsize);
     else
-        pm_error("premature EOF");
-}
-
+        table = NULL;
 
-static void
-read_bytes(ifp, n, buf)
-    FILE *ifp;
-    int n;
-    char *buf;
-{
-    int r;
+    image = readChannels(ifP, headP, table,
+                         cmdline.channelSpec, cmdline.channel);
 
-    r = fread((void *)buf, 1, n, ifp);
-    if( r != n )
-        readerr(ifp);
-}
+    imageToPnm(headP, image, maxval, cmdline.channelSpec, cmdline.channel);
 
+    pm_close(ifP);
 
-static short
-get_byte_as_short(ifp)
-    FILE *ifp;
-{
-    return (short)get_byte(ifp);
+    return 0;
 }
 
 
-static const char *
-compression_name(char compr)
-{
-    switch( compr ) {
-        case STORAGE_VERBATIM:
-            return "none";
-        case STORAGE_RLE:
-            return "RLE";
-        default:
-            return "unknown";
-    }
-}
-
diff --git a/converter/other/srf.c b/converter/other/srf.c
new file mode 100644
index 00000000..b0f97242
--- /dev/null
+++ b/converter/other/srf.c
@@ -0,0 +1,653 @@
+/*
+ * Funcs for working with SRF (Garmin vehicle) files
+ *
+ * Written by Mike Frysinger <vapier@gentoo.org>
+ * Released into the public domain
+ */
+
+#include <stdio.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "srf.h"
+
+
+static unsigned char
+csumRaw(void * const p,
+       size_t const len) {
+
+    unsigned char retval;
+
+    unsigned int i;
+    unsigned char * c;
+
+    for (i = 0, retval = 0, c = p; i < len; ++i)
+        retval += *c++;
+    
+    return retval;
+}
+
+
+
+static unsigned char
+csumPstring(struct srf_pstring * const pstringP) {
+
+  return
+      csumRaw(&pstringP->len, 4) + csumRaw(pstringP->val, pstringP->len);
+}
+
+
+
+static bool
+readPstring(FILE *               const ifP,
+            struct srf_pstring * const pstringP) {
+
+    size_t bytesRead;
+
+    pm_readlittlelong2u(ifP, &pstringP->len);
+
+    MALLOCARRAY(pstringP->val, pstringP->len + 1);
+
+    if (!pstringP->val)
+        pm_error("Failed to allocate buffer to read %u-byte pstring",
+                 pstringP->len);
+
+    bytesRead = fread(pstringP->val, 1, pstringP->len, ifP);
+    if (bytesRead != pstringP->len)
+        pm_error("Failed to read pstring.  Requested %u bytes, got %u",
+                 (unsigned)pstringP->len,  (unsigned)bytesRead);
+
+    pstringP->val[pstringP->len] = '\0';
+
+    return true;
+}
+
+
+
+static bool
+writePstring(FILE *               const ofP,
+             struct srf_pstring * const pstringP) {
+
+    bool retval;
+    size_t bytesWritten;
+
+    pm_writelittlelongu(ofP, pstringP->len);
+
+    bytesWritten = fwrite(pstringP->val, 1, pstringP->len, ofP);
+
+    if (bytesWritten == pstringP->len)
+        retval = true;
+    else
+        retval = false;
+
+    return retval;
+}
+
+
+
+static size_t
+lenHeader(struct srf_header * const headerP) {
+
+    return 16 + (4 * 4) + 4 + headerP->s578.len
+        + 4 + 4 + headerP->ver.len
+        + 4 + 4 + headerP->prod.len;
+}
+
+
+
+static unsigned char
+csumHeader(struct srf_header * const headerP) {
+
+    return
+        csumRaw(headerP->magic, 16) +
+        csumRaw(&headerP->_int4, 2 * 4) +
+        csumRaw(&headerP->img_cnt, 4) +
+        csumRaw(&headerP->_int5, 4) +
+        csumPstring(&headerP->s578) +
+        csumRaw(&headerP->_int6, 4) +
+        csumPstring(&headerP->ver) +
+        csumRaw(&headerP->_int7, 4) +
+        csumPstring(&headerP->prod);
+}
+
+
+
+static bool
+readHeader(FILE *              const ifP,
+           struct srf_header * const headerP) {
+
+    bool const retval =
+        fread(headerP->magic, 1, 16, ifP) == 16 &&
+        pm_readlittlelong2u(ifP, &headerP->_int4[0]) == 0 &&
+        pm_readlittlelong2u(ifP, &headerP->_int4[1]) == 0 &&
+        pm_readlittlelong2u(ifP, &headerP->img_cnt) == 0 &&
+        pm_readlittlelong2u(ifP, &headerP->_int5) == 0 &&
+        readPstring(ifP, &headerP->s578) &&
+        pm_readlittlelong2u(ifP, &headerP->_int6) == 0 &&
+        readPstring(ifP, &headerP->ver) &&
+        pm_readlittlelong2u(ifP, &headerP->_int7) == 0 &&
+        readPstring(ifP, &headerP->prod);
+
+    headerP->magic[16] = '\0';
+
+    return retval;
+}
+
+
+
+static bool
+writeHeader(FILE *              const ofP,
+            struct srf_header * const headerP) {
+
+    return
+        fwrite(headerP->magic, 1, 16, ofP) == 16 &&
+        pm_writelittlelongu(ofP, headerP->_int4[0]) == 0 &&
+        pm_writelittlelongu(ofP, headerP->_int4[1]) == 0 &&
+        pm_writelittlelongu(ofP, headerP->img_cnt) == 0 &&
+        pm_writelittlelongu(ofP, headerP->_int5) == 0 &&
+        writePstring(ofP, &headerP->s578) &&
+        pm_writelittlelongu(ofP, headerP->_int6) == 0 &&
+        writePstring(ofP, &headerP->ver) &&
+        pm_writelittlelongu(ofP, headerP->_int7) == 0 &&
+        writePstring(ofP, &headerP->prod);
+}
+
+
+
+static bool
+checkHeader(struct srf_header * const headerP) {
+
+    return
+        streq(headerP->magic, SRF_MAGIC) &&
+        headerP->_int4[0] == 4 &&
+        headerP->_int4[1] == 4 &&
+        /* Should we require img_cnt to be multiple of 2 ? */
+        headerP->img_cnt > 0 &&
+        headerP->_int5 == 5 &&
+        headerP->s578.len == 3 &&
+        strcmp(headerP->s578.val, "578") == 0 &&
+        headerP->_int6 == 6 &&
+        headerP->ver.len == 4 &&
+        /* Allow any headerP->ver value */
+        headerP->_int7 == 7 &&
+        headerP->prod.len == 12;
+    /* Allow any headerP->prod value */
+}
+
+
+
+static size_t
+lenImg(struct srf_img * const imgP) {
+
+    return
+        (4 * 3) + (2 * 2) + (1 * 2) + 2 + 4 +
+        4 + 4 + imgP->alpha.data_len +
+        4 + 4 + imgP->data.data_len;
+}
+
+
+
+static unsigned char
+csumImg(struct srf_img * const imgP) {
+
+    struct srf_img_header * const headerP = &imgP->header;
+    struct srf_img_alpha *  const alphaP  = &imgP->alpha;
+    struct srf_img_data *   const dataP   = &imgP->data;
+
+    return
+        csumRaw(&headerP->_ints, 4 * 3) +
+        csumRaw(&headerP->height, 2) +
+        csumRaw(&headerP->width, 2) +
+        csumRaw(headerP->_bytes, 2) +
+        csumRaw(&headerP->line_len, 2) +
+        csumRaw(&headerP->zeros, 4) +
+        csumRaw(&alphaP->type, 4) +
+        csumRaw(&alphaP->data_len, 4) +
+        csumRaw(alphaP->data, alphaP->data_len) +
+        csumRaw(&dataP->type, 4) +
+        csumRaw(&dataP->data_len, 4) +
+        csumRaw(dataP->data, dataP->data_len);
+}
+
+
+
+static bool
+readImgHeader(FILE *                  const ifP,
+              struct srf_img_header * const headerP) {
+    return
+        pm_readlittlelong2u(ifP, &headerP->_ints[0]) == 0 &&
+        pm_readlittlelong2u(ifP, &headerP->_ints[1]) == 0 &&
+        pm_readlittlelong2u(ifP, &headerP->_ints[2]) == 0 &&
+        pm_readlittleshortu(ifP, &headerP->height) == 0 &&
+        pm_readlittleshortu(ifP, &headerP->width) == 0 &&
+        fread(&headerP->_bytes[0], 1, 1, ifP) == 1 &&
+        fread(&headerP->_bytes[1], 1, 1, ifP) == 1 &&
+        pm_readlittleshortu(ifP, &headerP->line_len) == 0 &&
+        pm_readlittlelong2u(ifP, &headerP->zeros) == 0;
+}
+
+
+
+static bool
+writeImgHeader(FILE *                  const ofP,
+               struct srf_img_header * const headerP) {
+    return
+        pm_writelittlelongu(ofP, headerP->_ints[0]) == 0 &&
+        pm_writelittlelongu(ofP, headerP->_ints[1]) == 0 &&
+        pm_writelittlelongu(ofP, headerP->_ints[2]) == 0 &&
+        pm_writelittleshortu(ofP, headerP->height) == 0 &&
+        pm_writelittleshortu(ofP, headerP->width) == 0 &&
+        fwrite(&headerP->_bytes[0], 1, 1, ofP) == 1 &&
+        fwrite(&headerP->_bytes[1], 1, 1, ofP) == 1 &&
+        pm_writelittleshortu(ofP, headerP->line_len) == 0 &&
+        pm_writelittlelongu(ofP, headerP->zeros) == 0;
+}
+
+
+
+static bool
+checkImgHeader(struct srf_img_header * const headerP) {
+
+    return
+        headerP->_ints[0] == 0 &&
+        headerP->_ints[1] == 16 &&
+        headerP->_ints[2] == 0 &&
+        headerP->_bytes[0] == 16 &&
+        headerP->_bytes[1] == 8 &&
+        headerP->line_len == headerP->width * 2 &&
+        headerP->zeros == 0;
+}
+
+
+
+static bool
+readImgAlpha(FILE *                 const ifP,
+                   struct srf_img_alpha * const alphaP) {
+
+    bool retval;
+
+    pm_readlittlelong2u(ifP, &alphaP->type);
+    pm_readlittlelong2u(ifP, &alphaP->data_len);
+
+    MALLOCARRAY(alphaP->data, alphaP->data_len);
+
+    if (!alphaP->data)
+        retval = false;
+    else {
+        size_t bytesRead;
+
+        bytesRead = fread(alphaP->data, 1, alphaP->data_len, ifP);
+        retval = (bytesRead ==alphaP->data_len);
+    }
+    return retval;
+}
+
+
+
+static bool
+writeImageAlpha(FILE *                 const ofP,
+                    struct srf_img_alpha * const alphaP) {
+
+    return
+        pm_writelittlelongu(ofP, alphaP->type) == 0 &&
+        pm_writelittlelongu(ofP, alphaP->data_len) == 0 &&
+        fwrite(alphaP->data, 1, alphaP->data_len, ofP) == alphaP->data_len;
+}
+
+
+
+static bool
+checkImgAlpha(struct srf_img_alpha * const alphaP) {
+
+    return (alphaP->type == 11);
+}
+
+
+
+static bool
+readImgData(FILE *                const ifP,
+                  struct srf_img_data * const dataP) {
+
+    bool retval;
+
+    pm_readlittlelong2u(ifP, &dataP->type);
+    pm_readlittlelong2u(ifP, &dataP->data_len);
+
+    MALLOCARRAY(dataP->data, dataP->data_len / 2);
+
+    if (!dataP->data)
+        retval = false;
+    else {
+        size_t bytesRead;
+        bytesRead = fread(dataP->data, 2, dataP->data_len / 2, ifP);
+
+        retval = (bytesRead == dataP->data_len / 2);
+    }
+    return retval;
+}
+
+
+
+static bool
+writeImgData(FILE *                const ofP,
+                   struct srf_img_data * const dataP) {
+
+    return
+        pm_writelittlelongu(ofP, dataP->type) == 0 &&
+        pm_writelittlelongu(ofP, dataP->data_len) == 0 &&
+        fwrite(dataP->data, 2, dataP->data_len / 2, ofP)
+        == dataP->data_len / 2;
+}
+
+
+
+static bool
+checkImgData(struct srf_img_data * const dataP) {
+  return dataP->type == 1;
+}
+
+
+
+static bool
+readImg(FILE *           const ifP,
+             bool             const verbose,
+             uint32_t         const i,
+             struct srf_img * const imgP) {
+
+    if (!readImgHeader(ifP, &imgP->header))
+        pm_error("short srf image %u header", i);
+    if (!checkImgHeader(&imgP->header))
+        pm_error("invalid srf image %u header", i);
+
+    if (verbose)
+        pm_message("reading srf 16-bit RGB %ux%u image %u",
+                   imgP->header.width, imgP->header.height, i);
+
+    if (!readImgAlpha(ifP, &imgP->alpha))
+        pm_error("short srf image %u alpha mask", i);
+    if (!checkImgAlpha(&imgP->alpha))
+        pm_error("invalid srf image %u alpha mask", i);
+
+    if (!readImgData(ifP, &imgP->data))
+        pm_error("short srf image %u data", i);
+    if (!checkImgData(&imgP->data))
+        pm_error("invalid srf image %u data", i);
+
+    return true;
+}
+
+
+
+static bool
+writeImg(FILE *           const ofP,
+              uint32_t         const i,
+              struct srf_img * const imgP) {
+
+    if (!checkImgHeader(&imgP->header))
+        pm_error("invalid srf image %u header", i);
+    if (!writeImgHeader(ofP, &imgP->header))
+        pm_error("short srf image %u header", i);
+
+    if (!checkImgAlpha(&imgP->alpha))
+        pm_error("invalid srf image %u alpha mask", i);
+    if (!writeImageAlpha(ofP, &imgP->alpha))
+        pm_error("short srf image %u alpha mask", i);
+
+    if (!checkImgData(&imgP->data))
+        pm_error("invalid srf image %u data", i);
+    if (!writeImgData(ofP, &imgP->data))
+        pm_error("short srf image %u data", i);
+
+    return true;
+}
+
+
+
+static uint8_t
+csum(struct srf * const srfP,
+     size_t       const padLen) {
+/*----------------------------------------------------------------------------
+   The sum of everything in the SRF image except the checksum byte.  The
+   checksum byte is supposed to be the arithmetic opposite of this so that the
+   sum of everything is zero.
+-----------------------------------------------------------------------------*/
+    unsigned char retval;
+    unsigned int i;
+
+    retval = csumHeader(&srfP->header);
+
+    for (i = 0; i < srfP->header.img_cnt; ++i)
+        retval += csumImg(&srfP->imgs[i]);
+    
+    for (i = 0; i < padLen; ++i)
+        retval += 0xff;
+    
+    return retval;
+}
+
+
+
+void
+srf_read(FILE *       const ifP,
+         bool         const verbose,
+         struct srf * const srfP) {
+
+    uint8_t       trialCsum;
+    size_t        padLen;
+    unsigned char pad[256];
+    unsigned int  i;
+
+    if (!readHeader(ifP, &srfP->header))
+        pm_error("short srf header");
+    if (!checkHeader(&srfP->header))
+        pm_error("invalid srf header");
+
+    if (verbose)
+        pm_message("reading srf ver %s with prod code %s and %u images",
+                   srfP->header.ver.val, srfP->header.prod.val,
+                   srfP->header.img_cnt);
+
+    MALLOCARRAY(srfP->imgs, srfP->header.img_cnt);
+
+    if (!srfP->imgs)
+        pm_error("Could not allocate memory for %u images",
+                 srfP->header.img_cnt);
+
+    for (i = 0; i < srfP->header.img_cnt; ++i)
+        if (!readImg(ifP, verbose, i, &srfP->imgs[i]))
+            pm_error("invalid srf image %u", i);
+
+    padLen = fread(pad, 1, sizeof(pad), ifP);
+    if (!feof(ifP)) {
+        pm_errormsg("excess data at end of file");
+        return;
+    }
+
+    trialCsum = csum(srfP, 0);  /* initial value */
+    for (i = 0; i < padLen; ++i)
+        trialCsum += pad[i];
+    if (trialCsum != 0)
+        pm_errormsg("checksum does not match");
+}
+
+
+
+void
+srf_write(FILE *       const ofP,
+          struct srf * const srfP) {
+
+    uint8_t      srfCsum;    /* checksum value in SRF image */
+    size_t       padLen;
+    unsigned int i;
+    size_t       bytesWritten;
+
+    padLen = 1;  /* initial value */
+
+    if (!checkHeader(&srfP->header))
+        pm_error("invalid srf header");
+    if (!writeHeader(ofP, &srfP->header))
+        pm_error("write srf header");
+    padLen += lenHeader(&srfP->header);
+
+    for (i = 0; i < srfP->header.img_cnt; ++i) {
+        if (!writeImg(ofP, i, &srfP->imgs[i]))
+            pm_error("invalid srf image %u", i);
+        padLen += lenImg(&srfP->imgs[i]);
+    }
+
+    /* Pad to 256 bytes */
+    padLen = 256 - (padLen % 256);
+    if (padLen) {
+        char * d;
+        size_t bytesWritten;
+
+        MALLOCARRAY(d, padLen);
+
+        if (!d)
+            pm_error("Could not allocate memory for %u bytes of padding",
+                     (unsigned)padLen);
+
+        memset(d, 0xff, padLen);
+
+        bytesWritten = fwrite(d, 1, padLen, ofP);
+
+        if (bytesWritten != padLen)
+            pm_error("unable to 0xff pad file");
+
+        free(d);
+    }
+
+    /* Write out checksum byte */
+    srfCsum = 0xff - csum(srfP, padLen) + 1;
+    bytesWritten = fwrite(&srfCsum, 1, 1, ofP);
+    if (bytesWritten != 1)
+        pm_error("unable to write checksum");
+}
+
+
+
+static void
+freeImg(struct srf_img * const imgP) {
+
+    free(imgP->alpha.data);
+    free(imgP->data.data);
+}
+
+
+
+void
+srf_term(struct srf * const srfP) {
+
+    unsigned int i;
+    
+    free(srfP->header.s578.val);
+    free(srfP->header.ver.val);
+    free(srfP->header.prod.val);
+
+    for (i = 0; i < srfP->header.img_cnt; ++i)
+        freeImg(&srfP->imgs[i]);
+
+    free(srfP->imgs);
+}
+
+
+
+static void
+srf_img_init(struct srf_img * const imgP,
+             uint16_t         const width,
+             uint16_t         const height) {
+
+    struct srf_img_header * const headerP = &imgP->header;
+    struct srf_img_alpha *  const alphaP  = &imgP->alpha;
+    struct srf_img_data *   const dataP   = &imgP->data;
+
+    headerP->_ints[0]   = 0;
+    headerP->_ints[1]   = 16;
+    headerP->_ints[2]   = 0;
+    headerP->height     = height;
+    headerP->width      = width;
+    headerP->_bytes[0]  = 16;
+    headerP->_bytes[1]  = 8;
+    headerP->line_len   = width * 2;
+    headerP->zeros      = 0;
+
+    alphaP->type     = 11;
+    alphaP->data_len = height * width;
+    MALLOCARRAY(alphaP->data, alphaP->data_len);
+
+    if (!alphaP->data)
+        pm_error("Could not allocate buffer for %u bytes of alpha",
+                 alphaP->data_len);
+
+    dataP->type     = 1;
+    dataP->data_len = height * width * 2;
+    MALLOCARRAY(dataP->data, dataP->data_len / 2);
+
+    if (!dataP->data)
+        pm_error("Could not allocation buffer for %u units of data",
+                 dataP->data_len);
+}
+
+
+
+static void
+initPstring(struct srf_pstring * const pstringP,
+            const char *         const s) {
+
+    pstringP->len = strlen(s);
+
+    MALLOCARRAY(pstringP->val, pstringP->len + 1);
+
+    if (!pstringP->val)
+        pm_error("Could not allocate memory for string of length %u",
+                 pstringP->len);
+
+    memcpy(pstringP->val, s, pstringP->len + 1);
+}
+
+
+
+void
+srf_init(struct srf * const srfP) {
+
+    struct srf_header * const headerP = &srfP->header;
+
+    strcpy(headerP->magic, SRF_MAGIC);
+    headerP->_int4[0] = 4;
+    headerP->_int4[1] = 4;
+    headerP->img_cnt = 0;
+    headerP->_int5 = 5;
+    initPstring(&headerP->s578, "578");
+    headerP->_int6 = 6;
+    initPstring(&headerP->ver, "1.00");
+    headerP->_int7 = 7;
+    initPstring(&headerP->prod, "006-D0578-XX");
+
+    srfP->imgs = NULL;
+}
+
+
+
+void
+srf_create_img(struct srf * const srfP,
+               uint16_t     const width,
+               uint16_t     const height) {
+/*----------------------------------------------------------------------------
+   Add an "image" to the SRF.  An image is a horizontal series of 36 
+   square frames, each showing a different angle view of an object, 10
+   degrees about.  At least that's what it's supposed to be.  We don't
+   really care -- it's just an arbitrary rectangular raster image to us.
+-----------------------------------------------------------------------------*/
+    
+    ++srfP->header.img_cnt;
+
+    REALLOCARRAY(srfP->imgs, srfP->header.img_cnt);
+
+    if (!srfP->imgs)
+        pm_error("Could not allocate memory for %u images",
+                 srfP->header.img_cnt);
+
+    srf_img_init(&srfP->imgs[srfP->header.img_cnt-1], width, height);
+}
+                 
diff --git a/converter/other/srf.h b/converter/other/srf.h
new file mode 100644
index 00000000..e06355a4
--- /dev/null
+++ b/converter/other/srf.h
@@ -0,0 +1,170 @@
+#ifndef SRF_H_INCLUDED
+#define SRF_H_INCLUDED
+
+/*
+ * Structures for working with SRF (Garmin vehicle) files
+ * http://www.techmods.net/nuvi/
+ *
+ * Written by Mike Frysinger <vapier@gentoo.org>
+ * Released into the public domain
+ */
+
+#include "pm_config.h"
+#include "pam.h"
+
+struct srf_pstring {
+    uint32_t len;
+    char *   val;
+};
+
+#define SRF_NUM_FRAMES 36
+
+/*
+  File Header
+      16 bytes - string - "GARMIN BITMAP 01"
+      32 bytes - two 32-bit ints, [4, 4] -- purpose unknown
+      4 bytes - 32-bit int -- number of images (usually just 2)
+      4 bytes - 32-bit int, [5] -- purpose unknown
+      7 bytes - PString - "578"
+      4 bytes - 32-bit int, [6] -- purpose unknown
+      8 bytes - PString - version number ("1.00", "2.00", "2.10", or "2.20")
+      4 bytes - 32-bit int, [7] -- purpose unknown
+      16 bytes - PString - "006-D0578-XX" (where "XX" changes) --
+                           I assume this is Garmin's product code?
+*/
+#define SRF_MAGIC "GARMIN BITMAP 01"
+
+struct srf_header {
+    char magic[16 + 1];
+
+    uint32_t           _int4[2];
+
+    uint32_t           img_cnt;
+
+    uint32_t           _int5;
+
+    struct srf_pstring s578;
+
+    uint32_t           _int6;
+
+    struct srf_pstring ver;
+
+    uint32_t           _int7;
+
+    struct srf_pstring prod;
+};
+
+/*
+  Image Header
+     12 bytes - three 32-bit ints, [0,16,0] -- purpose unknown
+     2 bytes - 16-bit int -- height of image (just the 3D section, so it's 80)
+     2 bytes - 16-bit int -- width of image (just the 3D section, 2880 or 2881)
+     2 bytes - [16, 8] -- purpose unknown
+     2 bytes - 16-bit int -- byte length of each line of image RGB data
+                             (16-bit RGB), so "width * 2"
+     4 bytes - all zeroes -- purpose unknown
+*/
+struct srf_img_header {
+    uint32_t _ints[3];
+
+    uint16_t height, width;
+
+    char     _bytes[2];
+
+    uint16_t line_len;
+
+    uint32_t zeros;
+};
+
+/*
+  Image Alpha Mask
+
+      4 bytes - 32-bit int, [11] -- Might specify the type of data that
+                follows?
+
+      4 bytes - 32-bit int, length of following data (width*height of 3D
+                section)
+
+      width*height bytes - alpha mask data, 0 = opaque, 128 = transparent
+                           (across, then down)
+
+  Notes: The Garmin format has 129 values: [0..128] [opaque..transparent]
+         The PNG format has 256 values:    [0..255] [transparent..opaque]
+         So we have to do a little edge case tweaking to keep things lossless.
+*/
+
+#define SRF_ALPHA_OPAQUE 0
+#define SRF_ALPHA_TRANS  128
+
+struct srf_img_alpha {
+    uint32_t        type;
+
+    uint32_t        data_len;
+    unsigned char * data;
+};
+
+/*
+  Image RGB Data
+      4 bytes - 32-bit int, [1] -- Might specify the type of data that follows?
+      4 bytes - 32-bit int, length of following data (width*height*2 of 3D
+                            section, as the RGB data is 16-bit)
+      width*height*2 bytes - RBG values as "rrrrrggggg0bbbbb" bits
+                             (across, then down)
+*/
+
+struct srf_img_data {
+    uint32_t   type;
+
+    uint32_t   data_len;
+    uint16_t * data;
+};
+
+struct srf_img {
+    struct srf_img_header header;
+    struct srf_img_alpha  alpha;
+    struct srf_img_data   data;
+};
+
+/*
+  Footer
+      arbitrary number of bytes - all 0xFF -- these are used (as well as the
+                                              checksum byte) to pad the file
+                                              size to a multiple of 256.
+
+      1 byte - checksum byte -- use this byte to adjust so that the ascii sum
+                                of all bytes in the file is a multiple of 256.
+ */
+
+struct srf {
+    struct srf_header header;
+    struct srf_img *  imgs;
+};
+
+uint8_t
+srf_alpha_srftopam(uint8_t const d);
+
+uint8_t
+srf_alpha_pamtosrf(uint8_t const d);
+
+void
+srf_read(FILE *       const ifP,
+         bool         const verbose,
+         struct srf * const srfP);
+
+void
+srf_write(FILE *       const ofP,
+          struct srf * const srfP);
+
+void
+srf_term(struct srf * const srfP);
+
+void
+srf_init(struct srf * const srfP);
+
+void
+srf_create_img(struct srf * const srfP,
+               uint16_t     const width,
+               uint16_t     const height);
+
+#endif
+
diff --git a/converter/other/srftopam.c b/converter/other/srftopam.c
new file mode 100644
index 00000000..efe55253
--- /dev/null
+++ b/converter/other/srftopam.c
@@ -0,0 +1,226 @@
+/*
+ * Convert a SRF (Garmin vehicle) to a PAM image
+ *
+ * Copyright (C) 2011 by Mike Frysinger <vapier@gentoo.org>
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "pam.h"
+#include "srf.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;  /* '-' if stdin */
+  unsigned int  verbose;
+};
+
+static bool verbose;
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+    /* Instructions to pm_optParseOptions3 on how to parse our options.
+     */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "verbose",          OPT_FLAG,      NULL,
+            &cmdlineP->verbose,    0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFileName = argv[1];
+    else
+        pm_error("Program takes at most one argument:  input file name");
+}
+
+
+
+static unsigned int
+srfRed(uint16_t const pixel) {
+    return ((pixel >> 11) & 0x1f) << 3;
+}
+
+
+static unsigned int
+srfGrn(uint16_t const pixel) {
+    return ((pixel >>  6) & 0x1f) << 3;
+}
+
+
+static unsigned int
+srfBlu(uint16_t const pixel) {
+    return ((pixel >>  0) & 0x1f) << 3;
+}
+
+
+static uint8_t
+srfAlpha(uint8_t const d) {
+
+    uint8_t retval;
+
+    if (d == SRF_ALPHA_OPAQUE)
+        retval = 0xff;
+    else
+        retval = (128 - d) << 1;
+
+    return retval;
+}
+
+
+
+static void
+writeRaster(struct pam *     const pamP,
+            struct srf_img * const imgP) {
+
+    tuple *  tuplerow;
+    unsigned int row;
+
+    assert(imgP->header.width <= pamP->width);
+
+    tuplerow = pnm_allocpamrow(pamP);
+
+    for (row = 0; row < imgP->header.height; ++row) {
+        unsigned int const rowStart = row * imgP->header.width;
+
+        unsigned int col;
+
+        for (col = 0; col < imgP->header.width; ++col) {
+            uint16_t const data  = imgP->data.data[rowStart + col];
+            uint16_t const alpha = imgP->alpha.data[rowStart + col];
+
+            assert(col < pamP->width);
+            
+            tuplerow[col][PAM_RED_PLANE] = srfRed(data);
+            tuplerow[col][PAM_GRN_PLANE] = srfGrn(data);
+            tuplerow[col][PAM_BLU_PLANE] = srfBlu(data);
+            tuplerow[col][PAM_TRN_PLANE] = srfAlpha(alpha);
+        }
+
+        for (; col < pamP->width; ++col) {
+            tuplerow[col][PAM_RED_PLANE] = 0;
+            tuplerow[col][PAM_GRN_PLANE] = 0;
+            tuplerow[col][PAM_BLU_PLANE] = 0;
+            tuplerow[col][PAM_TRN_PLANE] = 0;
+        }            
+
+        pnm_writepamrow(pamP, tuplerow);
+    }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+convertOneImage(struct srf_img * const imgP,
+                FILE *           const ofP) {
+
+    const char * comment = "Produced by srftopam";  /* constant */
+
+    struct pam   outPam;
+
+    outPam.size             = sizeof(struct pam);
+    outPam.len              = PAM_STRUCT_SIZE(comment_p);
+    outPam.file             = ofP;
+    outPam.format           = PAM_FORMAT;
+    outPam.plainformat      = 0;
+    outPam.width            = imgP->header.width;
+    outPam.height           = imgP->header.height;
+    outPam.depth            = 4;
+    outPam.maxval           = 255;
+    outPam.bytes_per_sample = 1;
+    sprintf(outPam.tuple_type, "RGB_ALPHA");
+    outPam.allocation_depth = 4;
+    outPam.comment_p        = &comment;
+
+    pnm_writepaminit(&outPam);
+
+    writeRaster(&outPam, imgP);
+}
+
+
+
+static void
+srftopam(struct cmdlineInfo const cmdline,
+         FILE *             const ifP,
+         FILE *             const ofP) {
+
+    unsigned int imgSeq;
+    struct srf   srf;
+
+    srf_read(ifP, verbose, &srf);
+
+    for (imgSeq = 0; imgSeq < srf.header.img_cnt; ++imgSeq) {
+        if (verbose)
+            pm_message("Converting Image %u", imgSeq);
+        convertOneImage(&srf.imgs[imgSeq], ofP);
+    }
+
+    srf_term(&srf);
+}
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE *             ifP;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    srftopam(cmdline, ifP, stdout);
+
+    pm_closer(ifP);
+
+    return 0;
+}
+
+
+
diff --git a/converter/other/sunicontopnm.c b/converter/other/sunicontopnm.c
new file mode 100644
index 00000000..eff1be58
--- /dev/null
+++ b/converter/other/sunicontopnm.c
@@ -0,0 +1,172 @@
+/* icontopbm.c - read a Sun icon file and produce a Netbpbm image.
+**
+** Copyright (C) 1988 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.
+*/
+
+/*
+  Most icon images are monochrome: Depth=1
+  Depth=8 images are extremely rare.  At least some of these are color
+  images but we can't tell the palette color order.
+  Output will be in pgm.  Convert to ppm with pgmtoppm or pamlookup
+  if necessary.
+*/
+
+#include <assert.h>
+#include <string.h>
+
+#include "nstring.h"
+#include "pbm.h"
+#include "pgm.h"
+
+
+
+static void
+ReadIconFileHeader(FILE * const file, 
+                   int *  const widthP, 
+                   int *  const heightP, 
+                   int *  const depthP,
+                   int *  const bitsPerItemP) {
+
+    unsigned int fieldCt;
+
+    fieldCt = 0;
+    *widthP = *heightP = -1;
+
+    for ( ; ; ) {
+        char variable[80+1];
+        int ch;
+        unsigned int i;
+        int value;
+
+        while ((ch = getc(file)) == ',' || ch == '\n' || ch == '\t' ||
+                ch == ' ')
+            ;
+        for (i = 0;
+             ch != '=' && ch != ',' && ch != '\n' && ch != '\t' && 
+                 ch != ' ' && (i < (sizeof(variable) - 1));
+             ++i) {
+            variable[i] = ch;
+            if ((ch = getc( file )) == EOF)
+                pm_error( "invalid input file -- premature EOF" );
+        }
+        variable[i] = '\0';
+
+        if (streq(variable, "*/") && fieldCt > 0)
+            break;
+
+        if (fscanf( file, "%d", &value ) != 1)
+            continue;
+
+        if (streq( variable, "Width")) {
+            *widthP = value;
+            ++fieldCt;
+        } else if (streq( variable, "Height")) {
+            *heightP = value;
+            ++fieldCt;
+        } else if (streq( variable, "Depth")) {
+            if (value != 1 && value != 8)
+                pm_error("invalid depth");
+            *depthP = value;
+            ++fieldCt;
+        } else if (streq(variable, "Format_version")) {
+            if (value != 1)
+                pm_error("invalid Format_version");
+            ++fieldCt;
+        } else if (streq(variable, "Valid_bits_per_item")) {
+            if (value != 16 && value !=32)
+                pm_error("invalid Valid_bits_per_item");
+            *bitsPerItemP = value; 
+            ++fieldCt;
+        }
+    }
+
+    if (fieldCt < 5)
+        pm_error("invalid sun icon file header: "
+                 "only %u out of required 5 fields present", fieldCt);
+
+    if (*widthP <= 0)
+        pm_error("invalid width (must be positive): %d", *widthP);
+    if (*heightP <= 0)
+        pm_error("invalid height (must be positive): %d", *heightP);
+
+}
+
+
+int
+main(int argc, const char ** argv) {
+
+    FILE * ifP;
+    bit * bitrow;
+    gray * grayrow;
+    int rows, cols, depth, row, format, maxval, colChars, bitsPerItem;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments (%u).  Program takes at most one: "
+                 "name of input file", argc-1);
+
+    if (argc-1 == 1)
+        ifP = pm_openr(argv[1]);
+    else
+        ifP = stdin;
+
+    ReadIconFileHeader(ifP, &cols, &rows, &depth, &bitsPerItem);
+
+    if (depth == 1) {
+        format = PBM_TYPE;
+        maxval = 1;
+        pbm_writepbminit(stdout, cols, rows, 0);
+        bitrow = pbm_allocrow_packed(cols);
+        colChars = cols / 8;
+    } else {
+        assert(depth == 8);
+        format = PGM_TYPE;
+        maxval = 255;
+        pgm_writepgminit(stdout, cols, rows, maxval, 0);
+        grayrow = pgm_allocrow(cols);
+        colChars = cols;
+    }
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int colChar;
+        for (colChar = 0; colChar < colChars; ++colChar) {
+            unsigned int data;
+            int status;
+
+            /* read 8 bits */
+            if (row==0 && colChar == 0)
+                status = fscanf(ifP, " 0x%2x", &data);
+            else if (colChar % (bitsPerItem/8) == 0)
+                status = fscanf(ifP, ", 0x%2x", &data);
+            else
+                status = fscanf(ifP, "%2x", &data);
+
+            /* write 8 bits */
+            if (status == 1) {
+                if (format == PBM_TYPE)
+                    bitrow[colChar]  = data;
+                else
+                    grayrow[colChar] = data;
+            } else
+                pm_error("error scanning bits item %u" , colChar);
+        }
+
+        /* output row */
+        if (format == PBM_TYPE)
+            pbm_writepbmrow_packed(stdout, bitrow, cols, 0);
+        else
+            pgm_writepgmrow(stdout, grayrow, cols, maxval, 0);
+    }
+
+    pm_close(ifP);
+    pm_close(stdout);
+    return 0;
+}
diff --git a/converter/other/svgtopam.c b/converter/other/svgtopam.c
index 68deb3e0..58e7928f 100644
--- a/converter/other/svgtopam.c
+++ b/converter/other/svgtopam.c
@@ -26,7 +26,9 @@
    
 ============================================================================*/
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
+#define _POSIX_SOURCE   /* Make sure fileno() is in <stdio.h> */
 #include <assert.h>
 #include <string.h>
 #include <stdio.h>
@@ -66,7 +68,7 @@ parseCommandLine(int argc,
    was passed to us as the argv array.  We also trash *argv.
 --------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options. */
+        /* Instructions to pm_optParseOptions3 on how to parse our options. */
     optStruct3 opt;
 
     unsigned int option_def_index;
@@ -81,7 +83,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 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
     /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 < 1)
@@ -205,7 +207,7 @@ destroyPath(path * const pathP) {
     
     assert(pathP->pathTextLength == strlen(pathP->pathText));
 
-    strfree(pathP->pathText);
+    pm_strfree(pathP->pathText);
 
     free(pathP);
 }
@@ -531,7 +533,7 @@ interpretStyle(const char * const styleAttr) {
     p = &buffer[0];
 
     while (p) {
-        const char * const token = strsepN(&p, ";");
+        const char * const token = pm_strsep(&p, ";");
         const char * strippedToken;
         const char * p;
         char * buffer;
@@ -674,14 +676,14 @@ stringToUint(const char *   const string,
     /* TODO: move this to nstring.c */
 
     if (strlen(string) == 0)
-        asprintfN(errorP, "Value is a null string");
+        pm_asprintf(errorP, "Value is a null string");
     else {
         char * tailptr;
 
         *uintP = strtoul(string, &tailptr, 10);
 
         if (*tailptr != '\0')
-            asprintfN(errorP, "Non-numeric crap in string: '%s'", tailptr);
+            pm_asprintf(errorP, "Non-numeric crap in string: '%s'", tailptr);
         else
             *errorP = NULL;
     }
@@ -702,12 +704,12 @@ getSvgAttributes(xmlTextReaderPtr const xmlReaderP,
     stringToUint(width, colsP, &error);
     if (error) {
         pm_error("'width' attribute of <svg> has invalid value.  %s", error);
-        strfree(error);
+        pm_strfree(error);
     }
     stringToUint(height, rowsP, &error);
     if (error) {
         pm_error("'height' attribute of <svg> has invalid value.  %s", error);
-        strfree(error);
+        pm_strfree(error);
     }
 }
 
diff --git a/converter/other/tiff.c b/converter/other/tiff.c
index 90d50710..d0cbbd74 100644
--- a/converter/other/tiff.c
+++ b/converter/other/tiff.c
@@ -10,12 +10,6 @@
 
 #include <string.h>
 
-#ifdef VMS
-#ifdef SYSV
-#undef SYSV
-#endif
-#include <tiffioP.h>
-#endif
 #include <tiffio.h>
 
 #include "pm_c_util.h"
diff --git a/converter/other/tifftopnm.c b/converter/other/tifftopnm.c
index 4d40d117..4a8cf902 100644
--- a/converter/other/tifftopnm.c
+++ b/converter/other/tifftopnm.c
@@ -52,7 +52,6 @@
 #include <assert.h>
 #include <string.h>
 #include <stdio.h>
-#include <sys/wait.h>
 
 #include "pm_c_util.h"
 #include "shhopt.h"
@@ -60,12 +59,6 @@
 #include "nstring.h"
 #include "pnm.h"
 
-#ifdef VMS
-#ifdef SYSV
-#undef SYSV
-#endif
-#include <tiffioP.h>
-#endif
 /* See warning about tiffio.h in pamtotiff.c */
 #include <tiffio.h>
 
@@ -85,7 +78,7 @@
 #define PHOTOMETRIC_DEPTH 32768
 #endif
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -103,7 +96,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, const char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
    *cmdlineP structure are actually in the supplied argv array.  And
@@ -135,7 +128,7 @@ parseCommandLine(int argc, const char ** const argv,
     OPTENT3(0,   "alphaout",   
             OPT_STRING, &cmdlineP->alphaFilename, &alphaSpec,  0);
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
 
     if (argc - 1 == 0)
         cmdlineP->inputFilename = strdup("-");  /* he wants stdin */
@@ -158,6 +151,31 @@ parseCommandLine(int argc, const char ** const argv,
 
 
 
+static TIFF *
+newTiffImageObject(const char * const inputFileName) {
+/*----------------------------------------------------------------------------
+   Create a TIFF library object for accessing the TIFF input in file
+   named 'inputFileName'.  If 'inputFileName' is "-", that means
+   Standard Input.
+-----------------------------------------------------------------------------*/
+    const char * const tiffSourceName =
+        streq(inputFileName, "-") ? "Standard Input" : inputFileName;
+
+    TIFF * retval;
+
+    retval = TIFFFdOpen(fileno(pm_openr_seekable(inputFileName)),
+                        tiffSourceName,
+                        "r");
+
+    if (retval == NULL)
+        pm_error("Failed to access input file.  The OS opened the file fine, "
+                 "but the TIFF library's TIFFFdOpen rejected the open file.");
+
+    return retval;
+}
+
+
+
 static void
 getBps(TIFF *           const tif,
        unsigned short * const bpsP) {
@@ -226,6 +244,8 @@ tiffToImageDim(unsigned int   const tiffCols,
     }
 }
 
+
+
 static void
 getTiffDimensions(TIFF *         const tiffP,
                   unsigned int * const colsP,
@@ -345,15 +365,15 @@ readDirectory(TIFF *               const tiffP,
 
 
 static void
-readscanline(TIFF *         const tif, 
-             unsigned char  scanbuf[], 
-             int            const row, 
-             int            const plane,
-             unsigned int   const cols, 
-             unsigned short const bps,
-             unsigned short const spp,
-             unsigned short const fillorder,
-             unsigned int * const samplebuf) {
+readscanline(TIFF *          const tif, 
+             unsigned char * const scanbuf,
+             int             const row, 
+             int             const plane,
+             unsigned int    const cols, 
+             unsigned short  const bps,
+             unsigned short  const spp,
+             unsigned short  const fillorder,
+             unsigned int *  const samplebuf) {
 /*----------------------------------------------------------------------------
    Read one scanline out of the Tiff input and store it into samplebuf[].
    Unlike the scanline returned by the Tiff library function, samplebuf[]
@@ -381,8 +401,8 @@ readscanline(TIFF *         const tif,
                   "TIFFReadScanline() failed.",
                   row, plane);
     else if (bps == 8) {
-        int sample;
-        for (sample = 0; sample < cols*spp; sample++) 
+        unsigned int sample;
+        for (sample = 0; sample < cols * spp; ++sample) 
             samplebuf[sample] = scanbuf[sample];
     } else if (bps < 8) {
         /* Note that in this format, samples do not span bytes.  Rather,
@@ -390,12 +410,12 @@ readscanline(TIFF *         const tif,
            At least that's how I infer the format from reading pnmtotiff.c
            -Bryan 00.11.18
            */
-        int sample;
-        int bitsleft;
+        unsigned int sample;
+        unsigned int bitsleft;
         unsigned char * inP;
 
-        for (sample = 0, bitsleft=8, inP=scanbuf; 
-             sample < cols*spp; 
+        for (sample = 0, bitsleft = 8, inP = scanbuf; 
+             sample < cols * spp; 
              ++sample) {
             if (bitsleft == 0) {
                 ++inP; 
@@ -412,6 +432,7 @@ readscanline(TIFF *         const tif,
                 pm_error("Internal error: invalid value for fillorder: %u", 
                          fillorder);
             }
+            assert(bitsleft >= bps);
             bitsleft -= bps; 
             if (bitsleft < bps)
                 /* Don't count dregs at end of byte */
@@ -433,10 +454,10 @@ readscanline(TIFF *         const tif,
         for (sample = 0; sample < cols*spp; ++sample)
             samplebuf[sample] = scanbuf16[sample];
     } else if (bps == 32) {
-        uint32 * const scanbuf32 = (uint32 *) scanbuf;
+        const uint32 * const scanbuf32 = (const uint32 *) scanbuf;
         unsigned int sample;
         
-        for (sample = 0; sample < cols*spp; ++sample)
+        for (sample = 0; sample < cols * spp; ++sample)
             samplebuf[sample] = scanbuf32[sample];
     } else 
         pm_error("Internal error: invalid bits per sample passed to "
@@ -446,8 +467,11 @@ readscanline(TIFF *         const tif,
 
 
 static void
-pick_cmyk_pixel(const unsigned int samplebuf[], const int sample_cursor,
-                xelval * const r_p, xelval * const b_p, xelval * const g_p) {
+pick_cmyk_pixel(unsigned int const samplebuf[],
+                int          const sampleCursor,
+                xelval *     const redP,
+                xelval *     const bluP,
+                xelval *     const grnP) {
 
     /* Note that the TIFF spec does not say which of the 4 samples is
        which, but common sense says they're in order C,M,Y,K.  Before
@@ -455,10 +479,10 @@ pick_cmyk_pixel(const unsigned int samplebuf[], const int sample_cursor,
        But combined with a compensating error in the CMYK->RGB
        calculation, it had the same effect as C,M,Y,K.
     */
-    unsigned int const c = samplebuf[sample_cursor+0];
-    unsigned int const m = samplebuf[sample_cursor+1];
-    unsigned int const y = samplebuf[sample_cursor+2];
-    unsigned int const k = samplebuf[sample_cursor+3];
+    unsigned int const c = samplebuf[sampleCursor + 0];
+    unsigned int const m = samplebuf[sampleCursor + 1];
+    unsigned int const y = samplebuf[sampleCursor + 2];
+    unsigned int const k = samplebuf[sampleCursor + 3];
 
     /* The CMYK->RGB formula used by TIFFRGBAImageGet() in the TIFF 
        library is the following, (with some apparent confusion with
@@ -476,9 +500,9 @@ pick_cmyk_pixel(const unsigned int samplebuf[], const int sample_cursor,
        Yellow ink removes blue light from what the white paper reflects.  
     */
 
-    *r_p = 255 - MIN(255, c + k);
-    *g_p = 255 - MIN(255, m + k);
-    *b_p = 255 - MIN(255, y + k);
+    *redP = 255 - MIN(255, c + k);
+    *grnP = 255 - MIN(255, m + k);
+    *bluP = 255 - MIN(255, y + k);
 }
 
 
@@ -515,9 +539,9 @@ analyzeImageType(TIFF *             const tif,
                  unsigned short     const photomet,
                  xelval *           const maxvalP, 
                  int *              const formatP, 
-                 xel                      colormap[],
+                 xel *              const colormap,
                  bool               const headerdump,
-                 struct cmdlineInfo const cmdline) {
+                 struct CmdlineInfo const cmdline) {
 
     bool grayscale; 
 
@@ -726,44 +750,40 @@ spawnWithInputPipe(const char *  const shellCmd,
     int fd[2];
     int rc;
 
-    rc = pipe(fd);
+    rc = pm_pipe(fd);
 
     if (rc != 0)
-        asprintfN(errorP, "Failed to create pipe for process input.  "
-                  "Errno=%d (%s)", errno, strerror(errno));
+        pm_asprintf(errorP, "Failed to create pipe for process input.  "
+                    "Errno=%d (%s)", errno, strerror(errno));
     else {
-        int rc;
-
-        rc = fork();
+        int iAmParent;
+        pid_t childPid;
 
-        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);
+        pm_fork(&iAmParent, &childPid, errorP);
 
-            rc = system(shellCmd);
+        if (!*errorP) {
+            if (iAmParent) {
+                close(fd[PIPE_READ]);
 
-            exit(rc);
-        } else {
-            /* Parent */
-            pid_t const childPid = rc;
-
-            close(fd[PIPE_READ]);
+                *pidP   = childPid;
+                *pipePP = fdopen(fd[PIPE_WRITE], "w");
+                
+                if (*pipePP == NULL)
+                    pm_asprintf(errorP,"Unable to create stream from pipe.  "
+                                "fdopen() fails with errno=%d (%s)",
+                                errno, strerror(errno));
+                else
+                    *errorP = NULL;
+            } else {
+                int rc;
+                close(fd[PIPE_WRITE]);
+                close(STDIN_FILENO);
+                dup2(fd[PIPE_READ], STDIN_FILENO);
 
-            *pidP   = childPid;
-            *pipePP = fdopen(fd[PIPE_WRITE], "w");
+                rc = system(shellCmd);
 
-            if (*pipePP == NULL)
-                asprintfN(errorP,"Unable to create stream from pipe.  "
-                          "fdopen() fails with errno=%d (%s)",
-                          errno, strerror(errno));
-            else
-                *errorP = NULL;
+                exit(rc);
+            }
         }
     }
 }
@@ -780,7 +800,7 @@ createFlipProcess(FILE *         const outFileP,
    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
+   The process takes its 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
@@ -803,8 +823,8 @@ createFlipProcess(FILE *         const outFileP,
        file descriptor is equivalent to writing to the stream.
     */
 
-    asprintfN(&pamflipCmd, "pamflip -xform=%s >&%u",
-              xformNeeded(orientation), fileno(outFileP));
+    pm_asprintf(&pamflipCmd, "pamflip -xform=%s >&%u",
+                xformNeeded(orientation), fileno(outFileP));
 
     if (verbose)
         pm_message("Reorienting raster with shell command '%s'", pamflipCmd);
@@ -816,7 +836,7 @@ createFlipProcess(FILE *         const outFileP,
                  "raster, failed.  %s.  To work around this, you can use "
                  "the -orientraw option.", pamflipCmd, error);
 
-        strfree(error);
+        pm_strfree(error);
     }
 }
 
@@ -966,12 +986,12 @@ pnmOut_init(FILE *         const imageoutFileP,
    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.
+   Because we must set up the pnmOut object either to receive flipped or not
+   flipped input, we have *flipOkP and *noflipOkP outputs that tell Caller
+   whether he has to flip or not.  Note that Caller also influences which way
+   we set up pnmOut, with his 'flipIfNeeded' argument.  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;
@@ -1033,14 +1053,12 @@ pnmOut_term(pnmOut * const pnmOutP,
                        "waiting for Pamflip to terminate");
 
         if (pnmOutP->imagePipeP) {
-            int status;
             fclose(pnmOutP->imagePipeP);
-            waitpid(pnmOutP->imageFlipPid, &status, 0);
+            pm_waitpidSimple(pnmOutP->imageFlipPid);
         }
         if (pnmOutP->alphaPipeP) {
-            int status;
             fclose(pnmOutP->alphaPipeP);
-            waitpid(pnmOutP->alphaFlipPid, &status, 0);
+            pm_waitpidSimple(pnmOutP->alphaFlipPid);
         }
     } else {
         if (pnmOutP->imageoutFileP)
@@ -1083,8 +1101,8 @@ pnmOut_writeRow(pnmOut *     const pnmOutP,
 
 static void
 convertRow(unsigned int   const samplebuf[], 
-           xel                  xelrow[], 
-           gray                 alpharow[], 
+           xel *          const xelrow, 
+           gray *         const alpharow,
            int            const cols, 
            xelval         const maxval, 
            unsigned short const photomet, 
@@ -1160,9 +1178,9 @@ convertRow(unsigned int   const samplebuf[],
 
 
 static void
-scale32to16(unsigned int       samplebuf[],
-            unsigned int const cols,
-            unsigned int const spp) {
+scale32to16(unsigned int * const samplebuf,
+            unsigned int   const cols,
+            unsigned int   const spp) {
 /*----------------------------------------------------------------------------
   Convert every sample in samplebuf[] to something that can be expressed
   in 16 bits, assuming it takes 32 bits now.
@@ -1175,9 +1193,9 @@ scale32to16(unsigned int       samplebuf[],
 
 
 static void
-convertMultiPlaneRow(TIFF *         const tif,
-                     xel                   xelrow[],
-                     gray                  alpharow[],
+convertMultiPlaneRow(TIFF *          const tif,
+                     xel *           const xelrow,
+                     gray *          const alpharow,
                      int             const cols,
                      xelval          const maxval,
                      int             const row,
@@ -1193,15 +1211,15 @@ convertMultiPlaneRow(TIFF *         const tif,
        for the blues.
     */
 
-    int col;
-
     if (photomet != PHOTOMETRIC_RGB)
         pm_error("This is a multiple-plane file, but is not an RGB "
                  "file.  This program does not know how to handle that.");
     else {
+        unsigned int col;
+
         /* First, clear the buffer so we can add red, green,
            and blue one at a time.  
-                */
+        */
         for (col = 0; col < cols; ++col) 
             PPM_ASSIGN(xelrow[col], 0, 0, 0);
 
@@ -1254,8 +1272,8 @@ convertRasterByRows(pnmOut *       const pnmOutP,
                     bool           const verbose) {
 /*----------------------------------------------------------------------------
    With the TIFF header all processed (and relevant information from it in 
-   our arguments), write out the TIFF raster to the file images *imageoutFile
-   and *alphaFile.
+   our arguments), write out the TIFF raster to the Netpbm output files
+   as described by *pnmOutP.
 
    Do this one row at a time, employing the TIFF library's
    TIFFReadScanline.
@@ -1275,12 +1293,12 @@ convertRasterByRows(pnmOut *       const pnmOutP,
            row we are presently converting.
         */
 
-    int row;
+    unsigned int row;
 
     if (verbose)
         pm_message("Converting row by row ...");
 
-    scanbuf = (unsigned char *) malloc(TIFFScanlineSize(tif));
+    MALLOCARRAY(scanbuf, TIFFScanlineSize(tif));
     if (scanbuf == NULL)
         pm_error("can't allocate memory for scanline buffer");
 
@@ -1355,7 +1373,7 @@ warnBrokenTiffLibrary(TIFF * const tiffP) {
         case ORIENTATION_RIGHTBOT:
         case ORIENTATION_LEFTBOT:
             pm_message("WARNING: This TIFF image has an orientation that "
-                       "most TIFF libraries converts incorrectly.  "
+                       "most TIFF libraries convert incorrectly.  "
                        "Use -byrow to circumvent.");
             break;
         }
@@ -1414,20 +1432,23 @@ convertTiffRaster(uint32 *        const raster,
 
 
 
-enum convertDisp {CONV_DONE, CONV_OOM, CONV_UNABLE, CONV_FAILED, 
+enum convertDisp {CONV_DONE,
+                  CONV_OOM,
+                  CONV_UNABLE,
+                  CONV_FAILED, 
                   CONV_NOTATTEMPTED};
 
 static void
-convertRasterInMemory(pnmOut *       const pnmOutP,
-                      xelval         const maxval,
-                      TIFF *         const tif,
-                      unsigned short const photomet, 
-                      unsigned short const planarconfig,
-                      unsigned short const bps,
-                      unsigned short const spp,
-                      unsigned short const fillorder,
-                      xel            const colormap[],
-                      bool           const verbose,
+convertRasterInMemory(pnmOut *           const pnmOutP,
+                      xelval             const maxval,
+                      TIFF *             const tif,
+                      unsigned short     const photomet, 
+                      unsigned short     const planarconfig,
+                      unsigned short     const bps,
+                      unsigned short     const spp,
+                      unsigned short     const fillorder,
+                      xel                const colormap[],
+                      bool               const verbose,
                       enum convertDisp * const statusP) {
 /*----------------------------------------------------------------------------
    With the TIFF header all processed (and relevant information from
@@ -1462,7 +1483,7 @@ convertRasterInMemory(pnmOut *       const pnmOutP,
         int ok;
         ok = TIFFRGBAImageOK(tif, emsg);
         if (!ok) {
-            pm_message(emsg);
+            pm_message("%s", emsg);
             *statusP = CONV_UNABLE;
         } else {
             uint32 * raster;
@@ -1489,14 +1510,14 @@ convertRasterInMemory(pnmOut *       const pnmOutP,
                 
                 ok = TIFFRGBAImageBegin(&img, tif, stopOnErrorFalse, emsg);
                 if (!ok) {
-                    pm_message(emsg);
+                    pm_message("%s", emsg);
                     *statusP = CONV_FAILED;
                 } else {
                     int ok;
                     ok = TIFFRGBAImageGet(&img, raster, cols, rows);
                     TIFFRGBAImageEnd(&img) ;
                     if (!ok) {
-                        pm_message(emsg);
+                        pm_message("%s", emsg);
                         *statusP = CONV_FAILED;
                     } else {
                         *statusP = CONV_DONE;
@@ -1524,6 +1545,7 @@ convertRaster(pnmOut *           const pnmOutP,
               bool               const verbose) {
 
     enum convertDisp status;
+
     if (byrow || !flipOk)
         status = CONV_NOTATTEMPTED;
     else {
@@ -1563,7 +1585,7 @@ static void
 convertImage(TIFF *             const tifP,
              FILE *             const alphaFileP,
              FILE *             const imageoutFileP,
-             struct cmdlineInfo const cmdline) {
+             struct CmdlineInfo const cmdline) {
 
     struct tiffDirInfo tiffDir;
     int format;
@@ -1600,7 +1622,7 @@ static void
 convertIt(TIFF *             const tifP,
           FILE *             const alphaFile, 
           FILE *             const imageoutFile,
-          struct cmdlineInfo const cmdline) {
+          struct CmdlineInfo const cmdline) {
 
     unsigned int imageSeq;
     bool eof;
@@ -1625,8 +1647,8 @@ convertIt(TIFF *             const tifP,
 int
 main(int argc, const char * argv[]) {
 
-    struct cmdlineInfo cmdline;
-    TIFF * tif;
+    struct CmdlineInfo cmdline;
+    TIFF * tiffP;
     FILE * alphaFile;
     FILE * imageoutFile;
 
@@ -1634,15 +1656,7 @@ main(int argc, const char * argv[]) {
 
     parseCommandLine(argc, argv, &cmdline);
 
-    if (!streq(cmdline.inputFilename, "-")) {
-        tif = TIFFOpen(cmdline.inputFilename, "r");
-        if (tif == NULL)
-            pm_error("error opening TIFF file %s", cmdline.inputFilename);
-    } else {
-        tif = TIFFFdOpen(0, "Standard Input", "r");
-        if (tif == NULL)
-            pm_error("error opening standard input as TIFF file");
-    }
+    tiffP = newTiffImageObject(cmdline.inputFilename);
 
     if (cmdline.alphaStdout)
         alphaFile = stdout;
@@ -1656,16 +1670,19 @@ main(int argc, const char * argv[]) {
     else
         imageoutFile = stdout;
 
-    convertIt(tif, alphaFile, imageoutFile, cmdline);
+    convertIt(tiffP, alphaFile, imageoutFile, cmdline);
 
     if (imageoutFile != NULL) 
         pm_close( imageoutFile );
     if (alphaFile != NULL)
         pm_close( alphaFile );
 
-    strfree(cmdline.inputFilename);
+    TIFFClose(tiffP);
+
+    pm_strfree(cmdline.inputFilename);
 
     /* If the program failed, it previously aborted with nonzero completion
-       code, via various function calls.  */
+       code, via various function calls.
+    */
     return 0;
 }
diff --git a/converter/other/winicon.h b/converter/other/winicon.h
new file mode 100644
index 00000000..9ede01f5
--- /dev/null
+++ b/converter/other/winicon.h
@@ -0,0 +1,82 @@
+#include "pm_c_util.h"
+
+#define ICONDIR_TYPE_ICO (1)
+
+/*  windows icon structures  */
+struct IconDirEntry {
+    uint16_t width;               /* image width in pixels 0 == 256 */
+    uint16_t height;              /* image height in pixels 0 == 256 */
+    uint8_t  color_count;         /* 0 if bits_per_pixel >= 8 */
+    uint8_t  zero;                /* 0 */
+    uint16_t color_planes;        /* 1 */
+    uint16_t bits_per_pixel;      /* allowed values: 1, 4, 8, 16 or 32 (1) */
+    uint32_t size;                /* size of image */
+    uint32_t offset;              /* file offset of image */
+
+    uint16_t index;               /* extra field (not in file) */
+};
+
+/*  (1) This is from
+ *  http://blogs.msdn.com/b/oldnewthing/archive/2010/10/19/10077610.aspx.
+ *
+ *  However, the bpp value in the icon directory is used as a hint for
+ *  image selection only.  It seems to be legal to set this value to
+ *  zero, and e.g. in SHELL32.DLL of Win98SE, there are many 8bpp
+ *  images described as 24 bit images in the icon directory.
+ *
+ *  The bpp value of image 1 in icon 150 in SHELL32.DLL of WinXP is 24
+ *  (in header and BMP).  This may be a bug, as the 32 x 32 x 8 image
+ *  is missing, but it shows the Windows icon rendering engine is able
+ *  to cope with 24 bit images).
+ *
+ *  16bpp icons are at least rare in the wild.
+ */
+struct IconDir {
+    uint16_t zero;                /* 0 */
+    uint16_t type;                /* 1 */
+    uint16_t count;               /* number of images in icon */
+
+    unsigned int entriesAllocCt;     /* # of allocated slots in 'entries'*/
+    struct IconDirEntry * entries;   /* one entry for each image */
+};
+
+/*  BMP image structures  */
+
+struct BitmapInfoHeader {
+    uint32_t header_size;         /* >= 40 */
+    int32_t  bm_width;
+    int32_t  bm_height;
+    uint16_t color_planes;
+    uint16_t bits_per_pixel;
+    uint32_t compression_method;
+    uint32_t image_size;
+    int32_t  horizontal_resolution; /* pixels per meter (!) */
+    int32_t  vertical_resolution;   /* pixels per meter (!) */
+    uint32_t colors_in_palette;
+    uint32_t important_colors;
+
+    bool top_down;                /* extra field (not in file) */
+
+};
+
+typedef enum {
+    BI_RGB       = 0,
+    BI_BITFIELDS = 3
+
+} BiCompression;
+
+/*  PNG image structures  */
+#define PNG_HEADER { 0x89, 'P', 'N', 'G', '\r', '\n', 0x1A /* ^Z */, '\n' }
+
+struct PngIhdr {
+    uint32_t length;              /* 13 */
+    uint32_t signature;           /* "IHDR" */
+    uint32_t width;               /* image width in pixels */
+    uint32_t height;              /* image height in pixels */
+    uint8_t  bit_depth;           /* depth per channel */
+    uint8_t  color_type;          /* recognized values: 0, 2, 3, 4 and 6 */
+    uint8_t  compression;
+    uint8_t  filter;
+    uint8_t  interlace;
+    uint32_t crc;
+};
diff --git a/converter/other/winicontopam.c b/converter/other/winicontopam.c
new file mode 100644
index 00000000..664b4ef9
--- /dev/null
+++ b/converter/other/winicontopam.c
@@ -0,0 +1,1286 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "netpbm/pm_config.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+#include "netpbm/shhopt.h"
+#include "netpbm/pam.h"
+#include "netpbm/pm_system.h"
+
+#include "winicon.h"
+
+#define RED   0
+#define GRN   1
+#define BLU   2
+#define ALPHA 3
+#define CHANNEL_CHARS "RGBA"
+
+
+
+static bool verbose;
+
+
+
+struct CmdlineInfo {
+    
+    const char * inputFileName;
+    unsigned int allimages;
+    unsigned int imageSpec;
+    unsigned int image;
+    unsigned int andmasks;
+    unsigned int headerdump;
+    unsigned int verbose;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char **argv,
+                 struct CmdlineInfo * const cmdlineP) {
+
+    optEntry *   option_def;
+    unsigned int option_def_index;
+    optStruct3   opt3;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;
+
+    OPTENT3(0, "allimages",   OPT_FLAG,   NULL,
+            &cmdlineP->allimages,         0);
+    OPTENT3(0, "image",     OPT_UINT,   &cmdlineP->image,
+            &cmdlineP->imageSpec,         0);
+    OPTENT3(0, "andmasks",  OPT_FLAG,   NULL,
+            &cmdlineP->andmasks,          0);
+    OPTENT3(0, "headerdump",   OPT_FLAG,   NULL,
+            &cmdlineP->headerdump,        0);
+    OPTENT3(0, "verbose",   OPT_FLAG,   NULL,
+            &cmdlineP->verbose,           0);
+
+    opt3.opt_table     = option_def;
+    opt3.short_allowed = false;
+    opt3.allowNegNum   = false;
+
+    pm_optParseOptions3(&argc, (char **)argv, opt3, sizeof(opt3), 0);
+
+    if (cmdlineP->allimages && cmdlineP->imageSpec)
+        pm_error("You cannot specify both -allimages and -image");
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("Too many arguments.  The only possible "
+                     "non-option argument is the input file name");
+    }
+        
+    free(option_def);
+}
+
+
+
+static unsigned char const pngHeader[] = PNG_HEADER;
+
+
+
+struct File {
+
+    FILE *       fileP;
+    const char * name;
+    pm_filepos   pos;
+    
+};
+
+
+
+static uint32_t
+u8_le(const unsigned char * const buf,
+      size_t                const offset) {
+
+    return buf[offset + 0];
+}
+
+
+
+static uint32_t
+u16_le(const unsigned char * const buf,
+       size_t                const offset) {
+
+    return
+        ((uint32_t)buf[offset + 0] << 0) +
+        ((uint32_t)buf[offset + 1] << 8);
+}
+
+
+
+static uint32_t
+u32_le(const unsigned char * const buf,
+       size_t                const offset) {
+
+    return 
+        ((uint32_t)buf[offset + 0] <<  0) +
+        ((uint32_t)buf[offset + 1] <<  8) +
+        ((uint32_t)buf[offset + 2] << 16) +
+        ((uint32_t)buf[offset + 3] << 24);
+}
+
+
+
+static uint32_t
+s32_le(const unsigned char * const buf,
+       size_t                const offset) {
+
+    return 
+        ((uint32_t)buf[offset + 0] <<  0) +
+        ((uint32_t)buf[offset + 1] <<  8) +
+        ((uint32_t)buf[offset + 2] << 16) +
+        ((uint32_t)buf[offset + 3] << 24);
+}
+
+
+
+static uint32_t
+u8_be(const unsigned char * const buf,
+      size_t                const offset) {
+
+    return buf[offset + 0];
+}
+
+
+
+static uint32_t
+u32_be(const unsigned char * const buf,
+       size_t                const offset) {
+    
+    return 
+        ((uint32_t)buf[offset + 0] << 24) +
+        ((uint32_t)buf[offset + 1] << 16) +
+        ((uint32_t)buf[offset + 2] <<  8) +
+        ((uint32_t)buf[offset + 3] <<  0);
+}
+
+
+
+static uint32_t
+u32_xx(const unsigned char * const buf,
+       size_t                const offset) {
+
+    uint32_t u32;
+
+    ((uint8_t*) &u32)[0] = buf[offset + 0];
+    ((uint8_t*) &u32)[1] = buf[offset + 1];
+    ((uint8_t*) &u32)[2] = buf[offset + 2];
+    ((uint8_t*) &u32)[3] = buf[offset + 3];
+
+    return (u32);
+}
+
+
+
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn cmpfn;
+#endif
+
+static int
+cmpfn(const void * const aP,
+      const void * const bP) {
+
+    const struct IconDirEntry * const dirEntryAP = aP;
+    const struct IconDirEntry * const dirEntryBP = bP;
+
+    if (dirEntryAP->offset < dirEntryBP->offset)
+        return -1;
+    else if (dirEntryAP->offset > dirEntryBP->offset)
+        return +1;
+    else
+        return 0;
+}
+
+
+
+static void
+dumpIconDir(const struct IconDir * const dirP) {
+
+    unsigned int i;
+
+    pm_message("Type: %u", dirP->type);
+    pm_message("Icon directory has %u images:", dirP->count);
+
+    for (i = 0; i < dirP->count; ++i) {
+        const struct IconDirEntry * const dirEntryP = &dirP->entries[i];
+
+        pm_message("width: %u", dirEntryP->width);
+        pm_message("height: %u", dirEntryP->height);
+        pm_message("color count: %u", dirEntryP->color_count);
+        pm_message("# color planes: %u", dirEntryP->color_planes);
+        pm_message("bits per pixel: %u", dirEntryP->bits_per_pixel);
+        pm_message("offset in file of image: %u", dirEntryP->offset);
+        pm_message("size of image: %u", dirEntryP->size);
+        pm_message("zero field: %u", dirEntryP->zero);
+    }
+}
+
+            
+
+static struct IconDir *
+readIconDir(struct File * const fP,
+            bool          const needHeaderDump) {
+
+    struct IconDir head;
+    struct IconDir * dirP;
+    uint32_t  imageIndex; /* more bits than dir.count */
+
+    pm_readlittleshortu(fP->fileP, &head.zero);
+    pm_readlittleshortu(fP->fileP, &head.type);
+    pm_readlittleshortu(fP->fileP, &head.count);
+    fP->pos += 6;
+
+    if (head.zero != 0 || head.type != ICONDIR_TYPE_ICO)
+        pm_error("Not a valid windows icon file");
+
+    MALLOCVAR(dirP);
+
+    if (dirP == NULL)
+        pm_error("Could't allocate memory for Icon directory");
+
+    MALLOCARRAY(dirP->entries, head.count);
+
+    if (dirP->entries == NULL)
+        pm_error("Could not allocate memory for %u entries in icon directory",
+                 head.count);
+
+    dirP->zero           = head.zero;
+    dirP->type           = head.type;
+    dirP->count          = head.count;
+    dirP->entriesAllocCt = head.count;
+
+    for (imageIndex = 0; imageIndex < head.count; ++imageIndex) {
+        struct IconDirEntry * const dirEntryP = &dirP->entries[imageIndex];
+
+        unsigned char widthField, heightField;
+
+        unsigned long ul;
+
+        pm_readcharu(fP->fileP, &widthField);
+        dirEntryP->width  = (widthField == 0 ? 256 : widthField);
+
+        pm_readcharu(fP->fileP, &heightField);
+        dirEntryP->height = (heightField == 0 ? 256 : heightField);
+        
+        pm_readcharu(fP->fileP, &dirEntryP->color_count);
+
+        pm_readcharu(fP->fileP, &dirEntryP->zero);
+
+        pm_readlittleshortu(fP->fileP, &dirEntryP->color_planes);
+
+        pm_readlittleshortu(fP->fileP, &dirEntryP->bits_per_pixel);
+
+        pm_readlittlelongu(fP->fileP, &ul); dirEntryP->size = ul;
+
+        pm_readlittlelongu(fP->fileP, &ul); dirEntryP->offset = ul;
+
+        fP->pos += 16;
+
+        dirEntryP->index = imageIndex;
+    }
+
+    /* The following is paranoia code only:
+     
+       I've never seen a windows icon file in the wild with having the entries
+       in the directory stored in a different order than the images
+       themselves.  However, the file format allows for it ...
+     */
+    qsort(dirP->entries, dirP->count, sizeof(struct IconDirEntry), cmpfn);
+
+    if (verbose) {
+        pm_message("%s icon directory (%u image%s):",
+                   fP->name,
+                   dirP->count, dirP->count == 1 ? "" : "s");
+        
+        for (imageIndex = 0; imageIndex < dirP->count; ++imageIndex) {
+            const struct IconDirEntry * const dirEntryP =
+                &dirP->entries[imageIndex];
+
+            uint32_t colorCt;
+
+            if (dirEntryP->bits_per_pixel == 0)
+                colorCt = 0;
+            else if (dirEntryP->bits_per_pixel >= 32)
+                colorCt = 1u << 24;
+            else
+                colorCt = 1u << dirEntryP->bits_per_pixel;
+
+            if (dirEntryP->color_count != 0 &&
+                colorCt > dirEntryP->color_count) {
+                colorCt = dirEntryP->color_count;
+            }
+            pm_message ("%5u: %3u x %3u, %8u colors, %5u bytes",
+                        dirEntryP->index,
+                        dirEntryP->width,
+                        dirEntryP->height,
+                        colorCt,
+                        dirEntryP->size);
+        }
+    }
+
+    if (needHeaderDump)
+        dumpIconDir(dirP);
+
+    return dirP;
+}
+
+
+
+static void
+freeIconDir(struct IconDir * const dirP) {
+
+    free(dirP->entries);
+    free(dirP);
+}
+
+
+
+static const unsigned char *
+readImage(struct File *         const fP,
+          struct IconDirEntry * const dirEntryP) {
+
+    size_t rc;
+    unsigned char * image;
+    uint32_t skippedCt;
+
+    /*  Don't try to read an image that is smaller than the
+        BITMAPINFOHEADER of BMP images (40 bytes).
+     
+        PNG compressed images can't be smaller than that either, as the
+        PNG header plus the mandantory IHDR and IEND chunks already take
+        8 + 25 + 12 = 35 bytes, and there is to be a IDAT chunk too.
+     */
+    if (dirEntryP->size < 40) {
+        pm_error("image %2u: format violation: too small as an image.",
+                  dirEntryP->index);
+    }
+    if ((pm_filepos) dirEntryP->offset < fP->pos)
+        pm_error("image %2u: format violation: invalid offset.",
+                 dirEntryP->index);
+
+    /* The following is paranoia code only:
+     
+       I've never seen a windows icon file in the wild with gaps between
+       the images, but the file format allows for it, and Microsoft
+       expects the user to fseek() to the start of each image.
+     */
+    skippedCt = 0;
+
+    while ((pm_filepos) dirEntryP->offset > fP->pos) {
+        if (getc(fP->fileP) == EOF) {
+            pm_error("seeking to image %u: unexpected EOF", dirEntryP->index);
+        }
+        ++fP->pos;
+        ++skippedCt;
+    }
+
+    /*  The additional four bytes are for purify and friends, as the
+        routines reading BMP XOR and AND masks might read (but not
+        evaluate) some bytes beyond the image data.
+     */
+    image = malloc(dirEntryP->size + sizeof(uint32_t));
+    if (image == NULL)
+        pm_error("out of memory.");
+
+    rc = fread (image, 1, dirEntryP->size, fP->fileP);
+    if (rc != dirEntryP->size) {
+        pm_error("reading image %2u: unexpected EOF", dirEntryP->index);
+    }
+    fP->pos += dirEntryP->size;
+
+    return image;
+}
+
+
+
+static uint8_t
+getIdx1(const unsigned char * const bitmap,
+        uint32_t              const offset,
+        int16_t               const col) {
+
+    return u8_le(bitmap, offset + (col >> 3)) >> (7 - (col & 0x07)) & 0x1;
+}
+
+
+
+static uint8_t
+getIdx4(const unsigned char * const bitmap,
+        uint32_t              const offset,
+        int16_t               const col) {
+
+    if ((col & 1) == 0x0000)
+        return u8_le(bitmap, offset + (col >> 1)) >> 4 & 0x0F;
+    else
+        return u8_le(bitmap, offset + (col >> 1)) >> 0 & 0x0F;
+}
+
+
+
+static uint8_t
+getIdx8(const unsigned char * const bitmap,
+        uint32_t              const offset,
+        int16_t               const col) {
+
+    return u8_le(bitmap, offset + col);
+}
+
+
+
+typedef unsigned char PaletteEntry[4];
+
+
+
+static void
+dumpPalette(const PaletteEntry * const palette,
+            unsigned int         const colorCt) {
+
+    unsigned int i;
+
+    for (i = 0; i < colorCt; ++i) {
+        pm_message("Color %u: (%u, %u, %u)",
+                   i, palette[i][2], palette[i][1], palette[i][0]);
+    }
+}
+
+
+
+static void
+readXorPalette(struct BitmapInfoHeader * const hdrP,
+               const unsigned char *     const bitmap,
+               uint32_t                  const maxSize,
+               tuple **                  const tuples,
+               uint16_t                  const index,
+               bool                      const needHeaderDump,
+               uint32_t *                const bytesConsumedP) {
+
+    uint32_t paletteSize;
+
+    int16_t     row;
+    const PaletteEntry * palette;
+    uint32_t    truncatedXorSize;
+    uint32_t    bytesConsumed;
+    uint32_t    bytesPerRow;
+    const unsigned char * bitmapCursor;
+    uint32_t sizeRemaining;
+  
+    uint8_t (*getIdx) (const unsigned char * bitmap,
+                       uint32_t rowOffset,
+                       int16_t col);
+  
+    if (hdrP->compression_method != BI_RGB)
+        pm_error("image %2u: invalid compression method %u.",
+                 index, hdrP->compression_method);
+
+    assert(hdrP->bits_per_pixel < 16);
+
+    switch (hdrP->bits_per_pixel) {
+    case 1:
+        if (hdrP->colors_in_palette == 0)
+            hdrP->colors_in_palette = 2;
+        getIdx = getIdx1;
+        break;
+
+    case 4:
+        if (hdrP->colors_in_palette == 0)
+            hdrP->colors_in_palette = 16;
+        getIdx = getIdx4;
+        break;
+
+    case 8:
+        if (hdrP->colors_in_palette == 0)
+            hdrP->colors_in_palette = 256;
+        getIdx = getIdx8;
+        break;
+
+    default:
+        pm_error("image %2u: "
+                 "bits per pixel is a value we don't understand: %u",
+                  index, hdrP->bits_per_pixel);
+        getIdx = NULL;
+    }
+
+    bitmapCursor = &bitmap[0];  /* initial value */
+    sizeRemaining = maxSize;    /* initial value */
+    bytesConsumed = 0;          /* initial value */
+
+    paletteSize = hdrP->colors_in_palette * 4;
+
+    if (sizeRemaining < paletteSize)
+        pm_error("image %2u: "
+                 "reading palette: image truncated.", index);
+    
+    palette = (const PaletteEntry *) bitmapCursor;
+
+    if (needHeaderDump)
+        dumpPalette(palette, hdrP->colors_in_palette);
+
+    bitmapCursor  += paletteSize;
+    sizeRemaining -= paletteSize;
+    bytesConsumed += paletteSize;
+
+    {
+        uint32_t const xorSize = (uint32_t)
+            (((hdrP->bits_per_pixel * hdrP->bm_width + 31) / 32)
+             * 4 * hdrP->bm_height / 2);
+
+        if (sizeRemaining < xorSize) {
+            pm_message("image %2u: "
+                       "reading XOR mask: image truncated.", index);
+            truncatedXorSize = sizeRemaining;
+        } else
+            truncatedXorSize = xorSize;
+    }
+
+    bytesPerRow = ((hdrP->bits_per_pixel * hdrP->bm_width + 31) / 32) * 4;
+
+    for (row = 0; hdrP->bm_height / 2 > row; ++row) {
+        uint32_t rowOffset;
+
+        if (hdrP->top_down)
+            rowOffset = row * bytesPerRow;
+        else
+            rowOffset = (hdrP->bm_height / 2 - row - 1) * bytesPerRow;
+
+        if (rowOffset + bytesPerRow <= truncatedXorSize) {
+            int16_t col;
+            for (col = 0; hdrP->bm_width > col; ++col) {
+                uint8_t const idx = getIdx(bitmapCursor, rowOffset, col);
+
+                if (idx > hdrP->colors_in_palette)
+                    pm_error("invalid palette index in row %u, column %u.",
+                             row, col);
+
+                /*  The palette is an array of little-endian 32-bit values,
+                    where the RGB value is encoded as follows:
+                 
+                    red:   bits 2^16..2^23
+                    green: bits 2^8 ..2^15
+                    blue:  bits 2^0 ..2^7
+                 */
+                tuples[row][col][PAM_RED_PLANE] = palette[idx][2];
+                tuples[row][col][PAM_GRN_PLANE] = palette[idx][1];
+                tuples[row][col][PAM_BLU_PLANE] = palette[idx][0];
+            }
+        }
+    }
+
+    bitmapCursor  += truncatedXorSize;
+    sizeRemaining -= truncatedXorSize;
+    bytesConsumed += truncatedXorSize;
+
+    *bytesConsumedP = bytesConsumed;
+}
+
+
+
+static void
+readXorBitfields(struct BitmapInfoHeader * const hdrP,
+                 const unsigned char *     const bitmap,
+                 uint32_t                  const maxSize,
+                 tuple **                  const tuples,
+                 uint16_t                  const index,
+                 bool *                    const haveAlphaP,
+                 uint32_t *                const bytesConsumedP) {
+
+    uint32_t   bitfields[4];
+    uint8_t    shift    [4];
+    sample     maxval   [4];
+
+    int16_t      row;
+    uint32_t     bytesConsumed;
+    uint32_t     bytesPerSample;
+    uint32_t     bytesPerRow;
+    const unsigned char * bitmapCursor;
+    uint32_t     sizeRemaining;
+    uint32_t     truncatedXorSize;
+
+    static uint8_t alphas [256];
+    bool         allOpaque;
+    bool         allTransparent;
+
+    bytesConsumed = 0;
+
+    if (hdrP->compression_method != BI_RGB
+        && hdrP->compression_method != BI_BITFIELDS)
+        pm_error("image %2u: invalid compression method %u.",
+                 index, hdrP->compression_method);
+
+    assert(hdrP->bits_per_pixel >= 16);
+
+    switch (hdrP->bits_per_pixel) {
+    case 16:
+        bytesPerSample = 2;
+        bitfields[RED]   = 0x7C00;
+        bitfields[GRN]   = 0x03E0;
+        bitfields[BLU]   = 0x001F;
+        bitfields[ALPHA] = 0x0000;
+        break;
+
+    case 24:
+        bytesPerSample = 3;
+        bitfields[RED]   = 0xFF0000;
+        bitfields[GRN]   = 0x00FF00;
+        bitfields[BLU]   = 0x0000FF;
+        bitfields[ALPHA] = 0x000000;
+        break;
+
+    case 32:
+        bytesPerSample = 4;
+        bitfields[RED]   = 0x00FF0000;
+        bitfields[GRN]   = 0x0000FF00;
+        bitfields[BLU]   = 0x000000FF;
+        bitfields[ALPHA] = 0xFF000000;
+        break;
+
+    default:
+        pm_error("image %2u: bits per pixel is one we don't understand: %u.",
+                 index, hdrP->bits_per_pixel);
+        bytesPerSample = 0;
+    }
+
+    bitmapCursor = &bitmap[0]; /* initial value */
+    sizeRemaining = maxSize;  /* initial value */
+
+    /*  read bit fields from image data  */
+    if (hdrP->compression_method == BI_BITFIELDS) {
+        if (sizeRemaining < 12)
+            pm_error("image %2u: "
+                     "reading bit fields: image truncated.", index);
+
+        bitfields[RED]   = u32_le(bitmapCursor, 0);
+        bitfields[GRN]   = u32_le(bitmapCursor, 4);
+        bitfields[BLU]   = u32_le(bitmapCursor, 8);
+        bitfields[ALPHA] = 0;
+
+        bitmapCursor  += 12;
+        sizeRemaining -= 12;
+        bytesConsumed += 12;
+    }
+
+    /*  determine shift and maxval from bit field for each channel */
+    {
+        unsigned int i;
+
+        for (i = 0; 4 > i; ++i) {
+            if (bitfields[i] == 0) {
+                maxval[i] = 1;
+                shift [i] = 0;
+            } else {
+                unsigned int j;
+
+                maxval[i] = bitfields[i];
+
+                for (j = 0; 32 > j; ++j) {
+                    if ((maxval[i] & 0x1) != 0)
+                        break;
+                    maxval[i] >>= 1;
+                }
+                shift[i] = j;
+            }
+
+        }
+    }
+
+    /*  read the XOR mask */
+    {
+        uint32_t const xorSize = (uint32_t)
+            (((hdrP->bits_per_pixel * hdrP->bm_width + 31) / 32)
+             * 4 * hdrP->bm_height / 2);
+
+        if (sizeRemaining < xorSize) {
+            pm_message("image %2u: "
+                       "reading XOR mask: image truncated.", index);
+            truncatedXorSize = sizeRemaining;
+        } else
+            truncatedXorSize = xorSize;
+    }
+
+    bytesPerRow = ((hdrP->bits_per_pixel * hdrP->bm_width + 31) / 32) * 4;
+    MEMSZERO(alphas);
+
+    for (row = 0, allOpaque = true, allTransparent = true;
+         hdrP->bm_height / 2 > row;
+         ++row) {
+
+        uint32_t offset;
+
+        if (hdrP->top_down)
+            offset = row * bytesPerRow;
+        else
+            offset = (hdrP->bm_height / 2 - row - 1) * bytesPerRow;
+
+        if (offset + bytesPerRow <= truncatedXorSize) {
+            unsigned int col;
+            for (col = 0; col < hdrP->bm_width; ++col) {
+                uint32_t const pixel = u32_le(bitmapCursor, offset);
+                offset += bytesPerSample;
+
+                tuples[row][col][PAM_RED_PLANE] =
+                    pnm_scalesample((pixel & bitfields[RED]) >> shift[RED],
+                                    maxval[RED], 255);
+                tuples[row][col][PAM_GRN_PLANE] =
+                    pnm_scalesample((pixel & bitfields[GRN]) >> shift[GRN],
+                                    maxval[GRN], 255);
+                tuples [row][col][PAM_BLU_PLANE]
+                    = pnm_scalesample((pixel & bitfields[BLU]) >> shift[BLU],
+                                      maxval[BLU], 255);
+
+                if (bitfields [ALPHA] != 0) {
+                    tuples[row][col][PAM_TRN_PLANE]
+                        = pnm_scalesample(
+                            (pixel & bitfields[ALPHA]) >> shift[ALPHA],
+                            maxval[ALPHA], 255);
+
+                    if (tuples[row][col][PAM_TRN_PLANE] != 0)
+                        allTransparent = false;
+
+                    if (tuples [row][col][PAM_TRN_PLANE] != 255)
+                        allOpaque = false;
+
+                    alphas[tuples[row][col][PAM_TRN_PLANE]] = !0;
+                }
+            }
+        }
+    }
+
+    bitmapCursor  += truncatedXorSize;
+    sizeRemaining -= truncatedXorSize;
+    bytesConsumed += truncatedXorSize;
+
+    /*  A fully transparent alpha channel (all zero) in XOR mask is
+        defined to be void by Microsoft, and a fully opaque alpha
+        channel (all maxval) is trivial and will be dropped.
+    */
+    *haveAlphaP = !allTransparent && !allOpaque;
+
+    if (!allTransparent && verbose) {
+        unsigned int i;
+        unsigned int c;
+
+        for (i = 0, c = 0; 256 > i; ++i) {
+            if (alphas[i] != 0)
+                ++c;
+        }
+        pm_message("image %2u: %u distinct transparency value%s",
+                   index, c, (c == 1) ? "": "s");
+    }
+    *bytesConsumedP = bytesConsumed;
+}
+
+
+
+static void
+readAnd(struct BitmapInfoHeader * const hdrP,
+        const unsigned char *     const bitmap,
+        uint32_t                  const maxSize,
+        tuple **                  const tuples,
+        uint16_t                  const index,
+        unsigned int              const plane,
+        sample                    const maxval) {
+
+    int16_t  row;
+    uint32_t bytesConsumed;
+    uint32_t bytesPerRow;
+    uint32_t sizeRemaining;
+    uint32_t truncatedAndSize;
+
+    sizeRemaining = maxSize;  /* initial value */
+    bytesConsumed = 0;  /* initial value */
+
+    {
+        uint32_t const andSize = (uint32_t)
+            (((1 * hdrP->bm_width + 31) / 32) * 4 * hdrP->bm_height / 2);
+
+        if (sizeRemaining < andSize) {
+            pm_message ("image %2u: "
+                        "Input image ends %u bytes into the %u-byte "
+                        "AND mask.  Implying remainder of mask",
+                        index, sizeRemaining, andSize);
+            truncatedAndSize = sizeRemaining;
+        } else
+            truncatedAndSize = andSize;
+    }
+
+    bytesPerRow = ((1 * hdrP->bm_width + 31) / 32) * 4;
+
+    for (row = 0; row < hdrP->bm_height / 2; ++row) {
+        uint32_t offset;
+
+        if (hdrP->top_down)
+            offset = row * bytesPerRow;
+        else
+            offset = (hdrP->bm_height / 2 - row - 1) * bytesPerRow;
+
+        if (offset + bytesPerRow <= sizeRemaining) {
+            unsigned int col;
+            
+            for (col = 0; col < hdrP->bm_width; ++col) {
+                tuples[row][col][plane] =
+                    ((u8_le(bitmap, offset + col/8)
+                      & (1 << (7 - (col & 0x7)))) == 0x00) ?
+                    maxval : 0
+                ;
+            }
+        }
+    }
+    sizeRemaining -= truncatedAndSize;
+    bytesConsumed += truncatedAndSize;
+}
+
+
+
+static void
+dumpBmpHeader(struct BitmapInfoHeader const hdr,
+              unsigned int            const imageIndex) {
+
+    pm_message("BMP header for Image %u:", imageIndex);
+
+    pm_message("header size: %u", hdr.header_size);
+    pm_message("bitmap width: %u", hdr.bm_width);
+    pm_message("bitmap height * 2: %u", hdr.bm_height);
+    pm_message("row order: %s", hdr.top_down ? "top down" : "bottom up");
+    pm_message("# color planes: %u", hdr.color_planes);
+    pm_message("bits per pixel: %u", hdr.bits_per_pixel);
+    pm_message("image size: %u", hdr.image_size);
+    pm_message("horizontal resolution: %u", hdr.horizontal_resolution);
+    pm_message("vertical resolution: %u", hdr.vertical_resolution);
+    pm_message("# colors in palette: %u", hdr.colors_in_palette);
+    pm_message("# important colors: %u", hdr.important_colors);
+}
+
+
+
+static void
+readBmpHeader(const unsigned char *     const image,
+              uint32_t                  const size,
+              unsigned int              const imageIndex,
+              bool                      const needHeaderDump,
+              struct BitmapInfoHeader * const hdrP) {
+
+    /*  BITMAPINFOHEADER structure */
+
+    if (size < 40)
+        pm_error("image %2u: reading BITMAPINFOHEADER: not enough data.",
+                 imageIndex);
+
+    hdrP->header_size           = u32_le(image,  0);
+    hdrP->bm_width              = s32_le(image,  4);
+    hdrP->bm_height             = s32_le(image,  8);
+    hdrP->color_planes          = u16_le(image, 12);
+    hdrP->bits_per_pixel        = u16_le(image, 14);
+    hdrP->compression_method    = u32_le(image, 16);
+    hdrP->image_size            = u32_le(image, 20);
+    hdrP->horizontal_resolution = s32_le(image, 24);
+    hdrP->vertical_resolution   = s32_le(image, 28);
+    hdrP->colors_in_palette     = u32_le(image, 32);
+    hdrP->important_colors      = u32_le(image, 36);
+
+    if (hdrP->bm_height > 0) {
+        hdrP->top_down = false;
+    } else {
+        hdrP->top_down   = true;
+        hdrP->bm_height *= -1;
+    }
+
+    if (hdrP->header_size < 36
+        || hdrP->bm_width == 0 || hdrP->bm_height == 0
+        || (hdrP->bm_height & 1) != 0x0000) {
+        pm_error("image %2u: format violation: invalid BMP header.",
+                 imageIndex);
+    }
+
+    if (needHeaderDump)
+        dumpBmpHeader(*hdrP, imageIndex);
+}
+
+
+
+static void
+readXorMask(struct BitmapInfoHeader * const hdrP,
+            const unsigned char *     const imageCursor,
+            uint32_t                  const imageSize,
+            tuple **                  const tuples,
+            uint16_t                  const index,
+            bool                      const needHeaderDump,
+            bool *                    const haveAlphaP,
+            uint32_t *                const bytesConsumedP) {
+/*----------------------------------------------------------------------------
+   Read the so-called XOR mask (for non-monochrome images, this is the
+   color pixmap)
+-----------------------------------------------------------------------------*/
+    /*  preset the PAM with fully opaque black (just in case the image
+        is truncated and not all pixels are filled in below).
+    */
+    {
+        unsigned int row;
+
+        for (row = 0; row < hdrP->bm_height / 2; ++row) {
+            unsigned int col;
+            for (col = 0; col < hdrP->bm_width; ++col) {
+                tuples[row][col][PAM_RED_PLANE] = 0;
+                tuples[row][col][PAM_GRN_PLANE] = 0;
+                tuples[row][col][PAM_BLU_PLANE] = 0;
+                tuples[row][col][PAM_TRN_PLANE] = 255;
+            }
+        }
+    }
+
+    if (hdrP->bits_per_pixel < 16) {
+        readXorPalette(hdrP, imageCursor, imageSize, tuples, index,
+                       needHeaderDump,
+                       bytesConsumedP);
+        *haveAlphaP = false;
+    } else
+        readXorBitfields(hdrP, imageCursor, imageSize, tuples, index,
+                         haveAlphaP, bytesConsumedP);
+}
+
+
+
+static void
+reportImage(unsigned int            const imageIndex,
+            struct BitmapInfoHeader const hdr,
+            bool                    const haveAlpha) {
+
+    const char * const style = 
+        haveAlpha ? "RGB +alpha" :
+        hdr.bits_per_pixel < 16 ? "RGB/palette" :
+        "RGB"
+        ;
+
+    pm_message("image %2u: "
+               "BMP %3u x %3u x %2u (%s)",
+               imageIndex,
+               hdr.bm_width, hdr.bm_height / 2, hdr.bits_per_pixel,
+               style);
+}
+
+
+
+static void
+convertBmp(const unsigned char * const image,
+           FILE *                const ofP,
+           struct IconDirEntry * const dirEntryP,
+           bool                  const needHeaderDump,
+           bool                  const wantAndMaskPlane) {
+    
+    struct BitmapInfoHeader hdr;
+    uint32_t                offset;
+    bool                    haveAlpha;
+    uint32_t                xorByteCt;
+
+    struct pam outpam;
+    tuple **   tuples;
+
+    readBmpHeader(image, dirEntryP->size, dirEntryP->index, needHeaderDump,
+                  &hdr);
+
+    offset = hdr.header_size;  /* Start after header */
+
+    if ((dirEntryP->width != hdr.bm_width)
+        || (dirEntryP->height != hdr.bm_height / 2)) {
+        pm_message("image %2u: "
+                   "mismatch in header and image dimensions "
+                   "(%u x %u vs. %u x %u)",
+                   dirEntryP->index,
+                   dirEntryP->width,
+                   dirEntryP->height,
+                   hdr.bm_width,
+                   hdr.bm_height / 2);
+    }
+
+    if ((dirEntryP->bits_per_pixel != 0)
+        && (dirEntryP->bits_per_pixel != hdr.bits_per_pixel)) {
+        pm_message("image %2u "
+                   "mismatch in header and image bpp value"
+                   "(%u vs. %u)",
+                   dirEntryP->index,
+                   dirEntryP->bits_per_pixel,
+                   hdr.bits_per_pixel);
+    }
+
+    outpam.size   = sizeof(struct pam);
+    outpam.len    = PAM_STRUCT_SIZE(allocation_depth);
+    outpam.file   = ofP;
+    outpam.format = PAM_FORMAT;
+    outpam.width  = hdr.bm_width;
+    outpam.height = hdr.bm_height / 2;
+    outpam.maxval = 255;
+    outpam.allocation_depth = 5;
+    outpam.depth  = 0;
+        /* Just for tuple array allocation; we set the value for the actual
+           output image below.
+        */
+
+    tuples = pnm_allocpamarray(&outpam);
+
+    readXorMask(&hdr, &image[offset], 
+                dirEntryP->size - offset,
+                tuples, dirEntryP->index, needHeaderDump,
+                &haveAlpha, &xorByteCt);
+
+    offset += xorByteCt;
+
+    {
+        /* If there is no alpha channel in XOR mask, store the AND mask to
+           the transparency plane.  Else, here are two transparency
+           maps. If requested, store the AND mask to a fifth PAM plane
+        */
+        bool haveAnd;
+        unsigned int andPlane;
+
+        if (!haveAlpha) {
+            haveAnd = true;
+            andPlane = PAM_TRN_PLANE;
+            strcpy (outpam.tuple_type, "RGB_ALPHA");
+            outpam.depth  = 4;
+        } else if (wantAndMaskPlane) {
+            haveAnd = true;
+            andPlane = PAM_TRN_PLANE + 1;
+            outpam.depth  = 5;
+            strcpy(outpam.tuple_type, "RGB_ALPHA_ANDMASK");
+        } else {
+            haveAnd = false;
+            strcpy (outpam.tuple_type, "RGB_ALPHA");
+            outpam.depth  = 4;
+        }
+        if (haveAnd) {
+            readAnd(&hdr, &image[offset], dirEntryP->size - offset,
+                    tuples, dirEntryP->index, andPlane, outpam.maxval);
+        }
+    }
+    pnm_writepam(&outpam, tuples);
+    pnm_freepamarray(tuples, &outpam);
+
+    reportImage(dirEntryP->index, hdr, haveAlpha);
+}
+
+
+
+static void
+reportPngInfo(const unsigned char * const image,
+              struct IconDirEntry * const dirEntryP) {
+    
+    struct PngIhdr ihdr;
+
+    ihdr.length      = u32_be (image, sizeof(pngHeader)  +0);
+    ihdr.signature   = u32_xx (image, sizeof(pngHeader)  +4);
+    ihdr.width       = u32_be (image, sizeof(pngHeader)  +8);
+    ihdr.height      = u32_be (image, sizeof(pngHeader) +12);
+    ihdr.bit_depth   = u8_be  (image, sizeof(pngHeader) +16);
+    ihdr.color_type  = u8_be  (image, sizeof(pngHeader) +17);
+    ihdr.compression = u8_be  (image, sizeof(pngHeader) +18);
+    ihdr.filter      = u8_be  (image, sizeof(pngHeader) +19);
+    ihdr.interlace   = u8_be  (image, sizeof(pngHeader) +20);
+
+    if ((ihdr.length != 13)
+        || ihdr.signature != *(uint32_t*)"IHDR") {
+        pm_message("image %2u: PNG (uncommonly formatted)",
+                   dirEntryP->index);
+    } else {
+        uint32_t depth;
+        const char * colorType;
+
+        switch (ihdr.color_type) {
+        case 0:
+            colorType = "grayscale";
+            depth     = ihdr.bit_depth;
+            break;
+
+        case 2:
+            colorType = "RGB";
+            depth     = ihdr.bit_depth * 3;
+            break;
+
+        case 3:
+            colorType = "RGB/palette";
+            depth     = 8;
+            break;
+
+        case 4:
+            colorType = "grayscale + alpha";
+            depth     = ihdr.bit_depth * 2;
+            break;
+
+        case 6:
+            colorType = "RGB + alpha";
+            depth     = ihdr.bit_depth * 4;
+            break;
+
+        default:
+            colorType = "unknown color system";
+            depth     = 0;
+            break;
+        }
+        pm_message("image %2u: PNG %3u x %3u x %2u (%s)",
+                   dirEntryP->index,
+                   ihdr.width, ihdr.height, depth, colorType);
+
+        if ((dirEntryP->width != ihdr.width)
+            || (dirEntryP->height != ihdr.height)) {
+            pm_message("image %2u:"
+                       " mismatch in header and image dimensions"
+                       " (%u x %u vs %u x %u)",
+                       dirEntryP->index, dirEntryP->width, dirEntryP->height,
+                       ihdr.width, ihdr.height);
+        }
+        /* Mismatch between dirEntryP->bits_per_pixel and 'depth' is
+           normal, because the creator of the winicon file doesn't necessarily
+           know the true color resolution.
+        */
+    }
+}
+
+
+
+static void
+convertPng(const unsigned char * const image,
+           FILE *                const ofP,
+           struct IconDirEntry * const dirEntryP) {
+
+    struct bufferDesc imageBuffer;
+
+    reportPngInfo(image, dirEntryP);
+
+    imageBuffer.size = dirEntryP->size;
+    imageBuffer.buffer = (unsigned char *)image;
+
+    fflush (stdout);
+    pm_system(pm_feed_from_memory, &imageBuffer,
+              NULL /* stdout accepter */, NULL,
+              "pngtopam -alphapam");
+}
+
+
+
+static uint32_t
+bestImage(struct IconDir * const dirP) {
+
+    uint32_t imageIndex;
+    uint32_t bestPixelCt;
+    uint32_t bestColorCt;
+    uint16_t best;
+
+    bestPixelCt = 0;  /* initial value */
+    bestColorCt = 0;  /* initial value */
+    best        = 0;  /* initial value */
+    
+    for (imageIndex = 0; dirP->count > imageIndex; ++imageIndex) {
+        struct IconDirEntry * const dirEntryP = &dirP->entries[imageIndex];
+
+        uint32_t const pixelCt = dirEntryP->width * dirEntryP->height;
+
+        uint32_t colorCt;
+
+        /*  32-bit icons have 24 bit color information only.
+         
+            Since NT 5.1 (aka WinXP), it is allowed to place 8-bit
+            transparency information in the remaining bits (to check,
+            you have to read all these bits in the image!), so I prefer
+            32-bit images over 24-bit images (which violate the
+            spec. anyway).
+        */
+        if (dirEntryP->bits_per_pixel > 24)
+            colorCt = 1u << 25;
+        else
+            colorCt = 1u << dirEntryP->bits_per_pixel;
+
+        if (dirEntryP->color_count != 0 && colorCt > dirEntryP->color_count)
+            colorCt = dirEntryP->color_count;
+
+        if ((pixelCt > bestPixelCt)
+            || ((pixelCt == bestPixelCt) && (colorCt > bestColorCt))) {
+            /* This is a new best */
+            bestPixelCt = pixelCt;
+            bestColorCt = colorCt;
+            best        = imageIndex;
+        }
+    }
+    return best;
+}
+
+
+
+static void
+convertImage(struct File *         const icoP,
+             struct IconDirEntry * const dirEntryP,
+             FILE *                const ofP,
+             bool                  const needHeaderDump,
+             bool                  const wantAndMaskPlane) {
+
+    const unsigned char * image;  /* malloced */
+
+    image = readImage(icoP, dirEntryP);
+
+    if (MEMEQ(image, pngHeader, sizeof (pngHeader)))
+        convertPng(image, ofP, dirEntryP);
+    else
+        convertBmp(image, ofP, dirEntryP, needHeaderDump, wantAndMaskPlane);
+
+    free((void *)image);
+}
+
+
+
+int
+main (int argc, const char *argv []) {
+
+    struct File ico;
+    struct IconDir * dirP;
+    struct CmdlineInfo cmdline;
+
+    pm_proginit (&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+
+    ico.name =
+        streq(cmdline.inputFileName, "-") ?  "<stdin>" : cmdline.inputFileName;
+    ico.pos   = 0;
+    ico.fileP = pm_openr(cmdline.inputFileName);
+
+    dirP = readIconDir(&ico, cmdline.headerdump);
+
+    if (cmdline.allimages) {
+        unsigned int i;
+        for (i = 0; i < dirP->count; ++i)
+            convertImage(&ico, &dirP->entries[i], stdout,
+                         cmdline.headerdump, cmdline.andmasks);
+    } else if (cmdline.imageSpec) {
+        unsigned int i;
+        bool found;
+        for (i = 0, found = false; i < dirP->count; ++i) {
+            if (dirP->entries[i].index == cmdline.image) {
+                found = true;
+                convertImage(&ico, &dirP->entries[i], stdout,
+                             cmdline.headerdump, cmdline.andmasks);
+            }
+        }
+        if (!found)
+            pm_error("no image index %u in.input", cmdline.image);
+    } else {
+        convertImage(&ico, &dirP->entries[bestImage(dirP)], stdout,
+                     cmdline.headerdump, cmdline.andmasks);
+    }
+    
+    freeIconDir(dirP);
+
+    if (ico.fileP != stdin)
+        pm_close(ico.fileP);
+
+    return 0;
+}
+
+
+
diff --git a/converter/other/xwdtopnm.c b/converter/other/xwdtopnm.c
index 2cf1d39b..d49a2b09 100644
--- a/converter/other/xwdtopnm.c
+++ b/converter/other/xwdtopnm.c
@@ -113,7 +113,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc - 1 == 0)
@@ -837,7 +837,7 @@ typedef struct {
 
            'nBitsLeft' tells how many bits are in the buffer now.  It's
            zero when nothing has ever been read from the file.  Only
-           the least signficant 'nBitsLeft' bits are meaningful.
+           the least significant 'nBitsLeft' bits are meaningful.
 
            The numeric value of the member is the number whose pure
            binary representation is the bit string in the buffer.
@@ -974,7 +974,7 @@ readItem(pixelReader * const rdrP) {
 
 static unsigned long const lsbmask[] = {
 /*----------------------------------------------------------------------------
-   lsbmask[i] is the mask you use to select the i least signficant bits
+   lsbmask[i] is the mask you use to select the i least significant bits
    of a bit string.
 -----------------------------------------------------------------------------*/
     0x00000000,
diff --git a/converter/other/yuy2topam.c b/converter/other/yuy2topam.c
new file mode 100644
index 00000000..40ab98b3
--- /dev/null
+++ b/converter/other/yuy2topam.c
@@ -0,0 +1,266 @@
+/* Convert an YUY2 image to a PAM image
+ *
+ * See
+ * http://msdn.microsoft.com/en-us/library/aa904813%28VS.80%29.aspx#yuvformats_2
+ * and http://www.digitalpreservation.gov/formats/fdd/fdd000364.shtml for
+ * details.
+ *
+ * By Michael Haardt 2014.
+ *
+ * Contributed to the public domain by its author.
+ *
+ * Recoded in Netpbm style by Bryan Henderson
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "pm.h"
+#include "pam.h"
+#include "shhopt.h"
+
+
+
+struct CmdlineInfo {
+    const char * inputFileName;
+    unsigned int width;
+    unsigned int height;
+};
+
+
+
+static void 
+parseCommandLine(int argc, const char ** argv, 
+                 struct CmdlineInfo * const cmdlineP) {
+/* --------------------------------------------------------------------------
+   Parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+--------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to pm_optParseOptions3 on how to parse our options. */
+    optStruct3 opt;
+
+    unsigned int widthSpec, heightSpec;
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "width",    OPT_UINT,
+            &cmdlineP->width,   &widthSpec,                             0);
+    OPTENT3(0, "height",   OPT_UINT,
+            &cmdlineP->height,  &heightSpec,                            0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = false;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = false;   /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!widthSpec)
+        pm_error("You must specify the image width with -width");
+    if (cmdlineP->width == 0)
+        pm_error("-width cannot be zero");
+
+    if (cmdlineP->width % 2 != 0)
+        pm_error("-width %u is odd, but YUY2 images must have an even width.",
+                 cmdlineP->width);
+
+    if (!heightSpec)
+        pm_error("You must specify the image height with -height");
+    if (cmdlineP->height == 0)
+        pm_error("-height cannot be zero");
+
+    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);
+    }
+}
+
+
+
+typedef struct {
+    int y0;
+    int y1;
+    int u;
+    int v;
+} Yuy2Pixel;
+
+
+
+static Yuy2Pixel
+readPixel(FILE * const ifP) {
+/*----------------------------------------------------------------------------
+   Read one pixel from the YUY2 input.  YUY2 represents a pixel in 4 bytes.
+-----------------------------------------------------------------------------*/
+    Yuy2Pixel retval;
+    unsigned char c;
+
+    pm_readcharu(ifP, &c); retval.y0 = c -  16;
+    pm_readcharu(ifP, &c); retval.u  = c - 128;
+    pm_readcharu(ifP, &c); retval.y1 = c -  16;
+    pm_readcharu(ifP, &c); retval.v  = c - 128;
+
+    return retval;
+}
+
+
+
+typedef struct {
+    int a1;
+    int a2;
+    int a3;
+    int a4;
+} UvCoeff;
+
+typedef struct {
+    int a0a;
+    int a0b;
+    UvCoeff uv;
+} Coeff;
+
+
+
+static Coeff
+coeffFromYuy2(Yuy2Pixel const yuy2) {
+
+    Coeff retval;
+
+    retval.a0a   = 298 * yuy2.y0;
+    retval.a0b   = 298 * yuy2.y1;
+    retval.uv.a1 = 409 * yuy2.v;
+    retval.uv.a2 = 100 * yuy2.u;
+    retval.uv.a3 = 208 * yuy2.v;
+    retval.uv.a4 = 516 * yuy2.u;
+
+    return retval;
+}
+
+
+
+typedef struct {
+    int r;
+    int g;
+    int b;
+} Rgb;
+
+
+
+static Rgb
+rgbFromCoeff(int     const a0,
+             UvCoeff const uv) {
+
+    Rgb retval;
+
+    retval.r = (a0 + uv.a1 + 128) >> 8;
+    retval.g = (a0 - uv.a2 - uv.a3 + 128) >> 8;
+    retval.b = (a0 + uv.a4 + 128) >> 8;
+
+    return retval;
+}
+
+
+
+static Rgb
+rgbFromCoeff0(Coeff const coeff) {
+
+    return rgbFromCoeff(coeff.a0a, coeff.uv);
+}
+
+
+
+static Rgb
+rgbFromCoeff1(Coeff const coeff) {
+
+    return rgbFromCoeff(coeff.a0b, coeff.uv);
+}
+
+
+
+static void
+rgbToTuple(Rgb   const rgb,
+           tuple const out) {
+
+    out[PAM_RED_PLANE] = MIN(255, MAX(0, rgb.r));
+    out[PAM_GRN_PLANE] = MIN(255, MAX(0, rgb.g));
+    out[PAM_BLU_PLANE] = MIN(255, MAX(0, rgb.b));
+}
+
+
+
+static void
+yuy2topam(const char * const fileName,
+          unsigned int const width,
+          unsigned int const height) {
+
+    FILE * ifP;
+    struct pam outpam;
+    tuple * tuplerow;
+    unsigned int row;
+
+    outpam.size             = sizeof(struct pam);
+    outpam.len              = PAM_STRUCT_SIZE(allocation_depth);
+    outpam.file             = stdout;
+    outpam.format           = PAM_FORMAT;
+    outpam.plainformat      = 0;
+    outpam.width            = width;
+    outpam.height           = height;
+    outpam.depth            = 3;
+    outpam.maxval           = 255;
+    outpam.bytes_per_sample = 1;
+    strcpy(outpam.tuple_type, PAM_PPM_TUPLETYPE);
+    outpam.allocation_depth = 3;
+
+    ifP = pm_openr(fileName);
+
+    pnm_writepaminit(&outpam);
+
+    tuplerow = pnm_allocpamrow(&outpam);
+
+    for (row = 0; row < outpam.height; ++row) {
+        unsigned int col;
+
+        for (col = 0; col < outpam.width; col += 2) {
+            Yuy2Pixel const yuy2 = readPixel(ifP);
+
+            Coeff const coeff = coeffFromYuy2(yuy2);
+
+            rgbToTuple(rgbFromCoeff0(coeff), tuplerow[col]);
+            rgbToTuple(rgbFromCoeff1(coeff), tuplerow[col+1]);
+        }
+        pnm_writepamrow(&outpam, tuplerow);
+    }
+    pnm_freepamrow(tuplerow);
+
+    pm_closer(ifP);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    struct CmdlineInfo cmdline;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    yuy2topam(cmdline.inputFileName, cmdline.width, cmdline.height);
+
+    return 0;
+}
diff --git a/converter/pbm/Makefile b/converter/pbm/Makefile
index c859b105..602cb156 100644
--- a/converter/pbm/Makefile
+++ b/converter/pbm/Makefile
@@ -7,12 +7,13 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 include $(BUILDDIR)/config.mk
 
-PORTBINARIES =	atktopbm brushtopbm cmuwmtopbm ddbugtopbm g3topbm escp2topbm \
-		icontopbm macptopbm mdatopbm mgrtopbm mrftopbm \
+PORTBINARIES =	atktopbm brushtopbm cistopbm cmuwmtopbm \
+		ddbugtopbm g3topbm escp2topbm \
+		macptopbm mdatopbm mgrtopbm mrftopbm \
 		pbmto10x pbmto4425 pbmtoascii pbmtoatk \
-		pbmtobbnbg pbmtocmuwm pbmtodjvurle \
+		pbmtobbnbg pbmtocis pbmtocmuwm pbmtodjvurle \
 		pbmtoepsi pbmtoepson pbmtoescp2 \
-		pbmtog3 pbmtogem pbmtogo pbmtoibm23xx pbmtoicon pbmtolj \
+		pbmtog3 pbmtogem pbmtogo pbmtoibm23xx pbmtosunicon pbmtolj \
 		pbmtoln03 pbmtolps \
 		pbmtomacp pbmtomatrixorbital pbmtomda pbmtomgr pbmtomrf \
 		pbmtonokia \
@@ -72,6 +73,14 @@ thinkjettopbm.c:%.c:%.c1 $(SRCDIR)/lib/util/lexheader
 	  grep -v "^[[:space:]]*int yywrap(void);" \
 	  >$@
 
+install.bin: install.bin.local
+.PHONY: install.bin.local
+install.bin.local: $(PKGDIR)/bin
+# Remember that $(SYMLINK) might just be a copy command.
+# In December 2010 (Actually January 2011), pbmtosunicon replaced pbmtoicon
+	cd $(PKGDIR)/bin ; \
+	$(SYMLINK) pbmtosunicon$(EXE) pbmtoicon$(EXE)
+
 thisdirclean: localclean
 .PHONY: localclean
 localclean:
diff --git a/converter/pbm/atktopbm.c b/converter/pbm/atktopbm.c
index 62664999..807e4f4a 100644
--- a/converter/pbm/atktopbm.c
+++ b/converter/pbm/atktopbm.c
@@ -61,17 +61,24 @@
 */
 
 /* macros to generate case entries for switch statement */
-#define case1(v) case v
-#define case4(v) case v: case (v)+1: case (v)+2: case(v)+3
-#define case6(v) case4(v): case ((v)+4): case ((v)+5)
-#define case8(v) case4(v): case4((v)+4)
+#define CASE1(v) case v
+#define CASE4(v) case v: case (v)+1: case (v)+2: case(v)+3
+#define CASE6(v) CASE4(v): case ((v)+4): case ((v)+5)
+#define CASE8(v) CASE4(v): CASE4((v)+4)
+
+
 
 static long
-ReadRow(FILE * const file, unsigned char * const row, long const length) {
+ReadRow(FILE *          const file,
+        unsigned char * const row,
+        long            const length) {
 /*----------------------------------------------------------------------------
   'file' is where to get them from.
   'row' is where to put bytes.
   'length' is how many bytes in row must be filled.
+  
+  Return the delimiter that marks the end of the row, or EOF if EOF marks
+  the end of the row, or NUL in some cases.
 -----------------------------------------------------------------------------*/
     /* Each input character is processed by the central loop.  There are 
     ** some input codes which require two or three characters for
@@ -80,18 +87,23 @@ ReadRow(FILE * const file, unsigned char * const row, long const length) {
     ** to the Ready state whenever a character unacceptable to the
     ** current state is read.
     */
-    enum stateCode {
-        Ready,      /* any input code is allowed */
-        HexDigitPending,    /* have seen the first of a hex digit pair */
-        RepeatPending,  /* repeat code has been seen:
-                   must be followed by two hex digits */
-        RepeatAndDigit};    /* have seen repeat code and its first
-                   following digit */
-    enum stateCode InputState;  /* current state */
-    register int c;     /* the current input character */
-    register long repeatcount = 0;  /* current repeat value */
-    register long hexval;   /* current hex value */
-    long pendinghex = 0;    /* the first of a pair of hex characters */
+    enum StateCode {
+        Ready,
+            /* any input code is allowed */
+        HexDigitPending,
+            /* have seen the first of a hex digit pair */
+        RepeatPending,
+            /* repeat code has been seen: must be followed by two hex digits
+             */
+        RepeatAndDigit
+            /* have seen repeat code and its first following digit */
+    };
+    
+    enum StateCode InputState;  /* current state */
+    int c;     /* the current input character */
+    long repeatcount;  /* current repeat value */
+    long hexval;   /* current hex value */
+    long pendinghex;    /* the first of a pair of hex characters */
     int lengthRemaining;
     unsigned char * cursor;
     
@@ -101,262 +113,239 @@ ReadRow(FILE * const file, unsigned char * const row, long const length) {
     ** zero, we ungetc the byte.
     */
 
-    lengthRemaining = length;
-    cursor = row;
+    repeatcount = 0;  /* initial value */
+    pendinghex = 0;  /* initial value */
+
+    lengthRemaining = length;  /* initial value */
+    cursor = row;  /* initial value */
+    InputState = Ready;  /* initial value */
 
-    InputState = Ready;
     while ((c=getc(file)) != EOF) switch (c) {
 
-    case8(0x0):
-    case8(0x8):
-    case8(0x10):
-    case8(0x18):
-    case1(' '):
-        /* control characters and space are legal and ignored */
-        break;
-    case1(0x40):    /* '@' */
-    case1(0x5B):    /* '[' */
-    case4(0x5D):    /*  ']'  '^'  '_'  '`' */
-    case4(0x7D):    /* '}'  '~'  DEL  0x80 */
-    default:        /* all above 0x80 */
-        /* error code:  Ignored at present.  Reset InputState. */
-        InputState = Ready;
-        break;
-
-    case1(0x7B):    /* '{' */
-    case1(0x5C):    /* '\\' */
-        /* illegal end of line:  exit anyway */
-        ungetc(c, file);    /* retain terminator in stream */
-        /* DROP THROUGH */
-    case1(0x7C):    /* '|' */
-        /* legal end of row: may have to pad  */
-        while (lengthRemaining-- > 0)
-            *cursor++ = WHITEBYTE;
-        return c;
+        CASE8(0x0):
+        CASE8(0x8):
+        CASE8(0x10):
+        CASE8(0x18):
+        CASE1(' '):
+            /* control characters and space are legal and ignored */
+            break;
+        CASE1(0x40):    /* '@' */
+        CASE1(0x5B):    /* '[' */
+        CASE4(0x5D):    /*  ']'  '^'  '_'  '`' */
+        CASE4(0x7D):    /* '}'  '~'  DEL  0x80 */
+        default:        /* all above 0x80 */
+            /* error code:  Ignored at present.  Reset InputState. */
+            InputState = Ready;
+            break;
+
+        CASE1(0x7B):    /* '{' */
+        CASE1(0x5C):    /* '\\' */
+            /* illegal end of line:  exit anyway */
+            ungetc(c, file);    /* retain terminator in stream */
+            /* DROP THROUGH */
+        CASE1(0x7C):    /* '|' */
+            /* legal end of row: may have to pad  */
+            while (lengthRemaining-- > 0)
+                *cursor++ = WHITEBYTE;
+            return c;
     
-    case1(0x21):
-    case6(0x22):
-    case8(0x28):
-        /* punctuation characters: repeat byte given by two
-        ** succeeding hex chars
-        */
-        if (lengthRemaining <= 0) {
-            ungetc(c, file);
-            return('\0');
-        }
-        repeatcount = c - OTHERZERO;
-        InputState = RepeatPending;
-        break;
-
-    case8(0x30):
-    case8(0x38):
-        /* digit (or following punctuation)  -  hex digit */
-        hexval = c - 0x30;
-        goto hexdigit;
-    case6(0x41):
-        /* A ... F    -  hex digit */
-        hexval = c - (0x41 - 0xA);
-        goto hexdigit;
-    case6(0x61):
-        /* a ... f  - hex digit */
-        hexval = c - (0x61 - 0xA);
-        goto hexdigit;
-
-    case8(0x67):
-    case8(0x6F):
-    case4(0x77):
-        /* g ... z   -   multiple WHITE bytes */
-        if (lengthRemaining <= 0) {
-            ungetc(c, file);
-            return('\0');
-        }
-        repeatcount = c - WHITEZERO;
-        hexval = WHITEBYTE;
-        goto store;
-    case8(0x47):
-    case8(0x4F):
-    case4(0x57):
-        /* G ... Z   -   multiple BLACK bytes */
-        if (lengthRemaining <= 0) {
-            ungetc(c, file);
-            return('\0');
-        }
-        repeatcount = c - BLACKZERO;
-        hexval = BLACKBYTE;
-        goto store;
-
-hexdigit:
-        /* process a hex digit.  Use InputState to determine
-            what to do with it. */
-        if (lengthRemaining <= 0) {
-            ungetc(c, file);
-            return('\0');
-        }
-        switch(InputState) {
-        case Ready:
-            InputState = HexDigitPending;
-            pendinghex = hexval << 4;
+        CASE1(0x21):
+        CASE6(0x22):
+        CASE8(0x28):
+            /* punctuation characters: repeat byte given by two
+            ** succeeding hex chars
+            */
+            if (lengthRemaining <= 0) {
+                ungetc(c, file);
+                return('\0');
+            }
+            repeatcount = c - OTHERZERO;
+            InputState = RepeatPending;
             break;
-        case HexDigitPending:
-            hexval |= pendinghex;
-            repeatcount = 1;
+
+        CASE8(0x30):
+        CASE8(0x38):
+            /* digit (or following punctuation)  -  hex digit */
+            hexval = c - 0x30;
+            goto hexdigit;
+        CASE6(0x41):
+            /* A ... F    -  hex digit */
+            hexval = c - (0x41 - 0xA);
+            goto hexdigit;
+        CASE6(0x61):
+            /* a ... f  - hex digit */
+            hexval = c - (0x61 - 0xA);
+            goto hexdigit;
+
+        CASE8(0x67):
+        CASE8(0x6F):
+        CASE4(0x77):
+            /* g ... z   -   multiple WHITE bytes */
+            if (lengthRemaining <= 0) {
+                ungetc(c, file);
+                return('\0');
+            }
+            repeatcount = c - WHITEZERO;
+            hexval = WHITEBYTE;
             goto store;
-        case RepeatPending:
-            InputState = RepeatAndDigit;
-            pendinghex = hexval << 4;
-            break;
-        case RepeatAndDigit:
-            hexval |= pendinghex;
+        CASE8(0x47):
+        CASE8(0x4F):
+        CASE4(0x57):
+            /* G ... Z   -   multiple BLACK bytes */
+            if (lengthRemaining <= 0) {
+                ungetc(c, file);
+                return('\0');
+            }
+            repeatcount = c - BLACKZERO;
+            hexval = BLACKBYTE;
             goto store;
-        }
-        break;
-
-store:
-        /* generate byte(s) into the output row 
-            Use repeatcount, depending on state.  */
-        if (lengthRemaining < repeatcount) 
-            /* reduce repeat count if it would exceed
-                available space */
-            repeatcount = lengthRemaining;
-        lengthRemaining -= repeatcount;  /* do this before repeatcount-- */
-        while (repeatcount-- > 0)
+
+        hexdigit:
+            /* process a hex digit.  Use InputState to determine
+               what to do with it. */
+            if (lengthRemaining <= 0) {
+                ungetc(c, file);
+                return('\0');
+            }
+            switch(InputState) {
+            case Ready:
+                InputState = HexDigitPending;
+                pendinghex = hexval << 4;
+                break;
+            case HexDigitPending:
+                hexval |= pendinghex;
+                repeatcount = 1;
+                goto store;
+            case RepeatPending:
+                InputState = RepeatAndDigit;
+                pendinghex = hexval << 4;
+                break;
+            case RepeatAndDigit:
+                hexval |= pendinghex;
+                goto store;
+            }
+            break;
+
+        store:
+            /* generate byte(s) into the output row 
+               Use repeatcount, depending on state.  */
+            if (lengthRemaining < repeatcount) 
+                /* reduce repeat count if it would exceed
+                   available space */
+                repeatcount = lengthRemaining;
+            lengthRemaining -= repeatcount;  /* do this before repeatcount-- */
+            while (repeatcount-- > 0)
                 *cursor++ = hexval;
-        InputState = Ready;
-        break;
+            InputState = Ready;
+            break;
 
-    } /* end of while( - )switch( - ) */
+        } /* end of while( - )switch( - ) */
     return EOF;
 }
 
 
 
-#undef case1
-#undef case4
-#undef case6
-#undef case8
+#undef CASE1
+#undef CASE4
+#undef CASE6
+#undef CASE8
 
 
 
 static void
-ReadATKRaster(FILE * const file, 
-              int * const rwidth, 
-              int * const rheight, 
-              unsigned char ** const destaddrP) {
+ReadATKRaster(FILE * const ifP) {
 
-    int row, rowlen;  /* count rows;  byte length of row */
+    int row;  /* count rows;  byte length of row */
     int version;
     char keyword[6];
     int discardid;
     int objectid;     /* id read for the incoming pixel image */
     long tc;            /* temp */
     int width, height;      /* dimensions of image */
+    bit * bitrow;
 
-    if (fscanf(file, "\\begindata{raster,%d", &discardid) != 1
-                || getc(file) != '}' || getc(file) != '\n')
-      pm_error ("input file not Andrew raster object");
+    if (fscanf(ifP, "\\begindata{raster,%d", &discardid) != 1
+        || getc(ifP) != '}' || getc(ifP) != '\n')
+        pm_error ("input file not Andrew raster object");
 
-    fscanf(file, " %d ", &version);
+    fscanf(ifP, " %d ", &version);
     if (version < 2) 
-      pm_error ("version too old to parse");
+        pm_error ("version too old to parse");
 
     {
         unsigned int options;
         long xscale, yscale;
         long xoffset, yoffset, subwidth, subheight;
         /* ignore all these features: */
-        fscanf(file, " %u %ld %ld %ld %ld %ld %ld",  
+        fscanf(ifP, " %u %ld %ld %ld %ld %ld %ld",  
                &options, &xscale, &yscale, &xoffset, 
                &yoffset, &subwidth, &subheight);
     }
     /* scan to end of line in case this is actually something beyond V2 */
-    while (((tc=getc(file)) != '\n') && (tc != '\\') && (tc != EOF)) {}
+    while (((tc=getc(ifP)) != '\n') && (tc != '\\') && (tc != EOF)) {}
 
     /* read the keyword */
-    fscanf(file, " %5s", keyword);
+    fscanf(ifP, " %5s", keyword);
     if (!streq(keyword, "bits"))
-      pm_error ("keyword is not 'bits'!");
+        pm_error ("keyword is not 'bits'!");
 
-    fscanf(file, " %d %d %d ", &objectid, &width, &height);
+    fscanf(ifP, " %d %d %d ", &objectid, &width, &height);
 
     if (width < 1 || height < 1 || width > 1000000 || height > 1000000) 
-      pm_error ("bad width or height");
-
-    *rwidth = width;
-    *rheight = height;
-    rowlen = (width + 7) / 8;
-    MALLOCARRAY(*destaddrP, height * rowlen);
-    if (destaddrP == NULL)
-        pm_error("Unable to allocate %u bytes for the input image.",
-                 height * rowlen);
-    for (row = 0;   row < height;   row++)
-      {
-        long c;
-
-        c = ReadRow(file, *destaddrP + (row * rowlen), rowlen);
-        if (c != '|')
-          {
-        if (c == EOF)
-          pm_error ("premature EOF");
-        else
-          pm_error ("bad format");
-        break;
-          }
-      }
-    while (! feof(file) && getc(file) != '\\') {};  /* scan for \enddata */
-    if (fscanf(file, "enddata{raster,%d", &discardid) != 1
-        || getc(file) != '}' || getc(file) != '\n')
-      pm_error ("missing end-of-object marker");
-}
+        pm_error("bad width or height");
 
+    pbm_writepbminit(stdout, width, height, 0);
+    bitrow = pbm_allocrow_packed(width);
 
+    for (row = 0;   row < height; ++row) {
+        unsigned int const rowlen = (width + 7) / 8;
+        long const nextChar = ReadRow(ifP, bitrow, rowlen);
 
-int
-main(int argc, char **argv) {
+        switch (nextChar) {
+        case '|': 
+            pbm_writepbmrow_packed(stdout, bitrow, width, 0);
+            break;
+        case EOF:
+            pm_error("premature EOF");
+            break;
+        default:
+            pm_error("bad format");
+        }
+    }
 
-    FILE *ifp;
-    register bit *bitrow, *bP;
-    int rows, cols, row, col, charcount;
-    unsigned char *data, mask;
+    pbm_freerow_packed(bitrow);
 
+    while (! feof(ifP) && getc(ifP) != '\\') {};  /* scan for \enddata */
 
-    pbm_init ( &argc, argv );
+    if (fscanf(ifP, "enddata{raster,%d", &discardid) != 1
+        || getc(ifP) != '}' || getc(ifP) != '\n')
+        pm_error("missing end-of-object marker");
+}
 
-    if ( argc > 2 )
-        pm_usage( "[raster obj]" );
-    
-    if ( argc == 2 )
-        ifp = pm_openr( argv[1] );
-    else
-        ifp = stdin;
 
-    ReadATKRaster( ifp, &cols, &rows, &data );
 
-    pm_close( ifp );
+int
+main(int argc, const char ** argv) {
 
-    pbm_writepbminit( stdout, cols, rows, 0 );
-    bitrow = pbm_allocrow( cols );
+    FILE * ifP;
 
-    for ( row = 0; row < rows; ++row )
-    {
-        charcount = 0;
-        mask = 0x80;
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-        {
-            if ( charcount >= 8 )
-            {
-                ++data;
-                charcount = 0;
-                mask = 0x80;
-            }
-            *bP = ( *data & mask ) ? PBM_BLACK : PBM_WHITE;
-            ++charcount;
-            mask >>= 1;
-        }
-        ++data;
-        pbm_writepbmrow( stdout, bitrow, cols, 0 );
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        ifP = stdin;
+    else {
+        ifP = pm_openr(argv[1]);
+
+        if (argc-1 > 1)
+            pm_error("Too many arguments.  The only possible argument is "
+                     "the input file name");
     }
 
-    pm_close( stdout );
-    exit( 0 );
-}
+    ReadATKRaster(ifP);
+
+    pm_close(ifP);
 
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/converter/pbm/brushtopbm.c b/converter/pbm/brushtopbm.c
index 0cffaa4d..ebd817be 100644
--- a/converter/pbm/brushtopbm.c
+++ b/converter/pbm/brushtopbm.c
@@ -1,4 +1,4 @@
-/* brushtopbm.c - read a doodle brush file and write a portable bitmap
+/* brushtopbm.c - read a doodle brush file and write a PBM image
 **
 ** Copyright (C) 1988 by Jef Poskanzer.
 **
@@ -12,96 +12,96 @@
 
 #include "pbm.h"
 
-static void getinit ARGS(( FILE* file, int* colsP, int* rowsP ));
-static bit getbit ARGS(( FILE* file ));
+#define HEADERSIZE 16   /* 16 is just a guess at the header size */
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int rows, cols, padright, row, col;
-
-
-    pbm_init( &argc, argv );
-
-    if ( argc > 2 )
-	pm_usage( "[brushfile]" );
-
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
-
-    getinit( ifp, &cols, &rows );
-
-    pbm_writepbminit( stdout, cols, rows, 0 );
-    bitrow = pbm_allocrow( cols );
-
-    /* Compute padding to round cols up to the next multiple of 16. */
-    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
-
-    for ( row = 0; row < rows; ++row )
-	{
-	/* Get data. */
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-	    *bP = getbit( ifp );
-	/* Discard line padding. */
-        for ( col = 0; col < padright; ++col )
-	    (void) getbit( ifp );
-	/* Write row. */
-	pbm_writepbmrow( stdout, bitrow, cols, 0 );
-	}
-
-    pm_close( ifp );
-    pm_close( stdout );
-    
-    exit( 0 );
-    }
 
 
-static int item, bitsperitem, bitshift;
+static void
+getinit(FILE *         const ifP,
+        unsigned int * const colsP,
+        unsigned int * const rowsP) {
+
+    unsigned char header[HEADERSIZE];
+    size_t bytesRead;
+
+    bytesRead = fread(header, sizeof(header), 1, ifP);
+    if (bytesRead !=1)
+        pm_error("Error reading header");   
+
+    if (header[0] != 1)
+        pm_error("bad magic number 1");
+    if (header[1] != 0)
+        pm_error("bad magic number 2");
+
+    *colsP =  (header[2] << 8) + header[3];  /* Max 65535 */
+    *rowsP =  (header[4] << 8) + header[5];  /* Max 65535 */
+}
+
+
 
 static void
-getinit( file, colsP, rowsP )
-    FILE* file;
-    int* colsP;
-    int* rowsP;
-    {
-    int i;
-
-    if ( getc( file ) != 1 )
-	pm_error( "bad magic number 1" );
-    if ( getc( file ) != 0 )
-	pm_error( "bad magic number 2" );
-    *colsP = getc( file ) << 8;
-    *colsP += getc( file );
-    *rowsP = getc( file ) << 8;
-    *rowsP += getc( file );
-    bitsperitem = 8;
-
-    /* Junk rest of header. */
-    for ( i = 0; i < 10; ++i )  /* 10 is just a guess at the header size */
-	(void) getc( file );
-    }
+validateEof(FILE * const ifP) {
+
+    int rc;
+    rc = getc(ifP);
+    if (rc != EOF)
+        pm_message("Extraneous data at end of file");
+}
+
+
+/*
+   The routine for converting the raster closely resembles the pbm
+   case of pnminvert.  Input is padded up to 16 bit border.
+   afu December 2013
+ */
+
+
 
-static bit
-getbit( file )
-    FILE* file;
-    {
-    bit b;
-
-    if ( bitsperitem == 8 )
-	{
-	item = getc( file );
-	bitsperitem = 0;
-	bitshift = 7;
-	}
-    ++bitsperitem;
-    b = ( ( item >> bitshift) & 1 ) ? PBM_WHITE : PBM_BLACK;
-    --bitshift;
-    return b;
+int
+main(int argc, const char ** argv)  {
+
+    FILE * ifP;
+    bit * bitrow;
+    unsigned int rows, cols, row;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 > 0) {
+        ifP = pm_openr(argv[1]);
+        if (argc-1 > 1)
+            pm_error("Too many arguments (%u).  "
+                     "The only argument is the brush file name.", argc-1);
+    } else
+        ifP = stdin;
+
+    getinit(ifP, &cols, &rows);
+
+    pbm_writepbminit(stdout, cols, rows, 0);
+
+    bitrow = pbm_allocrow_packed(cols + 16);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int const inRowBytes = ((cols + 15) / 16) * 2;
+        unsigned int i;
+        size_t bytesRead;
+
+        bytesRead = fread (bitrow, 1, inRowBytes, ifP); 
+        if (bytesRead != inRowBytes)
+            pm_error("Error reading a row of data from brushfile");
+
+        for (i = 0; i < inRowBytes; ++i)
+            bitrow[i] = ~bitrow[i];
+
+        /* Clean off remainder of fractional last character */
+        pbm_cleanrowend_packed(bitrow, cols);
+
+        pbm_writepbmrow_packed(stdout, bitrow, cols, 0);
     }
+
+    validateEof(ifP);
+
+    pm_close(ifP);
+    pm_close(stdout);
+    
+    return 0;
+}
diff --git a/converter/pbm/cistopbm.c b/converter/pbm/cistopbm.c
new file mode 100644
index 00000000..591e2aa5
--- /dev/null
+++ b/converter/pbm/cistopbm.c
@@ -0,0 +1,180 @@
+/*
+ *  cistopbm: Convert images in the CompuServe RLE format to PBM
+ *  Copyright (C) 2009  John Elliott
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pbm.h"
+
+
+static void syntax(const char *prog)
+{
+        pm_usage(" { options } { input }\n\n"
+                 "Input file should be in CompuServe RLE format.\n"
+                 "Output file will be in PBM format.\n"
+                 "Options:\n"
+                 "-i, --inverse: Reverse black and white.\n"
+                 "--:            End of options\n\n"
+"cistopbm v1.01, Copyright 2009 John Elliott <jce@seasip.demon.co.uk>\n"
+"This program is redistributable under the terms of the GNU General Public\n"
+"License, version 2 or later.\n"
+                 );
+}
+
+int main(int argc, const char **argv)
+{
+    FILE *ifP;
+    int c[3];
+    int inoptions = 1;
+    int n, x, y;
+    int bw = PBM_BLACK;     /* Default colouring is white on black */
+    const char *inpname = NULL;
+    int height, width;
+    bit **bits;
+
+    pm_proginit(&argc, argv);
+
+    for (n = 1; n < argc; n++)
+    {
+        if (!strcmp(argv[n], "--"))
+        {
+            inoptions = 0;
+            continue;
+        }
+        if (inoptions)
+        {
+            if (pm_keymatch(argv[n], "-h", 2) ||
+                pm_keymatch(argv[n], "-H", 2) ||
+                pm_keymatch(argv[n], "--help", 6))
+            {
+                syntax(argv[0]);
+                return EXIT_SUCCESS;
+            }
+            if (pm_keymatch(argv[n], "-i", 2) ||
+                pm_keymatch(argv[n], "-I", 2) ||
+                pm_keymatch(argv[n], "--inverse", 9))
+            {
+                bw ^= (PBM_WHITE ^ PBM_BLACK);
+                continue;
+            }
+            if (argv[n][0] == '-' && argv[n][1] != 0)
+            {
+                pm_message("Unknown option: %s", argv[n]);
+                syntax(argv[0]);
+                return EXIT_FAILURE;
+            }
+        }
+
+        if (inpname == NULL) inpname = argv[n];
+        else { syntax(argv[0]); return EXIT_FAILURE; }
+    }
+    if (inpname == NULL) inpname = "-";
+    ifP  = pm_openr(inpname);
+
+    /* There may be junk before the magic number. If so, skip it. */
+    x = 0;
+    c[0] = c[1] = c[2] = EOF;
+
+    /* Read until the array c[] holds the magic number. */
+    do
+    {
+        c[0] = c[1];
+        c[1] = c[2];
+        c[2] = fgetc(ifP);
+
+        /* If character read was EOF, end of file was reached and magic number
+         * not found.
+         */
+        if (c[2] == EOF)
+        {
+            pm_error("Input file is not in CompuServe RLE format");
+        }
+        ++x;
+    } while (c[0] != 0x1B || c[1] != 0x47);
+
+    /* x = number of bytes read. Should be 3 if signature is at the start */
+    if (x > 3)
+    {
+        pm_message("Warning: %d bytes of junk skipped before image",
+                   x - 3);
+    }
+    /* Parse the resolution */
+    switch(c[2])
+    {
+    case 0x48:      height = 192; width = 256; break;
+    case 0x4D:      height =  96; width = 128; break;
+    default:        pm_error("Unknown resolution 0x%02x", c[2]);
+        break;
+    }
+    /* Convert the data */
+    bits = pbm_allocarray(width, height);
+    x = y = 0;
+    do
+    {
+        c[0] = fgetc(ifP);
+
+        /* Stop if we hit EOF or Escape */
+        if (c[0] == EOF)  break;        /* EOF */
+        if (c[0] == 0x1B) break;        /* End of graphics */
+        /* Other non-printing characters are ignored; some files contain a
+         * BEL
+         */
+        if (c[0] < 0x20)  continue;
+
+        /* Each character gives the number of pixels to draw in the appropriate
+         * colour.
+         */
+        for (n = 0x20; n < c[0]; n++)
+        {
+            if (x < width && y < height) bits[y][x] = bw;
+            x++;
+            /* Wrap at end of line */
+            if (x >= width)
+            {
+                x = 0;
+                y++;
+            }
+        }
+        /* And toggle colours */
+        bw ^= (PBM_WHITE ^ PBM_BLACK);
+    }
+    while (1);
+
+    /* See if the end-graphics signature (ESC G N) is present. */
+    c[1] = EOF;
+    if (c[0] == 0x1B)
+    {
+        c[1] = fgetc(ifP);
+        c[2] = fgetc(ifP);
+    }
+    if (c[0] != 0x1B || c[1] != 0x47 || c[2] != 0x4E)
+    {
+        pm_message("Warning: End-graphics signature not found");
+    }
+    /* See if we decoded the right number of pixels */
+    if (x != 0 || y != height)
+    {
+        pm_message("Warning: %d pixels found, should be %d",
+                   y * width + x, width * height);
+    }
+    pbm_writepbm(stdout, bits, width, height, 0);
+    pm_close(ifP);
+    return 0;       
+}
diff --git a/converter/pbm/cmuwm.h b/converter/pbm/cmuwm.h
deleted file mode 100644
index e667f25e..00000000
--- a/converter/pbm/cmuwm.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* cmuwm.h - definitions for the CMU window manager format
-*/
-
-#ifndef CMUWM_H_INCLUDED
-#define CMUWM_H_INCLUDED
-
-struct cmuwm_header
-    {
-    long magic;
-    long width;
-    long height;
-    short depth;
-    };
-
-#define CMUWM_MAGIC 0xf10040bbL
-
-#endif
diff --git a/converter/pbm/cmuwmtopbm.c b/converter/pbm/cmuwmtopbm.c
index eabff40c..ccf8cfc9 100644
--- a/converter/pbm/cmuwmtopbm.c
+++ b/converter/pbm/cmuwmtopbm.c
@@ -20,9 +20,17 @@
 
 
 #include "pbm.h"
-#include "cmuwm.h"
 
+/*--------------------------
+  CMUWN Header format:
 
+  32 bit const magic = 0xf10040bb ;
+  32 bit int   width;
+  32 bit int   height;
+  16 bit int   depth;
+
+  values are big-endian.
+--------------------------*/
 
 static void
 readCmuwmHeader(FILE *         const ifP,
@@ -32,6 +40,7 @@ readCmuwmHeader(FILE *         const ifP,
 
     const char * const initReadError =
         "CMU window manager header EOF / read error";
+    uint32_t const cmuwmMagic = 0xf10040bb;
 
     long l;
     short s;
@@ -39,20 +48,20 @@ readCmuwmHeader(FILE *         const ifP,
 
     rc = pm_readbiglong(ifP, &l);
     if (rc == -1 )
-        pm_error(initReadError);
-    if ((uint32_t)l != CMUWM_MAGIC)
+        pm_error("%s", initReadError);
+    if ((uint32_t)l != cmuwmMagic)
         pm_error("bad magic number in CMU window manager file");
     rc = pm_readbiglong(ifP, &l);
     if (rc == -1)
-        pm_error(initReadError);
+        pm_error("%s", initReadError);
     *colsP = l;
     rc = pm_readbiglong(ifP, &l);
     if (rc == -1 )
-        pm_error(initReadError);
+        pm_error("%s", initReadError);
     *rowsP = l;
     rc = pm_readbigshort(ifP, &s);
     if (rc == -1)
-        pm_error(initReadError);
+        pm_error("%s", initReadError);
     *depthP = s;
 }
 
diff --git a/converter/pbm/escp2topbm.c b/converter/pbm/escp2topbm.c
index 28296da9..632e6345 100644
--- a/converter/pbm/escp2topbm.c
+++ b/converter/pbm/escp2topbm.c
@@ -14,65 +14,266 @@
 ** copyright notice and this permission notice appear in supporting
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.
+
+** Major changes were made in July 2015 by Akira Urushibata.
+** Most notably the output functions were rewritten.
+** The -plain option is honored as in other programs.
+
+*   Implementation note (July 2015)
+*
+*   The input file does not have a global header.  Image data is divided
+*   into stripes (or data blocks).   Each stripe has a header with
+*   local values for width, height, horizontal/vertical resolution
+*   and compression mode.
+*
+*   We calculate the global height by adding up the local (stripe)
+*   height values, which may vary.
+*
+*   The width value in the first stripe is used throughout; if any
+*   subsequent stripe reports a different value we abort.
+*
+*   We ignore the resolution fields.  Changes in compression mode
+*   are tolerated; they pose no problem.
+*
+*   The official manual says resolution changes within an image are
+*   not allowed.  It does not mention whether changes in stripe height or
+*   width values should be allowed.
+*
+*   A different implementation approach would be to write temporary
+*   PBM files for each stripe and concatenate them at the final stage
+*   with a system call to "pnmcat -tb".  This method has the advantage
+*   of being capable of handling variations in width.
 */
 
-#include <string.h>
 
-#include "pbm.h"
+#include <stdbool.h>
+
 #include "mallocvar.h"
+#include "pbm.h"
+
+#define ESC 033
+
+
+
+static int
+huntEsc(FILE * const ifP) {
+/*-----------------------------------------------------------------------------
+  Hunt for valid start of image stripe in input.
+
+  Return values:
+    ESC: start of image stripe (data block)
+    EOF: end of file
+    0: any other char or sequence
+-----------------------------------------------------------------------------*/
+    int const ch1 = getc(ifP);
+
+    switch (ch1) {
+    case EOF: return EOF;
+    case ESC: {
+        int const ch2 = getc(ifP);
+
+        switch (ch2) {
+        case EOF: return EOF;
+        case '.': return ESC;
+        default:  return 0;
+        }
+    } break;
+    default: return 0;
+    }
+}
+
+
+
+static unsigned char
+readChar(FILE * const ifP) {
+
+    int const ch = getc(ifP);
+
+    if (ch == EOF)
+        pm_error("EOF encountered while reading image data.");
+
+    return (unsigned char) ch;
+}
+
+
+
+static void       
+readStripeHeader(unsigned int * const widthThisStripeP,
+                 unsigned int * const rowsThisStripeP,
+                 unsigned int * const compressionP,
+                 FILE *         const ifP) {
+
+    unsigned char stripeHeader[6];
+    unsigned int widthThisStripe, rowsThisStripe;
+    unsigned int compression;
+
+    if (fread(stripeHeader, sizeof(stripeHeader), 1, ifP) != 1)
+        pm_error("Error reading image data.");
+
+    compression     = stripeHeader[0];
+    /* verticalResolution   = stripeHeader[1]; */
+    /* horizontalResolution = stripeHeader[2]; */
+    rowsThisStripe  = stripeHeader[3];  
+    widthThisStripe = stripeHeader[5] * 256 + stripeHeader[4];
+
+    if (widthThisStripe == 0 || rowsThisStripe == 0)
+        pm_error("Error: Abnormal value in data block header:  "
+                 "Says stripe has zero width or height");
+
+    if (compression != 0 && compression != 1)
+        pm_error("Error: Unknown compression mode %u", compression);
+
+    *widthThisStripeP = widthThisStripe;
+    *rowsThisStripeP  = rowsThisStripe;
+    *compressionP     = compression;
+}
+
+
 
 /* RLE decoder */
-static unsigned int 
-dec_epson_rle(unsigned        const int k, 
-              unsigned        const char * in, 
-              unsigned char * const out) {
+static void
+decEpsonRLE(unsigned int    const blockSize, 
+            unsigned char * const outBuffer,
+            FILE *          const ifP) {
 
-    unsigned int i;
-    unsigned int pos;
     unsigned int dpos;
 
-    pos = 0;  /* initial value */
-    dpos = 0; /* initial value */
+    for (dpos = 0; dpos < blockSize; ) {
+        unsigned char const flag = readChar(ifP);
 
-    while (dpos < k) {
-        if (in[pos] < 128) {
-            for (i = 0; i < in[pos] + 1; ++i)
-                out[dpos+i] = in[pos + i + 1];     
+        if (flag < 128) {
             /* copy through */
-            pos += i + 1;
+
+            unsigned int const nonrunLength = flag + 1;
+
+            unsigned int i;
+
+            for (i = 0; i < nonrunLength; ++i)
+                outBuffer[dpos+i] = readChar(ifP);
+
+            dpos += nonrunLength;
+        } else if (flag == 128) {
+            pm_message("Code 128 detected in compressed input data: ignored");
         } else {
-            for (i = 0; i < 257 - in[pos]; ++i)
-                out[dpos + i] = in[pos + 1];  
             /* inflate this run */
-            pos += 2;
+
+            unsigned int const runLength = 257 - flag;
+            unsigned char const repeatChar = readChar( ifP );
+
+            unsigned int i;
+
+            for (i = 0; i < runLength; ++i)
+                outBuffer[dpos + i] = repeatChar;  
+            dpos += runLength;
         }
-        dpos += i;
     }
-    if(dpos > k)
-      pm_error("Corrupt compressed block"); 
-    return pos;        /* return number of treated input bytes */
+    if (dpos != blockSize)
+      pm_error("Corruption detected in compressed input data");
 }
 
 
 
-int
-main(int    argc,
-     char * argv[]) {
+static void
+processStripeRaster(unsigned char ** const bitarray,
+                    unsigned int     const rowsThisStripe,
+                    unsigned int     const width,
+                    unsigned int     const compression,
+                    FILE *           const ifP,
+                    unsigned int *   const rowIdxP) {
+         
+    unsigned int const initialRowIdx = *rowIdxP;
+    unsigned int const widthInBytes = pbm_packed_bytes(width);
+    unsigned int const blockSize = rowsThisStripe * widthInBytes;
+    unsigned int const margin = compression==1 ? 256 : 0;
+
+    unsigned char * imageBuffer;
+    unsigned int i;
+    unsigned int rowIdx;
 
-    unsigned int const size = 4096; /* arbitrary value */
+    /* We allocate a new buffer each time this function is called.  Add some
+       margin to the buffer for compressed mode to cope with overruns caused
+       by corrupt input data.
+    */
+
+    MALLOCARRAY(imageBuffer, blockSize + margin);
+
+    if (imageBuffer == NULL)
+        pm_error("Failed to allocate buffer for a block of size %u",
+                 blockSize);
+
+    if (compression == 0) {
+        if (fread(imageBuffer, blockSize, 1, ifP) != 1)
+            pm_error("Error reading image data");
+    } else /* compression == 1 */
+        decEpsonRLE(blockSize, imageBuffer, ifP);
+
+    /* Hand over image data to output by pointer assignment */
+    for (i = 0, rowIdx = initialRowIdx; i < rowsThisStripe; ++i)
+        bitarray[rowIdx++] = &imageBuffer[i * widthInBytes];
+
+    *rowIdxP = rowIdx;
+}
 
-    FILE *ifP;
-    unsigned int i, len, pos, opos, width, height;
-    unsigned char *input, *output;
-    const char * fileName;
 
-    pbm_init(&argc, argv);
 
-    MALLOCARRAY(input, size);
-    MALLOCARRAY(output, size);
-    
-    if (input == NULL || output == NULL)
-        pm_error("Cannot allocate memory");
+static void
+expandBitarray(unsigned char *** const bitarrayP,
+               unsigned int   *  const bitarraySizeP) {
+
+    unsigned int const heightIncrement = 5120;
+    unsigned int const heightMax = 5120 * 200;
+        /* 5120 rows is sufficient for US legal at 360 DPI */
+
+    *bitarraySizeP += heightIncrement;
+    if (*bitarraySizeP > heightMax)
+        pm_error("Image too tall");
+    else
+        REALLOCARRAY_NOFAIL(*bitarrayP, *bitarraySizeP); 
+}
+
+
+
+static void
+writePbmImage(unsigned char ** const bitarray,
+              unsigned int     const width,
+              unsigned int     const height) {
+
+    unsigned int row;
+
+    if (height == 0)
+        pm_error("No image");
+
+    pbm_writepbminit(stdout, width, height, 0);
+ 
+    for (row = 0; row < height; ++row) {
+        pbm_cleanrowend_packed(bitarray[row], width);
+        pbm_writepbmrow_packed(stdout, bitarray[row], width, 0);
+    }
+}
+
+
+
+int
+main(int          argc,
+     const char * argv[]) {
+
+    FILE * ifP;
+    unsigned int width;
+        /* Width of the image, or zero to mean width is not yet known.
+           (We get the width from the first stripe in the input; until
+           we've seen that stripe, we don't know the width)
+        */
+    unsigned int height;
+        /* Height of the image as seen so far.  (We process a stripe at a
+           time, increasing this value as we go).
+        */
+    unsigned int rowIdx;
+    unsigned char ** bitarray;
+    unsigned int bitarraySize;
+    const char * fileName;
+    bool eof;
+
+    pm_proginit(&argc, argv);
 
     if (argc-1 > 1)
         pm_error("Too many arguments (%u).  Only argument is filename.",
@@ -85,69 +286,64 @@ main(int    argc,
 
     ifP = pm_openr(fileName);
 
-    /* read the whole file */
-    len = 0;  /* initial value */
-    for (i = 0; !feof(ifP); ++i) {
-        size_t bytesRead;
-        REALLOCARRAY(input, (i+1) * size);
-        if (input == NULL)
-            pm_error("Cannot allocate memory");
-        bytesRead = fread(input + i * size, 1, size, ifP);
-        len += bytesRead;
-    }
+    /* Initialize bitarray */
+    bitarray = NULL;  bitarraySize = 0;
+    expandBitarray(&bitarray, &bitarraySize);
 
-    /* filter out raster data */
-    height = 0;  /* initial value */
-    width  = 0;  /* initial value */
-    pos = 0;     /* initial value */
-    opos = 0;    /* initial value */
-
-    while (pos < len) {
-        /* only ESC sequences are regarded  */
-        if (input[pos] == '\x1b' && input[pos+1] == '.') {
-            unsigned int const k =
-                input[pos+5] * ((input[pos+7] * 256 + input[pos+6] + 7) / 8);
-            unsigned int const margin = 256;
-            if(input[pos+5] == 0)
-                pm_error("Abnormal height value in escape sequence");
-            height += input[pos+5];
-            if(width == 0) /* initialize */
-                width = input[pos+7] * 256 + input[pos+6];
-            else if(width != input[pos+7] * 256 + input[pos+6])
-                pm_error("Abnormal width value in escape sequence");
-
-            REALLOCARRAY(output, opos + k + margin);
-            if (output == NULL)
-                pm_error("Cannot allocate memory");
-
-            switch (input[pos+2]) {
-            case 0:
-                /* copy the data block */
-                memcpy(output + opos, input + pos + 8, k);        
-                pos += k + 8;
-                opos += k;
-                break;
-            case 1: {
-                /* inflate the data block */
-                unsigned int l;
-                l = dec_epson_rle(k,input+pos+8,output+opos);  
-                pos += l + 8;
-                opos += k;
-            }
-                break;
-            default:
-                pm_error("unknown compression mode");
-                break;
+    for (eof = false, width = 0, height = 0, rowIdx = 0; !eof; ) {
+        int const r = huntEsc(ifP);
+
+        if (r == EOF)
+            eof = true;
+        else {
+            if (r == ESC) {
+                unsigned int compression;
+                unsigned int rowsThisStripe;
+                unsigned int widthThisStripe;
+            
+                readStripeHeader(&widthThisStripe, &rowsThisStripe,
+                                 &compression, ifP);
+
+                if (rowsThisStripe == 0)
+                    pm_error("Abnormal data block height value: 0");
+                else if (rowsThisStripe != 24 && rowsThisStripe != 8 &&
+                         rowsThisStripe != 1) {
+                    /* The official Epson manual says valid values are 1, 8,
+                       24 but we just print a warning message and continue if
+                       other values are detected.
+                    */ 
+                    pm_message("Abnormal data block height value: %u "
+                               "(ignoring)",
+                               rowsThisStripe);
+                }
+                if (width == 0) {
+                    /* Get width from 1st stripe header */
+                    width = widthThisStripe;
+                } else if (width != widthThisStripe) {
+                    /* width change not allowed */
+                    pm_error("Error: Width changed in middle of image "
+                             "from %u to %u",
+                             width, widthThisStripe);
+                }
+                height += rowsThisStripe;
+                if (height > bitarraySize)
+                    expandBitarray(&bitarray, &bitarraySize);
+
+                processStripeRaster(bitarray, rowsThisStripe, width,
+                                    compression, ifP, &rowIdx);
+            } else {
+                /* r != ESC; do nothing */
             }
         }
-        else
-            ++pos;      /* skip bytes outside the ESCX sequence */
     }
 
-    pbm_writepbminit(stdout, width, height, 0);
-    fwrite(output, opos, 1, stdout);
-    free(input); free(output);
-    fclose(stdout); fclose(ifP);
+    writePbmImage(bitarray, width, height);
+
+    fclose(stdout);
+    fclose(ifP);
 
     return 0;
 }
+
+
+
diff --git a/converter/pbm/g3topbm.c b/converter/pbm/g3topbm.c
index 54f1f577..fcac1981 100644
--- a/converter/pbm/g3topbm.c
+++ b/converter/pbm/g3topbm.c
@@ -97,7 +97,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (widthSpec && paper_sizeSpec)
@@ -169,7 +169,7 @@ readBit(struct bitStream * const bitStreamP,
     if ((bitStreamP->shbit & 0xff) == 0) {
         bitStreamP->shdata = getc(bitStreamP->fileP);
         if (bitStreamP->shdata == EOF)
-            asprintfN(errorP, "EOF or error reading file");
+            pm_asprintf(errorP, "EOF or error reading file");
         else {
             bitStreamP->shbit = 0x80;
             if ( bitStreamP->reversebits )
@@ -195,6 +195,8 @@ readBitAndDetectEol(struct bitStream * const bitStreamP,
 /*----------------------------------------------------------------------------
    Same as readBit(), but iff the bit read is the final bit of an EOL
    mark, return *eolP == TRUE.
+
+   An EOL mark is 11 zero bits followed by a one.
 -----------------------------------------------------------------------------*/
     readBit(bitStreamP, bitP, errorP);
     if (!*errorP) {
@@ -265,19 +267,19 @@ addtohash(g3TableEntry *     hash[],
 
 
 
-static g3TableEntry*
+static g3TableEntry *
 hashfind(g3TableEntry *       hash[], 
-         int          const length, 
-         int          const code, 
-         int          const a, 
-         int          const b) {
+         int            const length, 
+         int            const code, 
+         int            const a, 
+         int            const b) {
 
     unsigned int pos;
     g3TableEntry * te;
 
     pos = ((length + a) * (code + b)) % HASHSIZE;
     te = hash[pos];
-    return ((te && te->length == length && te->code == code) ? te : 0);
+    return ((te && te->length == length && te->code == code) ? te : NULL);
 }
 
 
@@ -316,7 +318,13 @@ static g3TableEntry *
 g3code(unsigned int const curcode,
        unsigned int const curlen,
        bit          const color) {
+/*----------------------------------------------------------------------------
+   Return the position in the code tables mtable and ttable of the
+   G3 code which is the 'curlen' bits long with value 'curcode'.
 
+   Note that it is the _position_ in the table that determines the meaning
+   of the code.  The contents of the table entry do not.
+-----------------------------------------------------------------------------*/
     g3TableEntry * retval;
 
     switch (color) {
@@ -379,7 +387,11 @@ processG3Code(const g3TableEntry * const teP,
               unsigned int *       const colP,
               bit *                const colorP,
               unsigned int *       const countP) {
-              
+/*----------------------------------------------------------------------------
+   'teP' is a pointer into the mtable/ttable.  Note that the thing it points
+   to is irrelevant to us; it is only the position in the table that
+   matters.
+-----------------------------------------------------------------------------*/
     enum g3tableId const teId =
         (teP > mtable ? 2 : 0) + (teP - ttable) % 2;
 
@@ -395,17 +407,17 @@ processG3Code(const g3TableEntry * const teP,
     switch (teId) {
     case TERMWHITE:
     case TERMBLACK: {
-        unsigned int runLengthSoFar;
+        unsigned int totalRunLength;
         unsigned int col;
         
         col = *colP;
-        runLengthSoFar = MIN(*countP + teCount, MAXCOLS - col);
+        totalRunLength = MIN(*countP + teCount, MAXCOLS - col);
 
-        if (runLengthSoFar > 0) {
+        if (totalRunLength > 0) {
             if (*colorP == PBM_BLACK)
-                writeBlackBitSpan(packedBitrow, runLengthSoFar, col);
+                writeBlackBitSpan(packedBitrow, totalRunLength, col);
             /* else : Row was initialized to white, so we just skip */
-            col += runLengthSoFar;
+            col += totalRunLength;
         }
         *colorP = !*colorP;
         *countP = 0;
@@ -428,12 +440,12 @@ formatBadCodeException(const char ** const exceptionP,
                        unsigned int  const curlen,
                        unsigned int  const curcode) {
 
-    asprintfN(exceptionP,
-        "bad code word at Column %u.  "
-        "No prefix of the %u bits 0x%x matches any recognized "
-        "code word and no code words longer than 13 bits are "
-        "defined.  ",
-        col, curlen, curcode);
+    pm_asprintf(exceptionP,
+                "bad code word at Column %u.  "
+                "No prefix of the %u bits 0x%x matches any recognized "
+                "code word and no code words longer than 13 bits are "
+                "defined.  ",
+                col, curlen, curcode);
 }
 
 
@@ -485,8 +497,8 @@ readFaxRow(struct bitStream * const bitStreamP,
 
     while (!done) {
         if (col >= MAXCOLS) {
-            asprintfN(exceptionP, "Line is too long for this program to "
-                      "handle -- longer than %u columns", MAXCOLS);
+            pm_asprintf(exceptionP, "Line is too long for this program to "
+                        "handle -- longer than %u columns", MAXCOLS);
             done = TRUE;
         } else {
             unsigned int bit;
@@ -507,7 +519,7 @@ readFaxRow(struct bitStream * const bitStreamP,
                 done = TRUE;
             else {
                 curcode = (curcode << 1) | bit;
-                curlen++;
+                ++curlen;
             
                 if (curlen > 13) {
                     formatBadCodeException(exceptionP, col, curlen, curcode);
@@ -516,7 +528,8 @@ readFaxRow(struct bitStream * const bitStreamP,
                     const g3TableEntry * const teP =
                         g3code(curcode, curlen, currentColor);
                         /* Address of structure that describes the 
-                           current G3 code
+                           current G3 code.  Null means 'curcode' isn't
+                           a G3 code yet (probably just the beginning of one)
                         */
                     if (teP) {
                         processG3Code(teP, packedBitrow,
@@ -570,7 +583,7 @@ handleRowException(const char * const exception,
                        row, exception);
         else
             pm_error("Problem reading Row %u.  Aborting.  %s", row, exception);
-        strfree(exception);
+        pm_strfree(exception);
     }
 
     if (error) {
@@ -579,7 +592,7 @@ handleRowException(const char * const exception,
                        row, error);
         else
             pm_error("Unable to read Row %u.  Aborting.  %s", row, error);
-        strfree(error);
+        pm_strfree(error);
     }
 }
 
@@ -626,16 +639,16 @@ analyzeLineSize(lineSizeAnalyzer * const analyzerP,
 
     if (analyzerP->expectedLineSize &&
         thisLineSize != analyzerP->expectedLineSize)
-        asprintfN(&error, "Image contains a line of %u pixels.  "
-                  "You specified lines should be %u pixels.",
-                  thisLineSize, analyzerP->expectedLineSize);
+        pm_asprintf(&error, "Image contains a line of %u pixels.  "
+                    "You specified lines should be %u pixels.",
+                    thisLineSize, analyzerP->expectedLineSize);
     else {
         if (analyzerP->maxLineSize && thisLineSize != analyzerP->maxLineSize)
-            asprintfN(&error, "There are at least two different "
-                      "line lengths in this image, "
-                      "%u pixels and %u pixels.  "
-                      "This is a violation of the G3 standard.  ",
-                      thisLineSize, analyzerP->maxLineSize);
+            pm_asprintf(&error, "There are at least two different "
+                        "line lengths in this image, "
+                        "%u pixels and %u pixels.  "
+                        "This is a violation of the G3 standard.  ",
+                        thisLineSize, analyzerP->maxLineSize);
         else
             error = NULL;
     }
@@ -649,7 +662,7 @@ analyzeLineSize(lineSizeAnalyzer * const analyzerP,
         } else
             pm_error("%s", error);
 
-        strfree(error);
+        pm_strfree(error);
     }
     analyzerP->maxLineSize = MAX(thisLineSize, analyzerP->maxLineSize);
 }
@@ -693,8 +706,8 @@ readFax(struct bitStream * const bitStreamP,
         unsigned int lineSize;
 
         if (row >= MAXROWS)
-            asprintfN(&error, "Image is too tall.  This program can "
-                      "handle at most %u rows", MAXROWS);
+            pm_asprintf(&error, "Image is too tall.  This program can "
+                        "handle at most %u rows", MAXROWS);
         else {
             const char * exception;
 
@@ -714,9 +727,9 @@ readFax(struct bitStream * const bitStreamP,
                     if (stretch) {
                         ++row;
                         if (row >= MAXROWS)
-                            asprintfN(&error, "Image is too tall.  This "
-                                      "program can handle at most %u rows "
-                                      "after stretching", MAXROWS);
+                            pm_asprintf(&error, "Image is too tall.  This "
+                                        "program can handle at most %u rows "
+                                        "after stretching", MAXROWS);
                         else
                             packedBits[row] = packedBits[row-1];
                     }
diff --git a/converter/pbm/icontopbm.c b/converter/pbm/icontopbm.c
deleted file mode 100644
index a0d1bd2b..00000000
--- a/converter/pbm/icontopbm.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/* icontopbm.c - read a Sun icon file and produce a portable bitmap
-**
-** Copyright (C) 1988 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.
-*/
-
-#include <string.h>
-
-#include "nstring.h"
-#include "pbm.h"
-
-/* size in bytes of a bitmap */
-#define BitmapSize(width, height) (((((width) + 15) >> 3) &~ 1) * (height))
-
-static void
-ReadIconFile(FILE *                const file, 
-             int *                 const widthP, 
-             int *                 const heightP, 
-             short unsigned int ** const dataP) {
-
-    char variable[80+1];
-    int ch;
-    int status, value, i, data_length, gotsome;
-
-    gotsome = 0;
-    *widthP = *heightP = -1;
-    for ( ; ; )
-    {
-        while ( ( ch = getc( file ) ) == ',' || ch == '\n' || ch == '\t' ||
-                ch == ' ' )
-            ;
-        for ( i = 0;
-              ch != '=' && ch != ',' && ch != '\n' && ch != '\t' && 
-                  ch != ' ' && (i < (sizeof(variable) - 1));
-              i++ )
-        {
-            variable[i] = ch;
-            if ((ch = getc( file )) == EOF)
-                pm_error( "invalid input file -- premature EOF" );
-        }
-        variable[i] = '\0';
-
-        if ( streq( variable, "*/" )&& gotsome )
-            break;
-
-        if ( fscanf( file, "%d", &value ) != 1 )
-            continue;
-
-        if ( streq( variable, "Width" ) )
-        {
-            *widthP = value;
-            gotsome = 1;
-        }
-        else if ( streq( variable, "Height" ) )
-        {
-            *heightP = value;
-            gotsome = 1;
-        }
-        else if ( streq( variable, "Depth" )  )
-        {
-            if ( value != 1 )
-                pm_error( "invalid depth" );
-            gotsome = 1;
-        }
-        else if ( streq( variable, "Format_version" ) )
-        {
-            if ( value != 1 )
-                pm_error( "invalid Format_version" );
-            gotsome = 1;
-        }
-        else if ( streq( variable, "Valid_bits_per_item" ) )
-        {
-            if ( value != 16 )
-                pm_error( "invalid Valid_bits_per_item" );
-            gotsome = 1;
-        }
-    }
-
-    if ( *widthP <= 0 )
-        pm_error( "invalid width (must be positive): %d", *widthP );
-    if ( *heightP <= 0 )
-        pm_error( "invalid height (must be positive): %d", *heightP );
-
-    data_length = BitmapSize( *widthP, *heightP );
-    *dataP = (short unsigned int *) malloc( data_length );
-    if ( *dataP == NULL )
-        pm_error( "out of memory" );
-    data_length /= sizeof( short );
-    
-    for ( i = 0 ; i < data_length; i++ )
-    {
-        if ( i == 0 )
-            status = fscanf( file, " 0x%4hx", *dataP );
-        else
-            status = fscanf( file, ", 0x%4hx", *dataP + i );
-        if ( status != 1 )
-            pm_error( "error 4 scanning bits item" );
-    }
-}
-
-
-
-int
-main(int  argc, char ** argv) {
-
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int rows, cols, row, col, shortcount, mask;
-    short unsigned int * data;
-
-
-    pbm_init( &argc, argv );
-
-    if ( argc > 2 )
-        pm_usage( "[iconfile]" );
-
-    if ( argc == 2 )
-        ifp = pm_openr( argv[1] );
-    else
-        ifp = stdin;
-
-    ReadIconFile( ifp, &cols, &rows, &data );
-
-    pm_close( ifp );
-
-    pbm_writepbminit( stdout, cols, rows, 0 );
-    bitrow = pbm_allocrow( cols );
-
-    for ( row = 0; row < rows; row++ )
-    {
-        shortcount = 0;
-        mask = 0x8000;
-        for ( col = 0, bP = bitrow; col < cols; col++, bP++ )
-        {
-            if ( shortcount >= 16 )
-            {
-                data++;
-                shortcount = 0;
-                mask = 0x8000;
-            }
-            *bP = ( *data & mask ) ? PBM_BLACK : PBM_WHITE;
-            shortcount++;
-            mask = mask >> 1;
-        }
-        data++;
-        pbm_writepbmrow( stdout, bitrow, cols, 0 );
-    }
-
-    pm_close( stdout );
-    exit( 0 );
-}
-
diff --git a/converter/pbm/macp.h b/converter/pbm/macp.h
index 26a720a2..d00dc5c9 100644
--- a/converter/pbm/macp.h
+++ b/converter/pbm/macp.h
@@ -1,12 +1,16 @@
-/* macp.h - header file for MacPaint files
-*/
-
+/*=============================================================================
+                               macp.h
+===============================================================================
+  Information about MacPaint files
+=============================================================================*/
 #ifndef MACP_H_INCLUDED
 #define MACP_H_INCLUDED
 
-#define	HEADER_LENGTH	512
-#define	MAX_LINES	720
-#define	BYTES_WIDE	72
-#define MAX_COLS	576	/* = BYTES_WIDE * 8 */
+#define	MACBIN_HEAD_LEN	128
+#define	MACP_HEAD_LEN	512
+#define	MACP_ROWS	720
+#define	MACP_COLCHARS	72
+#define MACP_COLS	((MACP_COLCHARS) * 8)
+#define MACP_BYTES	((MACP_COLCHARS) * (MACP_ROWS))
 
 #endif
diff --git a/converter/pbm/macptopbm.c b/converter/pbm/macptopbm.c
index f4a341d3..db628b6c 100644
--- a/converter/pbm/macptopbm.c
+++ b/converter/pbm/macptopbm.c
@@ -1,6 +1,8 @@
 /* macptopbm.c - read a MacPaint file and produce a portable bitmap
 **
 ** Copyright (C) 1988 by Jef Poskanzer.
+** Some code of ReadMacPaintFile() is based on the work of
+** Patrick J. Naughton.  (C) 1987, All Rights Reserved.
 **
 ** Permission to use, copy, modify, and distribute this software and its
 ** documentation for any purpose and without fee is hereby granted, provided
@@ -8,133 +10,347 @@
 ** copyright notice and this permission notice appear in supporting
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.
+
+
+** Apr 2015 afu
+** Changed code style (ANSI-style function definitions, etc.)
+** Added automatic detection of MacBinary header.
+** Added diagnostics for corruptions.
+** Replaced byte-wise operations with bit-wise ones.
 */
 
 #include "pbm.h"
+#include "pm_c_util.h"
 #include "macp.h"
 
-static void ReadMacPaintFile ARGS(( FILE* file, int extraskip, int* scanLineP, unsigned char Pic[MAX_LINES][BYTES_WIDE] ));
 
-static unsigned char Pic[MAX_LINES][BYTES_WIDE];
+
+static bool
+validateMacPaintVersion( const unsigned char * const rBuff,
+                         const int offset ) {
+/*---------------------------------------------------------------------------
+  Macpaint (or PNTG) files have two headers.
+  The 512 byte MacPaint header is mandatory.
+  The newer 128 byte MacBinary header is optional.  If it exists, it comes
+  before the MacPaint header.
+
+  Here we examine the first four bytes of the MacPaint header to get
+  the version number.
+
+  Valid version numbers are 0, 2, 3.
+  We also allow 1.
+-----------------------------------------------------------------------------*/
+
+    bool retval;
+    const unsigned char * const vNum = rBuff + offset;
+
+   if ( ( ( vNum[0] | vNum[1] | vNum[2] ) != 0x00 ) || vNum[3] > 3 )
+        retval = FALSE;
+    else
+        retval = TRUE;
+
+    pm_message("MacPaint version (at offset %u): %02x %02x %02x %02x (%s)",
+               offset, vNum[0], vNum[1], vNum[2], vNum[3],
+               retval == TRUE ? "valid" : "not valid" );
+
+    return( retval );
+}
+
+
+
+static bool
+scanMacBinaryHeader( const unsigned char * rBuff ) {
+/*----------------------------------------------------------------------------
+  We check byte 0 and 1, and then the MacPaint header version assuming it
+  starts at offset 128.
+
+  Byte 0: must be 0x00.
+  Byte 1: (filename length) must be 1-63.
+
+  Other fields that may be of interest:
+
+  Bytes 2 through 63: (Internal Filename)
+    See Apple Charmap for valid characters.
+    Unlike US-Ascii, 8-bit characters (range 0x80 - 0xFF) are valid.
+    0x00-0x1F and 0x7F are control characters.  0x00 appears in some files.
+    Colon ':' (0x3a) should be avoided in Mac environments but in practice
+    does appear.
+
+  Bytes 65 through 68: (File Type)
+    Four Ascii characters.  Should be "PNTG".
+
+  Bytes 82 to 85: (SizeOfDataFork)
+    uint32 value.  It seems this is file size (in bytes) / 256 + N, N <= 4.
+
+  Bytes 100 through 124:
+    Should be all zero if the header is MacBinary I.
+    Defined and used in MacBinary II.
+
+  Bytes 124,125: CRC
+    (MacBinary II only) CRC value of bytes 0 through 123.
+
+  All multi-byte values are big-endian.
+
+  Reference:
+  http://www.fileformat.info/format/macpaint/egff.htm
+  Fully describes the fields.  However, the detection method described
+  does not work very well.
+
+  Also see:
+  http://fileformats.archiveteam.org/wiki/MacPaint
+-----------------------------------------------------------------------------*/
+    bool          foundMacBinaryHeader;
+
+    /* Examine byte 0.  It should be 0x00.  Note that the first
+       byte of a valid MacPaint header should also be 0x00.
+    */
+    if ( rBuff[0] != 0x00 ) {
+        foundMacBinaryHeader = FALSE;
+    }
+
+    /* Examine byte 1, the length of the filename.
+       It should be in the range 1 - 63.
+    */
+    else if( rBuff[1] == 0 || rBuff[1] > 63 ) {
+        foundMacBinaryHeader = FALSE;
+    }
+
+    /* Check the MacPaint header version starting at offset 128. */
+    else if ( validateMacPaintVersion ( rBuff, MACBIN_HEAD_LEN ) == FALSE) {
+        foundMacBinaryHeader = FALSE;
+    }
+    else
+        foundMacBinaryHeader = TRUE;
+
+    if( foundMacBinaryHeader == TRUE)
+      pm_message("Input file contains a MacBinary header "
+                   "followed by a MacPaint header.");
+    else
+      pm_message("Input file does not start with a MacBinary header.");
+
+    return ( foundMacBinaryHeader );
+}
+
+
+
+
+static void
+skipHeader( FILE * const ifP ) {
+/*--------------------------------------------------------------------------
+  Determine whether the MacBinary header exists.
+  If it does, read off the initial 640 (=128 + 512) bytes of the file.
+  If it doesn't, read off 512 bytes.
+
+  In the latter case we check the MacHeader version number, but just issue
+  a warning if the value is invalid.  This is for backward comaptibility.
+---------------------------------------------------------------------------*/
+    unsigned int re;
+    const unsigned int buffsize = MAX( MACBIN_HEAD_LEN, MACP_HEAD_LEN );
+    unsigned char * const rBuff = malloc(buffsize);
+
+    if( rBuff == NULL )
+        pm_error("Out of memory.");
+
+    /* Read 512 bytes.
+       See if MacBinary header exists in the first 128 bytes and
+       the next 4 bytes signal the start of a MacPaint header. */
+    re = fread ( rBuff, MACP_HEAD_LEN, 1, ifP);
+        if (re < 1)
+        pm_error("EOF/error while reading header.");
+
+    if ( scanMacBinaryHeader( rBuff ) == TRUE ) {
+    /* MacBinary header found.  Read another 128 bytes to complete the
+       MacPaint header, but don't conduct any further analysis. */
+        re = fread ( rBuff, MACBIN_HEAD_LEN, 1, ifP);
+            if (re < 1)
+            pm_error("EOF/error while reading MacPaint header.");
+
+    } else {
+    /* MacBinary header not found.  We assume file starts with
+       MacPaint header.   Check MacPaint version but dismiss error. */
+        if (validateMacPaintVersion( rBuff, 0 ) == TRUE)
+          pm_message("Input file starts with valid MacPaint header.");
+        else
+          pm_message("  - Ignoring invalid version number.");
+    }
+    free( rBuff );
+}
+
+
+
+static void
+skipExtraBytes( FILE * const ifP,
+                int    const extraskip) {
+/*--------------------------------------------------------------------------
+  This function exists for backward compatibility.  Its purpose is to
+  manually delete the MacBinary header.
+
+  We check the MacHeader version number, but just issue a warning if the
+  value is invalid.
+---------------------------------------------------------------------------*/
+    unsigned int re;
+    unsigned char * const rBuff = malloc(MAX (extraskip, MACP_HEAD_LEN));
+
+    if( rBuff == NULL )
+        pm_error("Out of memory.");
+
+    re = fread ( rBuff, 1, extraskip, ifP);
+        if (re < extraskip)
+        pm_error("EOF/error while reading off initial %u bytes"
+                     "specified by -extraskip.", extraskip);
+    re = fread ( rBuff, MACP_HEAD_LEN, 1, ifP);
+        if (re < 1)
+        pm_error("EOF/error while reading MacPaint header.");
+
+    /* Check the MacPaint version number.  Dismiss error. */
+    if (validateMacPaintVersion( rBuff, 0 ) == TRUE)
+        pm_message("Input file starts with valid MacPaint header.");
+    else
+        pm_message("  - Ignoring invalid version number.");
+
+    free( rBuff );
+}
+
+
+
+static unsigned char
+readChar( FILE * const ifP ) {
+
+    int const ch = getc( ifP );
+
+    if (ch ==EOF)
+        pm_error("EOF encountered while unpacking image data.");
+
+    /* else */
+        return ((unsigned char) ch);
+}
+
+
+
+
+static void
+ReadMacPaintFile( FILE *  const ifP,
+                  int  * outOfSyncP,
+                  int  * pixelCntP ) {
+/*---------------------------------------------------------------------------
+  Unpack image data.  Compression method is called "Packbits".
+  This run-length encoding scheme has also been adopted by
+  Postscript and TIFF.  See source: converter/other/pnmtops.c
+
+  Unpacked raster array is raw PBM.  No conversion is required.
+
+  One source says flag byte should not be 0xFF (255), but we don't reject
+  the value, for in practice, it is widely used.
+
+  Sequences should never cross row borders.
+  Violations of this rule are recorded in outOfSync.
+
+  Note that pixelCnt counts bytes, not bits, so it is the number of pixels
+  multiplied by 8.  This counter exists to detect corruptions.
+---------------------------------------------------------------------------*/
+    int           pixelCnt   = 0;   /* Initial value */
+    int           outOfSync  = 0;   /* Initial value */
+    unsigned int  flag;             /* Read from input */
+    unsigned int  i;
+    unsigned char * const bitrow = pbm_allocrow_packed(MACP_COLS);
+
+    while ( pixelCnt < MACP_BYTES ) {
+        flag = (unsigned int) readChar( ifP );    /* Flag (count) byte */
+        if ( flag < 0x80 ) {
+            /* Unpack next (flag + 1) chars as is */
+            for ( i = 0; i <= flag; i++ )
+                if( pixelCnt < MACP_BYTES) {
+                  int const colChar = pixelCnt % MACP_COLCHARS;
+                  pixelCnt++;
+                  bitrow[colChar] = readChar( ifP );
+                  if (colChar == MACP_COLCHARS-1)
+                      pbm_writepbmrow_packed( stdout, bitrow, MACP_COLS, 0 );
+                  if (colChar == 0 && i > 0 )
+                      outOfSync++;
+                }
+        }
+        else {
+          /* Repeat next char (2's complement of flagCnt) times */
+            unsigned int  const flagCnt = 256 - flag;
+            unsigned char const ch = readChar( ifP );
+            for ( i = 0; i <= flagCnt; i++ )
+                if( pixelCnt < MACP_BYTES) {
+                  int const colChar = pixelCnt % MACP_COLCHARS;
+                  pixelCnt++;
+                  bitrow[colChar] = ch;
+                  if (colChar == MACP_COLCHARS-1)
+                      pbm_writepbmrow_packed( stdout, bitrow, MACP_COLS, 0 );
+                  if (colChar == 0 && i > 0 )
+                      outOfSync++;
+                }
+        }
+    }
+    pbm_freerow_packed ( bitrow );
+    *outOfSyncP  = outOfSync;
+    *pixelCntP   = pixelCnt;
+}
+
 
 int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    int argn, extraskip, scanLine, rows, cols, row, bcol, i;
-    const char* usage = "[-extraskip N] [macpfile]";
+main( int argc, char * argv[])  {
 
+    FILE * ifp;
+    int argn, extraskip;
+    const char * const usage = "[-extraskip N] [macpfile]";
+    int outOfSync;
+    int pixelCnt;
 
     pbm_init( &argc, argv );
 
-    argn = 1;
-    extraskip = 0;
+    argn = 1;      /* initial value */
+    extraskip = 0; /* initial value */
 
     /* Check for flags. */
-    if ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-	{
-	if ( pm_keymatch( argv[argn], "-extraskip", 2 ) )
-	    {
-	    argn++;
-	    if ( argn == argc || sscanf( argv[argn], "%d", &extraskip ) != 1 )
-		pm_usage( usage );
-	    }
-	else
-	    pm_usage( usage );
-	argn++;
-	}
-
-    if ( argn < argc )
-	{
-	ifp = pm_openr( argv[argn] );
-	argn++;
-	}
+    if ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) {
+        if ( pm_keymatch( argv[argn], "-extraskip", 2 ) ) {
+            argn++;
+            if ( argn == argc || sscanf( argv[argn], "%d", &extraskip ) != 1 )
+                pm_usage( usage );
+        }
+        else
+            pm_usage( usage );
+        argn++;
+    }
+
+    if ( argn < argc ) {
+        ifp = pm_openr( argv[argn] );
+        argn++;
+        }
     else
-	ifp = stdin;
+        ifp = stdin;
 
     if ( argn != argc )
-	pm_usage( usage );
+        pm_usage( usage );
 
-    ReadMacPaintFile( ifp, extraskip, &scanLine, Pic );
+    if ( extraskip > 256 * 1024 )
+        pm_error("-extraskip value too large");
+    else if ( extraskip > 0 )
+        skipExtraBytes( ifp, extraskip);
+    else
+        skipHeader( ifp );
 
+    pbm_writepbminit( stdout, MACP_COLS, MACP_ROWS, 0 );
+
+    ReadMacPaintFile( ifp, &outOfSync, &pixelCnt );
+    /* We may not be at EOF.
+       Macpaint files often have extra bytes after image data. */
     pm_close( ifp );
 
-    cols = BYTES_WIDE * 8;
-    rows = scanLine;
-    pbm_writepbminit( stdout, cols, rows, 0 );
-    bitrow = pbm_allocrow( cols );
+    if ( pixelCnt == 0 )
+        pm_error("No image data.");
+
+    else if ( pixelCnt < MACP_BYTES )
+        pm_error("Compressed image data terminated prematurely.");
 
-    for ( row = 0; row < rows; row++ )
-	{
-	for ( bcol = 0; bcol < BYTES_WIDE; bcol++ )
-	    for ( i = 0; i < 8; i++ )
-		bitrow[bcol * 8 + i] =
-		    ( (Pic[row][bcol] >> (7 - i)) & 1 ) ? PBM_BLACK : PBM_WHITE;
-	pbm_writepbmrow( stdout, bitrow, cols, 0 );
-	}
+    else if ( outOfSync > 0 )
+        pm_message("Warning: Corrupt image data.  %d rows misaligned.",
+                   outOfSync);
 
     pm_close( stdout );
     exit( 0 );
-    }
-
-/*
-** Some of the following routine is:
-**
-**                Copyright 1987 by Patrick J. Naughton
-**                         All Rights Reserved
-** 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.
-*/
-
-static void
-ReadMacPaintFile( file, extraskip, scanLineP, Pic )
-    FILE* file;
-    int extraskip;
-    int* scanLineP;
-    unsigned char Pic[MAX_LINES][BYTES_WIDE];
-    {
-    unsigned int i, j, k;
-    unsigned char ch;
-
-    /* Skip over the header. */
-    for ( i = 0; i < extraskip; i++ )
-	getc( file );
-    for ( i = 0; i < HEADER_LENGTH; i++ )
-	getc( file );
-
-    *scanLineP = 0;
-    k = 0;
-
-    while ( *scanLineP < MAX_LINES )
-	{
-	ch = (unsigned char) getc( file );	/* Count byte */
-	i = (unsigned int) ch;
-	if ( ch < 0x80 )
-	    {	/* Unpack next (I+1) chars as is */
-	    for ( j = 0; j <= i; j++ )
-		if ( *scanLineP < MAX_LINES )
-		    {
-		    Pic[*scanLineP][k++] = (unsigned char) getc( file );
-		    if ( ! (k %= BYTES_WIDE) )
-			*scanLineP += 1;
-		    }
-	    }
-	else
-	    {	/* Repeat next char (2's comp I) times */
-	    ch = getc( file );
-	    for ( j = 0; j <= 256 - i; j++ )
-		if ( *scanLineP < MAX_LINES )
-		    {
-		    Pic[*scanLineP][k++] = (unsigned char) ch;
-		    if ( ! (k %= BYTES_WIDE) )
-			*scanLineP += 1;
-		    }
-	    }
-	}
-    }
+}
diff --git a/converter/pbm/pbmto10x.c b/converter/pbm/pbmto10x.c
index f8a38b5d..d040b3ed 100644
--- a/converter/pbm/pbmto10x.c
+++ b/converter/pbm/pbmto10x.c
@@ -12,16 +12,14 @@
 ** Modified to shorten stripes and eliminate blank stripes. Dec 1994.
 */
 
+#include <stdbool.h>
+
 #include "pbm.h"
 #include "mallocvar.h"
 
 #define LOW_RES_ROWS    8       /* printed per pass */
 #define HIGH_RES_ROWS   16      /* printed per pass */
 
-static int  highres = 0;
-static FILE *ifp;
-static int  rows, cols, format;
-
 
 
 static void
@@ -29,8 +27,6 @@ outstripe(char * const stripe,
           char * const sP, 
           int    const reschar) {
 
-    int ncols;
-
     char * p;
 
     p = sP;  /* initial value */
@@ -41,10 +37,14 @@ outstripe(char * const stripe,
             ++p;
             break;
         }
-    ncols = p - stripe;
-    if (ncols > 0) {
-        printf("\033%c%c%c", reschar, ncols % 256, ncols / 256);
-        fwrite(stripe, sizeof(char), ncols, stdout);
+
+    {
+        unsigned int const ncols = p - stripe;
+
+        if (ncols > 0) {
+            printf("\033%c%c%c", reschar, ncols % 256, ncols / 256);
+            fwrite(stripe, sizeof(char), ncols, stdout);
+        }
     }
     putchar('\n');          /* flush buffer */
 }
@@ -52,26 +52,44 @@ outstripe(char * const stripe,
 
 
 static void
-res_60x72(void) {
-    int i, item, npins, row, col;
-    bit *bitrows[LOW_RES_ROWS], *bP[LOW_RES_ROWS];
-    char *stripe, *sP;
+res_60x72(FILE * const ifP,
+          int    const rows,
+          int    const cols,
+          int    const format) {
+
+    int row;
+    unsigned int i;
+    bit * bitrows[LOW_RES_ROWS];
+    char *stripe;
+    char *sP;
 
     MALLOCARRAY(stripe, cols);
     if (stripe == NULL)
         pm_error("Unable to allocate %u bytes for a stripe buffer.",
-                 cols * sizeof(stripe[0]));
+                 (unsigned)(cols * sizeof(stripe[0])));
+
     for (i = 0; i < LOW_RES_ROWS; ++i)
         bitrows[i] = pbm_allocrow(cols);
+
     printf("\033A\010");        /* '\n' = 8/72 */
+
     for (row = 0, sP = stripe; row < rows; row += LOW_RES_ROWS, sP = stripe) {
+        unsigned int col;
+        unsigned int i;
+        unsigned int npins;
+        bit * bP[LOW_RES_ROWS];
+
         if (row + LOW_RES_ROWS <= rows)
             npins = LOW_RES_ROWS;
         else
             npins = rows - row;
+
         for (i = 0; i < npins; ++i)
-            pbm_readpbmrow(ifp, bP[i] = bitrows[i], cols, format);
+            pbm_readpbmrow(ifP, bP[i] = bitrows[i], cols, format);
+
         for (col = 0; col < cols; ++col) {
+            unsigned int item;
+
             item = 0;
             for (i = 0; i < npins; ++i)
                 if (*(bP[i]++) == PBM_BLACK)
@@ -81,32 +99,52 @@ res_60x72(void) {
         outstripe(stripe, sP, 'K');
     }
     printf("\033@");
+
+    for (i = 0; i < LOW_RES_ROWS; ++i)
+        pbm_freerow(bitrows[i]);
+
     free(stripe);
 }
 
 
 
 static void
-res_120x144(void) {
-    int i, pin, item, npins, row, col;
-    bit *bitrows[HIGH_RES_ROWS], *bP[HIGH_RES_ROWS];
-    char *stripe, *sP;
+res_120x144(FILE * const ifP,
+            int    const rows,
+            int    const cols,
+            int    const format) {
+
+    unsigned int i;
+    int row;
+    char *stripe;
+    char * sP;
+    bit * bitrows[HIGH_RES_ROWS];
 
     MALLOCARRAY(stripe, cols);
     if (stripe == NULL)
         pm_error("Unable to allocate %u bytes for a stripe buffer.",
-                 cols * sizeof(stripe[0]));
+                 (unsigned)(cols * sizeof(stripe[0])));
+
     for (i = 0; i < HIGH_RES_ROWS; ++i)
         bitrows[i] = pbm_allocrow(cols);
+
     printf("\0333\001");            /* \n = 1/144" */
+
     for (row = 0, sP = stripe; row < rows; row += HIGH_RES_ROWS, sP = stripe) {
+        unsigned int i;
+        unsigned int col;
+        bit * bP[HIGH_RES_ROWS];
+        unsigned int npins;
+
         if (row + HIGH_RES_ROWS <= rows)
             npins = HIGH_RES_ROWS;
         else
             npins = rows - row;
         for (i = 0; i < npins; ++i)
-            pbm_readpbmrow(ifp, bP[i] = bitrows[i], cols, format);
+            pbm_readpbmrow(ifP, bP[i] = bitrows[i], cols, format);
         for (col = 0; col < cols; ++col) {
+            unsigned int pin;
+            unsigned int item;
             item = 0;
             /* even rows */
             for (pin = i = 0; i < npins; i += 2, ++pin)
@@ -115,8 +153,9 @@ res_120x144(void) {
             *sP++ = item;
         }
         outstripe(stripe, sP, 'L');
-        sP = stripe;
-        for (col = 0; col < cols; ++col) {
+        for (col = 0, sP = stripe; col < cols; ++col) {
+            unsigned int pin;
+            unsigned int item;
             item = 0;
             /* odd rows */
             for (i = 1, pin = 0; i < npins; i += 2, ++pin)
@@ -128,20 +167,29 @@ res_120x144(void) {
         printf("\033J\016");        /* 14/144 down, \n did 1/144 */
     }
     printf("\033@");
+
+    for (i = 0; i < LOW_RES_ROWS; ++i)
+        pbm_freerow(bitrows[i]);
+
     free(stripe);
 }
 
 
 
 int
-main(int argc, char * argv[]) {
+main(int argc, const char ** argv) {
 
     const char * fname;
+    static FILE * ifP;
+    int rows, cols, format;
+
+    bool isHighRes;
 
-    pbm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
+    isHighRes = false;  /* initial assumption */
     if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'h') {
-        highres = 1;
+        isHighRes = true;
         --argc;
         ++argv;
     }
@@ -152,17 +200,18 @@ main(int argc, char * argv[]) {
     else
         fname = "-";
     
-    ifp = pm_openr(fname);
+    ifP = pm_openr(fname);
 
-    pbm_readpbminit(ifp, &cols, &rows, &format);
+    pbm_readpbminit(ifP, &cols, &rows, &format);
 
-    if (highres)
-        res_120x144();
+    if (isHighRes)
+        res_120x144(ifP, rows, cols, format);
     else
-        res_60x72();
+        res_60x72(ifP, rows, cols, format);
+
+    pm_close(ifP);
 
-    pm_close(ifp);
-    exit(0);
+    return 0;
 }
 
 
diff --git a/converter/pbm/pbmtoascii.c b/converter/pbm/pbmtoascii.c
index 0472f809..976b188f 100644
--- a/converter/pbm/pbmtoascii.c
+++ b/converter/pbm/pbmtoascii.c
@@ -1,4 +1,4 @@
-/* pbmtoascii.c - read a portable bitmap and produce ASCII graphics
+/* pbmtoascii.c - read a PBM image and produce ASCII graphics
 **
 ** Copyright (C) 1988, 1992 by Jef Poskanzer.
 **
@@ -10,8 +10,33 @@
 ** implied warranty.
 */
 
+#include <assert.h>
+#include "mallocvar.h"
 #include "pbm.h"
 
+/*
+  The algorithm is based on describing the 2 or 8 pixels in a cell with a
+  single integer called a signature, which we use as an index into an array to
+  get the character whose glyph best matches those pixels.  The encoding is as
+  follows.  Make a string of bits, with each bit being one pixel of the cell,
+  1 = black, 0 = white.  The order of the string is left to right across the
+  top row, then the next row down, etc.  Considering that string to be a
+  binary cipher, the integer it represents is the signature.
+
+  Example: the 2x4 cell consists of these pixels: (* = black)
+
+      * *
+      *
+
+        *
+  The bit string to represent this is 11100001.  So the signature for this
+  cell is the integer 0xE1 (225).
+
+  You index the array carr2x4 with 0xE1, and get '?' as the character to
+  represent that cell.  (We don't really try very hard to match the shape;
+  it's mostly important to match the density).
+*/
+
 #define SQQ '\''
 #define BSQ '\\'
 
@@ -19,7 +44,7 @@
 **     1
 **     2
 */
-static char carr1x2[4] = {
+static char const carr1x2[4] = {
 /*   0    1    2    3   */
     ' ', '"', 'o', 'M' };
 
@@ -35,47 +60,177 @@ static char carr1x2[4] = {
 #define D06 '&'
 #define D05 '$'
 #define D04 '?'
-static char carr2x4[256] = {
+static char const carr2x4[256] = {
 /*0  1    2   3    4   5    6   7    8   9    A   B    C   D    E   F  */
-' ',SQQ, '`','"', '-',SQQ, SQQ,SQQ, '-','`', '`','`', '-','^', '^','"',/*00-0F*/
-'.',':', ':',':', '|','|', '/',D04, '/','>', '/','>', '~','+', '/','*',/*10-1F*/
-'.',':', ':',':', BSQ,BSQ, '<','<', '|',BSQ, '|',D04, '~',BSQ, '+','*',/*20-2F*/
-'-',':', ':',':', '~',D04, '<','<', '~','>', D04,'>', '=','b', 'd','#',/*30-3F*/
-'.',':', ':',':', ':','!', '/',D04, ':',':', '/',D04, ':',D04, D04,'P',/*40-4F*/
-',','i', '/',D04, '|','|', '|','T', '/',D04, '/','7', 'r','}', '/','P',/*50-5F*/
-',',':', ';',D04, '>',D04, 'S','S', '/',')', '|','7', '>',D05, D05,D06,/*60-6F*/
-'v',D04, D04,D05, '+','}', D05,'F', '/',D05, '/',D06, 'p','D', D06,D07,/*70-7F*/
-'.',':', ':',':', ':',BSQ, ':',D04, ':',BSQ, '!',D04, ':',D04, D04,D05,/*80-8F*/
-BSQ,BSQ, ':',D04, BSQ,'|', '(',D05, '<','%', D04,'Z', '<',D05, D05,D06,/*90-9F*/
-',',BSQ, 'i',D04, BSQ,BSQ, D04,BSQ, '|','|', '|','T', D04,BSQ, '4','9',/*A0-AF*/
-'v',D04, D04,D05, BSQ,BSQ, D05,D06, '+',D05, '{',D06, 'q',D06, D06,D07,/*B0-BF*/
-'_',':', ':',D04, ':',D04, D04,D05, ':',D04, D04,D05, ':',D05, D05,D06,/*C0-CF*/
-BSQ,D04, D04,D05, D04,'L', D05,'[', '<','Z', '/','Z', 'c','k', D06,'R',/*D0-DF*/
-',',D04, D04,D05, '>',BSQ, 'S','S', D04,D05, 'J',']', '>',D06, '1','9',/*E0-EF*/
-'o','b', 'd',D06, 'b','b', D06,'6', 'd',D06, 'd',D07, '#',D07, D07,D08 /*F0-FF*/
-    };
+' ',SQQ, '`','"', '-',SQQ, SQQ,SQQ, '-','`', '`','`', '-','^','^','"',/*00-0F*/
+'.',':', ':',':', '|','|', '/',D04, '/','>', '/','>', '~','+','/','*',/*10-1F*/
+'.',':', ':',':', BSQ,BSQ, '<','<', '|',BSQ, '|',D04, '~',BSQ,'+','*',/*20-2F*/
+'-',':', ':',':', '~',D04, '<','<', '~','>', D04,'>', '=','b','d','#',/*30-3F*/
+'.',':', ':',':', ':','!', '/',D04, ':',':', '/',D04, ':',D04,D04,'P',/*40-4F*/
+',','i', '/',D04, '|','|', '|','T', '/',D04, '/','7', 'r','}','/','P',/*50-5F*/
+',',':', ';',D04, '>',D04, 'S','S', '/',')', '|','7', '>',D05,D05,D06,/*60-6F*/
+'v',D04, D04,D05, '+','}', D05,'F', '/',D05, '/',D06, 'p','D',D06,D07,/*70-7F*/
+'.',':', ':',':', ':',BSQ, ':',D04, ':',BSQ, '!',D04, ':',D04,D04,D05,/*80-8F*/
+BSQ,BSQ, ':',D04, BSQ,'|', '(',D05, '<','%', D04,'Z', '<',D05,D05,D06,/*90-9F*/
+',',BSQ, 'i',D04, BSQ,BSQ, D04,BSQ, '|','|', '|','T', D04,BSQ,'4','9',/*A0-AF*/
+'v',D04, D04,D05, BSQ,BSQ, D05,D06, '+',D05, '{',D06, 'q',D06,D06,D07,/*B0-BF*/
+'_',':', ':',D04, ':',D04, D04,D05, ':',D04, D04,D05, ':',D05,D05,D06,/*C0-CF*/
+BSQ,D04, D04,D05, D04,'L', D05,'[', '<','Z', '/','Z', 'c','k',D06,'R',/*D0-DF*/
+',',D04, D04,D05, '>',BSQ, 'S','S', D04,D05, 'J',']', '>',D06,'1','9',/*E0-EF*/
+'o','b', 'd',D06, 'b','b', D06,'6', 'd',D06, 'd',D07, '#',D07,D07,D08 /*F0-FF*/
+};
 
 
 
-int
-main( argc, argv )
-int argc;
-char* argv[];
+static void
+makeRowOfSigs(FILE *         const ifP,
+              unsigned int   const cols,
+              unsigned int   const rows,
+              int            const format,
+              unsigned int   const cellWidth,
+              unsigned int   const cellHeight,
+              unsigned int   const row,
+              unsigned int * const sig,
+              unsigned int   const ccols) {
+/*----------------------------------------------------------------------------
+   Compute the signatures for every cell in a row.
+
+   Read the pixels from *ifP, which is positioned to the first pixel row
+   of the cell row, which is row number 'row'.  The image dimensions are
+   'cols' x 'rows' pixels.
+
+   Each cell is 'cellWidth' x 'cellHeight'.
+
+   Return the signatures as sig[], which is 'ccols' wide because that's how
+   many cells you get from 'cols' pixels divided into cells 'cellWidth' pixels
+   wide.
+-----------------------------------------------------------------------------*/
+    unsigned int b;
+    unsigned int subrow;  /* row within cell */
+    bit * bitrow;        /* malloc'ed array */
+
+    bitrow = pbm_allocrow(cols);
     {
-    FILE* ifp;
-    int argn, gridx, gridy, rows, cols, format;
-    int ccols, lastcol, row, subrow, subcol;
-    register int col, b;
-    bit* bitrow;
-    register bit* bP;
-    int* sig;
-    register int* sP;
-    char* line;
-    register char* lP;
-    char* carr;
+        unsigned int col;
+        for (col = 0; col < ccols; ++col)
+            sig[col] = 0;
+    }
+
+    b = 0x1;  /* initial value */
+    for (subrow = 0; subrow < cellHeight; ++subrow) {
+        if (row + subrow < rows) {
+            unsigned int subcol;  /* col within cell */
+            pbm_readpbmrow(ifP, bitrow, cols, format);
+            for (subcol = 0; subcol < cellWidth; ++subcol) {
+                unsigned int col, ccol;
+                for (col = subcol, ccol = 0;
+                     col < cols;
+                     col += cellWidth, ++ccol) {
+                    if (bitrow[col] == PBM_BLACK)
+                        sig[ccol] |= b;
+                }
+                b <<= 1;
+            }
+        }
+    }
+    pbm_freerow(bitrow);
+}
+
+
+
+static void
+findRightMargin(const unsigned int * const sig,
+                unsigned int         const ccols,
+                const char *         const carr,
+                unsigned int *       const endColP) {
+/*----------------------------------------------------------------------------
+   Find the first cell of the right margin, i.e. a contiguous set of
+   all-white cells at the right end of the row.
+-----------------------------------------------------------------------------*/
+    unsigned int endcol;
+
+    for (endcol = ccols; endcol > 0; --endcol) {
+        if (carr[sig[endcol-1]] != ' ')
+            break;
+    }
+    *endColP = endcol;
+}
+
+
+
+static void
+assembleCellRow(const unsigned int * const sig,
+                unsigned int         const ccols,
+                const char *         const carr,
+                char *               const line) {
+/*----------------------------------------------------------------------------
+   Return as line[] the line of ASCII codes for the characters of one
+   row of cells, ready for printing.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+
+    for (col = 0; col < ccols; ++col)
+        line[col] = carr[sig[col]];
+
+    line[ccols] = '\0';
+}
+
+
+
+static void
+pbmtoascii(FILE *       const ifP,
+           unsigned int const cellWidth,
+           unsigned int const cellHeight,
+           const char * const carr) {
+
+    int cols, rows, format;
+    unsigned int ccols;
+    char * line;         /* malloc'ed array */
+    unsigned int row;
+    unsigned int * sig;  /* malloc'ed array */
+        /* This describes in a single integer the pixels of a cell,
+           as described above.
+        */
+    assert(cellWidth * cellHeight <= sizeof(sig[0])*8);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    ccols = (cols + cellWidth - 1) / cellWidth;
+
+    MALLOCARRAY(sig, ccols);
+    if (sig == NULL)
+        pm_error("No memory for %u columns", ccols);
+    MALLOCARRAY_NOFAIL(line, ccols+1);
+    if (line == NULL)
+        pm_error("No memory for %u columns", ccols);
+
+    for (row = 0; row < rows; row += cellHeight) {
+        unsigned int endCol;
+
+        makeRowOfSigs(ifP, cols, rows, format, cellWidth, cellHeight,
+                      row, sig, ccols);
+
+        findRightMargin(sig, ccols, carr, &endCol);
+
+        assembleCellRow(sig, endCol, carr, line);
+
+        puts(line);
+    }
+    free(sig);
+    free(line);
+}
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    FILE * ifP;
+    int argn, gridx, gridy;
+    const char * carr;
     const char* usage = "[-1x2|-2x4] [pbmfile]";
 
-    pbm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     /* Set up default parameters. */
     argn = 1;
@@ -85,81 +240,38 @@ char* argv[];
 
     /* Check for flags. */
     while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-	{
-	if ( pm_keymatch( argv[argn], "-1x2", 2 ) )
-	    {
-	    gridx = 1;
-	    gridy = 2;
-	    carr = carr1x2;
-	    }
-	else if ( pm_keymatch( argv[argn], "-2x4", 2 ) )
-	    {
-	    gridx = 2;
-	    gridy = 4;
-	    carr = carr2x4;
-	    }
-	else
-	    pm_usage( usage );
-	++argn;
-	}
+    {
+        if ( pm_keymatch( argv[argn], "-1x2", 2 ) )
+        {
+            gridx = 1;
+            gridy = 2;
+            carr = carr1x2;
+        }
+        else if ( pm_keymatch( argv[argn], "-2x4", 2 ) )
+        {
+            gridx = 2;
+            gridy = 4;
+            carr = carr2x4;
+        }
+        else
+            pm_usage( usage );
+        ++argn;
+    }
 
     if ( argn < argc )
-	{
-	ifp = pm_openr( argv[argn] );
-	++argn;
-	}
+    {
+        ifP = pm_openr( argv[argn] );
+        ++argn;
+    }
     else
-	ifp = stdin;
+        ifP = stdin;
     
     if ( argn != argc )
         pm_usage( usage );
 
-    pbm_readpbminit( ifp, &cols, &rows, &format );
-    ccols = ( cols + gridx - 1 ) / gridx;
-    bitrow = pbm_allocrow( cols );
-    sig = (int*) pm_allocrow( ccols, sizeof(int) );
-    line = (char*) pm_allocrow( ccols + 1, sizeof(char) );
-
-    for ( row = 0; row < rows; row += gridy )
-	{
-	/* Get a character-row's worth of sigs. */
-	for ( col = 0; col < ccols; ++col )
-	    sig[col] = 0;
-	b = 1;
-	for ( subrow = 0; subrow < gridy; ++subrow )
-	    {
-	    if ( row + subrow < rows )
-		{
-		pbm_readpbmrow( ifp, bitrow, cols, format );
-		for ( subcol = 0; subcol < gridx; ++subcol )
-		    {
-		    for ( col = subcol, bP = &(bitrow[subcol]), sP = sig;
-			  col < cols;
-			  col += gridx, bP += gridx, ++sP )
-			if ( *bP == PBM_BLACK )
-			    *sP |= b;
-		    b <<= 1;
-		    }
-		}
-	    }
-	/* Ok, now remove trailing blanks.  */
-	for ( lastcol = ccols - 1; lastcol >= 0; --lastcol )
-	    if ( carr[sig[lastcol]] != ' ' )
-		break;
-	/* Copy chars to an array and print. */
-	for ( col = 0, sP = sig, lP = line; col <= lastcol; ++col, ++sP, ++lP )
-	    *lP = carr[*sP];
-	*lP++ = '\0';
-	puts( line );
-	}
-
-    pm_close( ifp );
-    pbm_freerow( bitrow );
-    pm_freerow( (char*) sig );
-    pm_freerow( (char*) line );
-
-    /* If the program failed, it previously aborted with nonzero completion
-       code, via various function calls.
-    */
+    pbmtoascii(ifP, gridx, gridy, carr);
+
+    pm_close(ifP);
+
     return 0;
-    }
+}
diff --git a/converter/pbm/pbmtoatk.c b/converter/pbm/pbmtoatk.c
index 9399f602..ea5b7abe 100644
--- a/converter/pbm/pbmtoatk.c
+++ b/converter/pbm/pbmtoatk.c
@@ -118,63 +118,52 @@ process_atk_byte(int *           const pcurcount,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char ** argv) {
 
-    FILE *ifd;
-    bit *bitrow;
-    register bit *bP;
-    int rows, cols, format, row;
-    int col;
-    unsigned char curbyte, newbyte;
-    int curcount, gather;
+    FILE * ifP;
+    bit * bitrow;
+    int rows, cols, format;
+    unsigned int row;
+    unsigned char curbyte;
+    int curcount;
 
-    pbm_init ( &argc, argv );
+    pm_proginit(&argc, argv);
 
     if (argc-1 > 1)
         pm_error("Too many arguments.  Only argument is file name");
 
     else if (argc-1 == 1) {
-        ifd = pm_openr( argv[1] );
+        ifP = pm_openr(argv[1]);
     } else {
-        ifd = stdin;
+        ifP = stdin;
     }
 
-    pbm_readpbminit(ifd, &cols, &rows, &format);
-    bitrow = pbm_allocrow(cols);
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+    bitrow = pbm_allocrow_packed(cols);
 
-    printf ("\\begindata{raster,%d}\n", 1);
-    printf ("%d %d %d %d ", RASTERVERSION, 0, DEFAULTSCALE, DEFAULTSCALE);
-    printf ("%d %d %d %d\n", 0, 0, cols, rows); /* subraster */
-    printf ("bits %d %d %d\n", 1, cols, rows);
+    printf("\\begindata{raster,%d}\n", 1);
+    printf("%d %d %d %d ", RASTERVERSION, 0, DEFAULTSCALE, DEFAULTSCALE);
+    printf("%d %d %d %d\n", 0, 0, cols, rows); /* subraster */
+    printf("bits %d %d %d\n", 1, cols, rows);
 
     for (row = 0; row < rows; ++row) {
-        pbm_readpbmrow(ifd, bitrow, cols, format);
-        bP = bitrow;
-        gather = 0;
-        newbyte = 0;
-        curbyte = 0;
-        curcount = 0;
-        col = 0;
-        while (col < cols) {
-            if (gather > 7) {
-                process_atk_byte (&curcount, &curbyte, stdout, newbyte, FALSE);
-                gather = 0;
-                newbyte = 0;
-            }
-            newbyte = (newbyte << 1) | (*bP++);
-            gather += 1;
-            col += 1;
-        }
-
-        if (gather > 0) {
-            newbyte = (newbyte << (8 - gather));
-            process_atk_byte (&curcount, &curbyte, stdout, newbyte, TRUE);
+        unsigned int const byteCt = pbm_packed_bytes(cols);
+        unsigned int i;
+
+        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+        pbm_cleanrowend_packed(bitrow, cols);
+        
+        for (i = 0, curbyte = 0, curcount = 0; i < byteCt; ++i) {
+            process_atk_byte(&curcount, &curbyte, stdout,
+                             bitrow[i],
+                             i + 1 < byteCt ? FALSE : TRUE );
         }
     }
 
-    pm_close( ifd );
+    pbm_freerow_packed(bitrow);
+    pm_close(ifP);
     
-    printf ("\\enddata{raster, %d}\n", 1);
+    printf("\\enddata{raster, %d}\n", 1);
 
     return 0;
 }
diff --git a/converter/pbm/pbmtocis.c b/converter/pbm/pbmtocis.c
new file mode 100644
index 00000000..9bb42c56
--- /dev/null
+++ b/converter/pbm/pbmtocis.c
@@ -0,0 +1,170 @@
+/*
+ *  cistopbm: Convert images in the CompuServe RLE format to PBM
+ *  Copyright (C) 2009  John Elliott
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "pbm.h"
+
+/* The maximum length of a run. Limit it to 0x5E bytes so that it is always
+ * represented by a printable character 0x20-0x7E */
+#define MAXRUNLENGTH 0x5E
+
+static void syntax(const char *prog)
+{
+    pm_usage(" { options } { input } }\n\n"
+         "Input file should be in PBM format.\n"
+         "Output will be in CompuServe RLE format.\n"
+         "Options:\n"
+         "-i, --inverse: Reverse black and white.\n"
+         "-w, --whitebg: White background.\n"
+         "--:            End of options\n\n"
+"pbmtocis v1.00, Copyright 2009 John Elliott <jce@seasip.demon.co.uk>\n"
+"This program is redistributable under the terms of the GNU General Public\n"
+"License, version 2 or later.\n"
+         );
+}
+
+int main(int argc, const char **argv)
+{
+    FILE *ofP = stdout;
+    FILE *ifP;
+    int inoptions = 1;
+    int n, x, y;
+    int bg = PBM_BLACK; /* Default colouring is white on black */
+    int inverse = 0;
+    int cell, last, run;
+    const char *inpname = NULL;
+
+    int outh, outw;
+    int height, width;
+    bit **bits;
+
+    pm_proginit(&argc, argv);
+
+    for (n = 1; n < argc; n++)
+    {
+        if (!strcmp(argv[n], "--"))
+        {
+            inoptions = 0;
+            continue;
+        }
+        if (inoptions)
+        {
+            if (pm_keymatch(argv[n], "-h", 2) ||
+                pm_keymatch(argv[n], "-H", 2) ||
+                pm_keymatch(argv[n], "--help", 6))
+            {
+                syntax(argv[0]);
+                return EXIT_SUCCESS;
+            }
+            if (pm_keymatch(argv[n], "-i", 2) ||
+                pm_keymatch(argv[n], "-I", 2) ||
+                pm_keymatch(argv[n], "--inverse", 9))
+            {
+                inverse = 1;
+                continue;
+            }
+            if (pm_keymatch(argv[n], "-w", 2) ||
+                pm_keymatch(argv[n], "-W", 2) ||
+                pm_keymatch(argv[n], "--whitebg", 9))
+            {
+                bg = PBM_WHITE; 
+                continue;
+            }
+            if (argv[n][0] == '-' && argv[n][1] != 0)
+            {
+                pm_message("Unknown option: %s", argv[n]);
+                syntax(argv[0]);
+                return EXIT_FAILURE;
+            }
+        }
+
+        if (inpname == NULL) inpname = argv[n];
+        else { syntax(argv[0]); return EXIT_FAILURE; }
+    }
+    if (inpname == NULL) inpname = "-";
+    ifP  = pm_openr(inpname);
+
+    /* Load the PBM */
+    bits = pbm_readpbm(ifP, &width, &height);
+
+    if      (width <= 128 && height <= 96)  { outw = 128; outh = 96; }
+    else if (width <= 256 && height <= 192) { outw = 256; outh = 192; }
+    else
+    {
+        outw = 256;
+        outh = 192;
+        pm_message("Warning: Input file is larger than 256x192. "
+                "It will be cropped.");
+    }
+    /* Write the magic number */
+    fputc(0x1B, ofP);
+    fputc(0x47, ofP);
+    fputc((outw == 128) ? 0x4D : 0x48, ofP);
+
+    /* And now start encoding */
+    y = x = 0;
+    last = PBM_BLACK;
+    run = 0;
+    while (y < outh)
+    {
+        if (x < width && y < height)    
+        {
+            cell = bits[y][x];
+            if (inverse) cell ^= (PBM_BLACK ^ PBM_WHITE);
+        }
+        else cell = bg;
+
+        if (cell == last)   /* Cell is part of current run */
+        {
+            ++run;
+            if (run > MAXRUNLENGTH)
+            {       
+                fputc(0x20 + MAXRUNLENGTH, ofP);
+                fputc(0x20, ofP);
+                run -= MAXRUNLENGTH;
+            }   
+        }
+        else    /* change */
+        {
+            fputc(run + 0x20, ofP);
+            last = last ^ (PBM_BLACK ^ PBM_WHITE);
+            run = 1;
+        }
+        ++x;
+        if (x >= outw) { x = 0; ++y; }
+    }
+    if (last == bg) /* Last cell written was background. Write foreground */
+    {
+        fputc(run + 0x20, ofP);
+    }
+    else if (run)   /* Write background and foreground */
+    {
+        fputc(run + 0x20, ofP);
+        fputc(0x20, ofP);
+    }
+    /* Write the end-graphics signature */
+    fputc(0x1B, ofP);
+    fputc(0x47, ofP);
+    fputc(0x4E, ofP);
+    pm_close(ifP);
+    return 0;   
+}
diff --git a/converter/pbm/pbmtocmuwm.c b/converter/pbm/pbmtocmuwm.c
index 773d988b..983ea491 100644
--- a/converter/pbm/pbmtocmuwm.c
+++ b/converter/pbm/pbmtocmuwm.c
@@ -18,18 +18,20 @@
 */
 
 #include "pbm.h"
-#include "cmuwm.h"
+
+
 
 static void
 putinit(unsigned int const rows,
         unsigned int const cols) {
 
-    const char * const initWriteError =
+    const char initWriteError[] =
         "CMU window manager header write error";
+    uint32_t const cmuwmMagic = 0xf10040bb;
 
     int rc;
 
-    rc = pm_writebiglong(stdout, CMUWM_MAGIC);
+    rc = pm_writebiglong(stdout, cmuwmMagic);
     if (rc == -1)
         pm_error(initWriteError);
     rc = pm_writebiglong(stdout, cols);
diff --git a/converter/pbm/pbmtoepsi.c b/converter/pbm/pbmtoepsi.c
index 5eccc298..87985a1f 100644
--- a/converter/pbm/pbmtoepsi.c
+++ b/converter/pbm/pbmtoepsi.c
@@ -16,19 +16,24 @@
 ** implied warranty.
 */
 
+/*
+ *
+ * Official guide from Adobe:
+ *
+ * Encapsulated PostScript File Format Specification
+ * http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf
+ *
+*/
+
 #include "pm_c_util.h"
 #include "pbm.h"
 #include "shhopt.h"
 
-#if !defined(MAXINT)
-#define MAXINT (0x7fffffff)
-#endif
-
 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 */
+    const char *inputFileName;
 
     unsigned int dpiX;     /* horiz component of DPI option */
     unsigned int dpiY;     /* vert component of DPI option */
@@ -40,8 +45,9 @@ struct cmdlineInfo {
 
 
 static void
-parse_dpi(char * const dpiOpt, 
-          unsigned int * const dpiXP, unsigned int * const dpiYP) {
+parseDpi(char *         const dpiOpt, 
+         unsigned int * const dpiXP,
+         unsigned int * const dpiYP) {
 
     char *dpistr2;
     unsigned int dpiX, dpiY;
@@ -72,7 +78,7 @@ parse_dpi(char * const dpiOpt,
 
 
 static void
-parseCommandLine(int argc, char ** const argv,
+parseCommandLine(int argc, const char ** const argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
@@ -97,48 +103,58 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
     
 
     if (dpiOptSpec)
-        parse_dpi(dpiOpt, &cmdlineP->dpiX, &cmdlineP->dpiY);
+        parseDpi(dpiOpt, &cmdlineP->dpiX, &cmdlineP->dpiY);
     else
         cmdlineP->dpiX = cmdlineP->dpiY = 72;
     
     if ((argc-1) > 1)
-        pm_error("Too many arguments (%d).  Only argument is input filespec",
+        pm_error("Too many arguments (%d).  Only argument is input file name",
                  argc-1);
     
     if (argc-1 == 0)
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
     else
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
 }
 
 
 
 static void
-findPrincipalImage(bit ** const bits, 
-                   int    const rows,
-                   int    const cols,
-                   int *  const topP,
-                   int *  const bottomP,
-                   int *  const leftP,
-                   int *  const rightP) {
+findPrincipalImage(bit **         const bits, 
+                   unsigned int   const rows,
+                   unsigned int   const cols,
+                   unsigned int * const topP,
+                   unsigned int * const bottomP,
+                   unsigned int * const leftP,
+                   unsigned int * const rightP) {
+/*----------------------------------------------------------------------------
+   Find the foreground image on a white background.
+
+   Find the image in the pixels bits[][], which is 'rows' rows by
+   'cols' columns.
 
-    int top, bottom, left, right;
-    int row;
+   Return the boundaries of the foreground image as *topP, *bottomP, *leftP,
+   and *rightP.
+
+   If the image is all white, consider the entire image foreground.
+-----------------------------------------------------------------------------*/
+    unsigned int top, bottom, left, right;
+    unsigned int row;
 
     /* Initial values */
-    top = MAXINT;
-    bottom = -MAXINT;
-    left = MAXINT;
-    right = -MAXINT;
+    top    = rows;
+    bottom = 0;
+    left   = cols;
+    right  = 0;
  
-    for (row = 0; row < rows; row++) {
-        int col;
-        for (col = 0; col < cols; col++) {
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
             if (bits[row][col] == PBM_BLACK) {
                 if (row < top) 
                     top = row;
@@ -152,16 +168,19 @@ findPrincipalImage(bit ** const bits,
         }
     }
 
-    if(bottom == -MAXINT) {  /* No black pixels encountered */ 
+    if (top > bottom) {
+        /* No black pixels encountered */ 
         pm_message("Blank page");
-        top = left = 0;
-        bottom = rows-1;  right = cols-1;
-	}
+        top    = 0;
+        left   = 0;
+        bottom = rows - 1;
+        right  = cols - 1;
+    }
 
-    *topP = top;
+    *topP    = top;
     *bottomP = bottom;
-    *leftP = left;
-    *rightP = right;
+    *leftP   = left;
+    *rightP  = right;
 }
 
 
@@ -190,7 +209,8 @@ eightPixels(bit ** const bits,
 /*----------------------------------------------------------------------------
   Compute a byte that represents the 8 pixels starting at Column 'col' of
   row 'row' of the raster 'bits'.  The most significant bit of the result
-  represents the leftmost pixel, with 1 meaning black.
+  represents the leftmost pixel, with 1 meaning black.  (Note that this is
+  the opposite of Postscript.)
 
   The row is 'cols' columns wide, so fill on the right with white if there
   are not eight pixels in the row starting with Column 'col'.
@@ -212,23 +232,23 @@ eightPixels(bit ** const bits,
 
 
 int
-main(int argc, char * argv[]) {
+main(int argc, const char * argv[]) {
 
     struct cmdlineInfo cmdline;
-    FILE *ifP;
-    bit **bits;
+    FILE * ifP;
+    bit ** bits;
     int rows, cols;
-    int top, bottom, left, right;
+    unsigned int top, bottom, left, right;
         /* boundaries of principal part of image -- i.e. excluding white
            borders
         */
 
-    pbm_init( &argc, argv );
+    pm_proginit(&argc, argv);
     
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.inputFilespec);
-    bits = pbm_readpbm( ifP, &cols, &rows );
+    ifP = pm_openr(cmdline.inputFileName);
+    bits = pbm_readpbm(ifP, &cols, &rows);
     pm_close(ifP);
 
     findPrincipalImage(bits, rows, cols, &top, &bottom, &left, &right);
@@ -244,24 +264,26 @@ main(int argc, char * argv[]) {
                right - left + 1, bottom - top + 1, bottom - top + 1);
 
         for (row = top; row <= bottom; row++) {
-            int col;
-	    int outChars = 2;
-	    printf("%% ");
+            unsigned int col;
+            unsigned int outChars;
+            printf("%% ");
+
+            outChars = 2;  /* initial value */
 
             for (col = left; col <= right; col += 8) {
                 if (outChars == 72) {
-		  printf("\n%% ");
-                  outChars = 2;
-		}  
+                    printf("\n%% ");
+                    outChars = 2;
+                }  
 
                 printf("%02x", eightPixels(bits, row, col, cols));
                 outChars += 2;
-	    }
-	    if (outChars > 0)
+            }
+            if (outChars > 0)
                 printf("\n");
         }
         printf("%%%%EndImage\n");
         printf("%%%%EndPreview\n");
     }
-    exit(0);
+    return 0;
 }
diff --git a/converter/pbm/pbmtoepson.c b/converter/pbm/pbmtoepson.c
index 86185d15..bb36791d 100644
--- a/converter/pbm/pbmtoepson.c
+++ b/converter/pbm/pbmtoepson.c
@@ -11,13 +11,12 @@
 ** implied warranty.
 */
 
-#define _BSD_SOURCE    /* Make sure strcasecmp() is in string.h */
-
+#define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
 #include <stdio.h>
-#include <string.h>
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
+#include "nstring.h"
 #include "shhopt.h"
 
 #include "pbm.h"
@@ -29,22 +28,22 @@ enum epsonProtocol {ESCP9, ESCP};
 
 enum adjacence {ADJACENT_ANY, ADJACENT_YES, ADJACENT_NO};
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* '-' if stdin */
-    unsigned int dpi;  /* zero means "any" */
-    enum adjacence adjacence;
+    const char *       inputFileName;  /* '-' if stdin */
+    unsigned int       dpi;  /* zero means "any" */
+    enum adjacence     adjacence;
     enum epsonProtocol protocol;
 };
 
 
 
 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.  
@@ -56,7 +55,7 @@ parseCommandLine(int                 argc,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -69,20 +68,20 @@ parseCommandLine(int                 argc,
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "protocol",   OPT_STRING,   &protocol,
+    OPTENT3(0, "protocol",     OPT_STRING,   &protocol,
             &protocolSpec,                    0);
-    OPTENT3(0, "dpi",        OPT_UINT,   &cmdlineP->dpi,
-            &dpiSpec,                    0);
-    OPTENT3(0, "adjacent",   OPT_FLAG,   NULL,
+    OPTENT3(0, "dpi",          OPT_UINT,     &cmdlineP->dpi,
+            &dpiSpec,                         0);
+    OPTENT3(0, "adjacent",     OPT_FLAG,     NULL,
             &adjacentSpec,                    0);
-    OPTENT3(0, "nonadjacent",   OPT_FLAG,   NULL,
-            &nonadjacentSpec,                    0);
+    OPTENT3(0, "nonadjacent",  OPT_FLAG,     NULL,
+            &nonadjacentSpec,                 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
@@ -96,11 +95,11 @@ parseCommandLine(int                 argc,
     if (!protocolSpec)
         cmdlineP->protocol = ESCP9;
     else {
-        if (strcasecmp(protocol, "escp9") == 0)
+        if (strcaseeq(protocol, "escp9"))
             cmdlineP->protocol = ESCP9;
-        else if (strcasecmp(protocol, "escp") == 0)
+        else if (strcaseeq(protocol, "escp"))
             cmdlineP->protocol = ESCP;
-        else if (strcasecmp(protocol, "escp2") == 0)
+        else if (strcaseeq(protocol, "escp2"))
             pm_error("This program cannot do ESC/P2.  Try Pbmtoescp2.");
         else
             pm_error("Unrecognized value '%s' for -protocol.  "
@@ -118,13 +117,15 @@ parseCommandLine(int                 argc,
         cmdlineP->adjacence = ADJACENT_ANY;
 
     if (argc-1 < 1)
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
     else {
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
         if (argc-1 > 1)
             pm_error("Too many arguments (%d).  The only non-option argument "
                      "is the file name", argc-1);
     }
+
+    free(option_def);
 }
 
 
@@ -273,7 +274,7 @@ convertToEpson(const bit **       const bits,
                enum adjacence     const adjacence) {
     
     unsigned int const rowsPerStripe = 8;
-    unsigned int const stripes = (rows + rowsPerStripe-1) / rowsPerStripe;
+    unsigned int const stripeCt = (rows + rowsPerStripe-1) / rowsPerStripe;
 
     unsigned int stripe;
     char m;
@@ -288,7 +289,7 @@ convertToEpson(const bit **       const bits,
        stripe can be fewer than 8 rows.
     */
 
-    for (stripe = 0; stripe < stripes; ++stripe) {
+    for (stripe = 0; stripe < stripeCt; ++stripe) {
         const bit ** const stripeBits = &bits[stripe*rowsPerStripe];
         unsigned int const stripeRows = 
             MIN(rowsPerStripe, rows - stripe * rowsPerStripe);
@@ -313,18 +314,18 @@ convertToEpson(const bit **       const bits,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char ** argv) {
 
-    struct cmdlineInfo cmdline;
-    FILE* ifP;
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
     const bit** bits;
     int rows, cols;
 
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
     bits = (const bit **)pbm_readpbm(ifP, &cols, &rows);
 
diff --git a/converter/pbm/pbmtoescp2.c b/converter/pbm/pbmtoescp2.c
index e280b3df..6f284f3c 100644
--- a/converter/pbm/pbmtoescp2.c
+++ b/converter/pbm/pbmtoescp2.c
@@ -1,4 +1,4 @@
-/* pbmtoescp2.c - read a portable bitmap and produce Epson ESC/P2 raster
+ /* pbmtoescp2.c - read a portable bitmap and produce Epson ESC/P2 raster
 **                 graphics output data for Epson Stylus printers
 **
 ** Copyright (C) 2003 by Ulrich Walcher (u.walcher@gmx.de)
@@ -10,45 +10,65 @@
 ** copyright notice and this permission notice appear in supporting
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.
+**
+** Major changes were made in July 2015 by Akira Urushibata.
+** Added 720 DPI capability.
+** Added -formfeed, -raw and -stripeheight.
+** Replaced Packbits run length encoding function.  (Use library function.)
+*
+*  ESC/P Reference Manual (1997)
+*  ftp://download.epson-europe.com/pub/download/182/epson18162eu.zip
 */
 
-/* I used the Epson ESC/P Reference Manual (1997) in writing this. */
-
 #include <string.h>
 
 #include "pm_c_util.h"
-#include "pbm.h"
+#include "mallocvar.h"
 #include "shhopt.h"
+#include "runlength.h"
+#include "pbm.h"
+
+
 
 static char const esc = 033;
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     const char * inputFileName;
     unsigned int resolution;
     unsigned int compress;
+    unsigned int stripeHeight;
+    bool raw;
+    bool formfeed;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo *cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo *cmdlineP) {
 
     optStruct3 opt;
     unsigned int option_def_index = 0;
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def = malloc(100*sizeof(optEntry));
 
-    unsigned int compressSpec, resolutionSpec;
+    unsigned int compressSpec, resolutionSpec, stripeHeightSpec,
+                 rawSpec, formfeedSpec;
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;
     opt.allowNegNum = FALSE;
     OPTENT3(0, "compress",     OPT_UINT,    &cmdlineP->compress,    
-            &compressSpec, 0);
+            &compressSpec,    0);
     OPTENT3(0, "resolution",   OPT_UINT,    &cmdlineP->resolution,  
-            &resolutionSpec, 0);
+            &resolutionSpec,  0);
+    OPTENT3(0, "stripeheight", OPT_UINT,    &cmdlineP->stripeHeight,  
+            &stripeHeightSpec, 0);
+    OPTENT3(0, "raw",          OPT_FLAG,    NULL,  
+            &rawSpec,    0);
+    OPTENT3(0, "formfeed",     OPT_FLAG,    NULL,  
+            &formfeedSpec,    0);
     
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
     
     if (argc-1 > 1)
         pm_error("Too many arguments: %d.  "
@@ -62,79 +82,81 @@ parseCommandLine(int argc, char ** argv,
         cmdlineP->compress = 1;
 
     if (resolutionSpec) {
-        if (cmdlineP->resolution != 360 && cmdlineP->resolution != 180)
+        if (cmdlineP->resolution != 720 && cmdlineP->resolution != 360 &&
+            cmdlineP->resolution != 180)
             pm_error("Invalid -resolution value: %u.  "
-                     "Only 180 and 360 are valid.", cmdlineP->resolution);
+                     "Only 180, 360 and 720 are valid.", cmdlineP->resolution);
     } else
         cmdlineP->resolution = 360;
 
+    if (stripeHeightSpec) {
+        if (cmdlineP->stripeHeight == 0 ||
+            cmdlineP->stripeHeight > 255)
+            pm_error("Invalid -stripeheight value: %u. "
+                     "Should be 24, 8, or 1, and must be in the range 1-255",
+                     cmdlineP->stripeHeight);
+        else if (cmdlineP->stripeHeight != 24 &&
+                 cmdlineP->stripeHeight != 8  &&
+                 cmdlineP->stripeHeight != 1)
+            pm_message("Proceeding with irregular -stripeheight value: %u. "
+                       "Should be 24, 8, or 1.", cmdlineP->stripeHeight);
+        else if (cmdlineP->resolution == 720 &&
+                 cmdlineP->stripeHeight != 1)
+            /* The official Epson manual mandates single-row stripes for
+               720 dpi high-resolution images.
+            */
+            pm_message("Proceeding with irregular -stripeheight value: %u. "
+                       "Because resolution i 720dpi, should be 1.",
+                        cmdlineP->stripeHeight);
+    } else
+        cmdlineP->stripeHeight = cmdlineP->resolution == 720 ? 1 : 24;
+
+    if (rawSpec && formfeedSpec)
+        pm_error("You cannot specify both -raw and -formfeed");
+    else {
+        cmdlineP->raw = rawSpec ? true : false ;
+        cmdlineP->formfeed = formfeedSpec ? true : false ;
+    }
+
     if (argc-1 == 1)
         cmdlineP->inputFileName = argv[1];
     else
         cmdlineP->inputFileName = "-";
+
+    free(option_def);
 }
 
 
 
-static unsigned int
-enc_epson_rle(unsigned int          const l, 
-              const unsigned char * const src, 
-              unsigned char *       const dest) {
-/*----------------------------------------------------------------------------
-  compress l data bytes from src to dest and return the compressed
-  length
------------------------------------------------------------------------------*/
-    unsigned int i;      /* index */
-    unsigned int state;  /* run state */
-    unsigned int pos;    /* source position */
-    unsigned int dpos;   /* destination position */
-
-    pos = dpos = state  = 0;
-    while ( pos < l )
-    {
-        for (i=0; i<128 && pos+i<l; i++)
-            /* search for begin of a run, smallest useful run is 3
-               equal bytes 
-            */
-            if(src[pos+i]==src[pos+i+1] && src[pos+i]==src[pos+i+2])
-            {
-                state=1;                       /* set run state */
-                break;
-            }
-	if(i)
-	{
-        /* set counter byte for copy through */
-        dest[dpos] = i-1;       
-        /* copy data bytes before run begin or end cond. */
-        memcpy(dest+dpos+1,src+pos,i);    
-        pos+=i; dpos+=i+1;                 /* update positions */
-	}
-    if (state)
-    {
-        for (i=0; src[pos+i]==src[pos+i+1] && i<128 && pos+i<l; i++);
-        /* found the runlength i */
-        dest[dpos]   = 257-i;           /* set counter for byte repetition */
-        dest[dpos+1] = src[pos];        /* set byte to be repeated */
-        pos+=i; dpos+=2; state=0;       /* update positions, reset run state */
-        }
-    }
-    return dpos;
+static void
+writeSetup(unsigned int const hres) {
+
+    /* Set raster graphic mode. */
+    printf("%c%c%c%c%c%c", esc, '(', 'G', 1, 0, 1);
+
+    /* Set line spacing in units of 1/360 inches. */
+    printf("%c%c%c", esc, '+', 24 * hres / 10);
 }
 
 
 
 int
-main(int argc, char* argv[]) {
+main(int argc, const char * argv[]) {
 
-    FILE* ifP;
+    FILE * ifP;
     int rows, cols;
     int format;
-    unsigned int row, idx, len;
-    unsigned int h, v;
-    unsigned char *bytes, *cprbytes;
-    struct cmdlineInfo cmdline;
+    unsigned int row;
+    unsigned int idx;
+    unsigned int outColByteCt;
+    unsigned int stripeByteCt;
+    unsigned int hres, vres;
+    unsigned char * inBuff;
+    unsigned char * bitrow[256];
+    unsigned char * compressedData;
+    struct CmdlineInfo cmdline;
     
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -142,51 +164,83 @@ main(int argc, char* argv[]) {
 
     pbm_readpbminit(ifP, &cols, &rows, &format);
 
-    bytes = malloc(24*pbm_packed_bytes(cols)+2);
-    cprbytes = malloc(2*24*pbm_packed_bytes(cols));
-    if (bytes == NULL || cprbytes == NULL)
-        pm_error("Cannot allocate memory");
+    if (cols / 256 > 127)  /* Limit in official Epson manual */
+        pm_error("Image width is too large");
 
-    h = v = 3600/cmdline.resolution;
+    outColByteCt = pbm_packed_bytes(cols);
+    stripeByteCt = cmdline.stripeHeight * outColByteCt;
 
-    /* Set raster graphic mode. */
-    printf("%c%c%c%c%c%c", esc, '(', 'G', 1, 0, 1);
+    MALLOCARRAY(inBuff, stripeByteCt);
+    if (inBuff == NULL)
+      pm_error("Out of memory trying to create input buffer of %u bytes",
+               stripeByteCt);
 
-    /* Set line spacing in units of 1/360 inches. */
-    printf("%c%c%c", esc, '+', 24*h/10);
-
-    /* Write out raster stripes 24 rows high. */
-    for (row = 0; row < rows; row += 24) {
-        unsigned int const linesThisStripe = (rows-row<24) ? rows%24 : 24;
-        printf("%c%c%c%c%c%c%c%c", esc, '.', cmdline.compress, 
-               v, h, linesThisStripe, 
-               cols%256, cols/256);
-        /* Read pbm rows, each padded to full byte */
-        for (idx = 0; idx < 24 && row+idx < rows; ++idx)
-            pbm_readpbmrow_packed(ifP,bytes+idx*pbm_packed_bytes(cols),
-                                  cols,format);
-        /* Add delimiter to end of rows, using inverse of final
-           data byte to prevent match. */
-        *(bytes+idx*pbm_packed_bytes(cols)) =
-          ~ *(bytes+idx*pbm_packed_bytes(cols)-1);
-
-        /* Write raster data. */
-        if (cmdline.compress != 0) {
-            /* compressed */
-            len = enc_epson_rle(linesThisStripe * pbm_packed_bytes(cols), 
-                                bytes, cprbytes);
-            fwrite(cprbytes,len,1,stdout);
-        } else
-            /* uncompressed */
-            fwrite(bytes, pbm_packed_bytes(cols), linesThisStripe, stdout);    
-
-        if (rows-row >= 24) putchar('\n');
+    if (cmdline.compress != 0)
+        pm_rlenc_allocoutbuf(&compressedData, stripeByteCt, PM_RLE_PACKBITS);
+    else
+        compressedData = NULL;
+
+    for (idx = 0; idx <= cmdline.stripeHeight; ++idx)
+        bitrow[idx]= &inBuff[idx * outColByteCt];
+
+    hres = vres = 3600 / cmdline.resolution;
+        /* Possible values for hres, vres: 20, 10, 5 */
+
+    if (!cmdline.raw)
+        writeSetup(hres);
+
+    /* Write out raster stripes */
+
+    for (row = 0; row < rows; row += cmdline.stripeHeight ) {
+        unsigned int const rowsThisStripe =
+            MIN(rows - row, cmdline.stripeHeight);
+        unsigned int const outCols = outColByteCt * 8;
+
+        if (rowsThisStripe > 0) {
+            unsigned int idx;
+
+            printf("%c%c%c%c%c%c%c%c", esc, '.', cmdline.compress, vres, hres,
+                   cmdline.stripeHeight, outCols % 256, outCols / 256);
+
+            /* Read pbm rows, each padded to full byte */
+
+            for (idx = 0; idx < rowsThisStripe; ++idx) {
+                pbm_readpbmrow_packed (ifP, bitrow[idx], cols, format);
+                pbm_cleanrowend_packed(bitrow[idx], cols);
+            }
+
+            /* If at bottom pad with empty rows up to stripe height */
+            if (rowsThisStripe < cmdline.stripeHeight )
+                memset(bitrow[rowsThisStripe], 0,
+                       (cmdline.stripeHeight - rowsThisStripe) * outColByteCt);
+
+            /* Write raster data */
+            if (cmdline.compress != 0) {  /* compressed */
+                size_t compressedDataCt;
+
+                pm_rlenc_compressbyte(inBuff, compressedData, PM_RLE_PACKBITS,
+                                      stripeByteCt, &compressedDataCt);
+                fwrite(compressedData, compressedDataCt, 1, stdout);
+            } else                        /* uncompressed */
+                fwrite(inBuff, stripeByteCt, 1, stdout);
+
+            /* Emit newline to print the stripe */
+            putchar('\n');
+        }
     }
-    free(bytes); free(cprbytes);
+
+    free(inBuff); 
+    free(compressedData);
     pm_close(ifP);
 
-    /* Reset printer. */
-    printf("%c%c", esc, '@');
+    /* Form feed */
+    if (cmdline.formfeed)
+        putchar('\f');
+
+    if (!cmdline.raw) {
+        /* Reset printer. a*/
+        printf("%c%c", esc, '@');
+    }
 
     return 0;
 }
diff --git a/converter/pbm/pbmtog3.c b/converter/pbm/pbmtog3.c
index c0dd8c64..f0fd1252 100644
--- a/converter/pbm/pbmtog3.c
+++ b/converter/pbm/pbmtog3.c
@@ -65,7 +65,7 @@ struct outStream {
 static struct outStream out;
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -79,7 +79,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
@@ -108,7 +108,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = TRUE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     free(option_def);
@@ -407,7 +407,7 @@ int
 main(int    argc,
      char * argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     unsigned char * bitrow;
        /* This is the bits of the current row, as read from the input and
diff --git a/converter/pbm/pbmtog3.test b/converter/pbm/pbmtog3.test
deleted file mode 100644
index 9ca45079..00000000
--- a/converter/pbm/pbmtog3.test
+++ /dev/null
@@ -1,23 +0,0 @@
-echo Test 1.  Should print 3697098186 144
-./pbmtog3 ../../testgrid.pbm | cksum
-echo Test 2.  Should print 1248301383 122
-./pbmtog3 -nofixedwidth ../../testgrid.pbm | cksum
-echo Test 3.  Should print 686713716 144
-./pbmtog3 -reverse ../../testgrid.pbm | cksum
-echo Test 4.  Should print 215463240 122
-./pbmtog3 -nofixedwidth -reverse ../../testgrid.pbm | cksum
-echo Test 5.  Should print 28792587 47
-pbmmake -w 10 10 | ./pbmtog3 | cksum
-echo Test 6.  Should print 277456854 32
-pbmmake -w 10 10 | ./pbmtog3 -nofixedwidth | cksum
-echo Test 7.  Should print 28792587 47
-pbmmake -w 10000 10 | ./pbmtog3 | cksum
-echo Test 8.  Should print 871281767 162
-pbmmake -w 10000 10 | ./pbmtog3 -nofixedwidth | cksum
-echo Test 9.  Should print 3736247115 62
-pbmmake -b 10 10 | ./pbmtog3 | cksum
-echo Test 10.  Should print 2820255307 2191856
-pbmmake -g 1700 2286 | ./pbmtog3 | cksum
-echo Test 11.  Should print 4159089282 2226575
-pbmmake -g 1800 2286 | ./pbmtog3 | cksum
-echo Tests done.
diff --git a/converter/pbm/pbmtogem.c b/converter/pbm/pbmtogem.c
index cefbdc95..9eab0416 100644
--- a/converter/pbm/pbmtogem.c
+++ b/converter/pbm/pbmtogem.c
@@ -27,17 +27,21 @@
 *  removed rounding of the imagewidth to the next word boundary
 *  removed arbitrary limit to imagewidth
 *  changed pattern length to 1 to simplify locating of compressable parts
-*	in real world images
+*       in real world images
 *  add solid run and pattern run compression
 *
 *  Deficiencies:
 *  Compression of repeated scanlines not added
 *  
-*	Johann Haider (jh@fortec.tuwien.ac.at)
+*       Johann Haider (jh@fortec.tuwien.ac.at)
 *
 * 94/01/31 Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de)
 * Changed to remove architecture dependencies
 * Added compression of repeated scanlines
+*
+* Feb 2010 afu
+* Added dimension check to prevent short int from overflowing
+* Changed code style (ANSI-style function definitions, etc.)
 */
 
 #include <stdio.h>
@@ -47,59 +51,11 @@
 #define SOLID_0 0
 #define SOLID_1 0xff
 #define MINRUN 4
+#define INT16MAX 32767
+
 #define putsolid(v,c) putc((v&0x80)|c, stdout)
 #define putpattern(v,c) putc(0, stdout);putc(c, stdout);putc(v, stdout)
 
-static void putinit ARGS ((int rows, int cols));
-static void putbit ARGS(( bit b ));
-static void putitem ARGS(( void ));
-static void putrow ARGS(( void ));
-static void flushrow ARGS ((void));
-static void putstring ARGS((register unsigned char *p, register int n));
-
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int rows, cols, format, row, col;
-
-    pbm_init( &argc, argv );
-
-    if ( argc > 2 )
-	pm_usage( "[pbmfile]" );
-
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
-
-    pbm_readpbminit( ifp, &cols, &rows, &format );
-
-    bitrow = pbm_allocrow( cols );
-
-    putinit (rows, cols);
-    for ( row = 0; row < rows; ++row )
-	{
-#ifdef DEBUG
-	fprintf (stderr, "row %d\n", row);
-#endif
-	pbm_readpbmrow( ifp, bitrow, cols, format );
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-	    putbit( *bP );
-        putrow( );
-        }
-    flushrow ();
-
-    pm_close( ifp );
-
-
-    exit( 0 );
-    }
-
 static short item;
 static int outcol, outmax;
 static short bitsperitem, bitshift;
@@ -107,9 +63,9 @@ static short linerepeat;
 static unsigned char *outrow, *lastrow;
 
 static void
-putinit (rows, cols)
-     int rows, cols;
+putinit (int const rows, int const cols)
 {
+
   if (pm_writebigshort (stdout, (short) 1) == -1 /* Image file version */
       || pm_writebigshort (stdout, (short) 8) == -1 /* Header length */
       || pm_writebigshort (stdout, (short) 1) == -1 /* Number of planes */
@@ -130,17 +86,6 @@ putinit (rows, cols)
 }
 
 static void
-putbit( bit b )
-    {
-    if ( bitsperitem == 8 )
-	putitem( );
-    ++bitsperitem;
-    if ( b == PBM_BLACK )
-	item += 1 << bitshift;
-    --bitshift;
-    }
-
-static void
 putitem( )
     {
     outrow[outcol++] = item;
@@ -149,19 +94,93 @@ putitem( )
     bitshift = 7;
     }
 
+
+static void
+putbit( bit const b )
+    {
+    if ( bitsperitem == 8 )
+        putitem( );
+    ++bitsperitem;
+    if ( b == PBM_BLACK )
+        item += 1 << bitshift;
+    --bitshift;
+    }
+
+
 static void
-putstring (p, n)
-register unsigned char *p;
-register int n;
+putstring ( unsigned char *p, int n)
 {
 #ifdef DEBUG
     fprintf (stderr, "Bitstring, length: %d, pos %d\n", n, outcol);
 #endif
     (void) putc((char) 0x80, stdout);     /* a Bit string */
-    (void) putc(n, stdout);	/* count */
+    (void) putc(n, stdout);     /* count */
     fwrite( p, n, 1, stdout );
 }
 
+
+static void
+flushrow( )
+    {
+    unsigned char *outp, *p, *q;
+    int count;
+    int col = outmax;
+
+    if (linerepeat > 1)
+      {
+        /* Put out line repeat count */
+        fwrite ("\0\0\377", 3, 1, stdout);
+        putchar (linerepeat);
+      }
+    for (outp = p = lastrow; col > 0;)
+    {
+            for (q = p, count=0; (count < col) && (*q == *p); q++,count++);
+            if (count > MINRUN)
+            {
+                if (p > outp)
+                {
+                    putstring (outp, p-outp);
+                    outp = p;
+                }
+                col -= count;
+                switch (*p)
+                {
+                case SOLID_0:
+#ifdef DEBUG
+/*                      if (outcol > 0) */
+                        fprintf (stderr, "Solid run 0, length: %d\n", count);
+#endif
+                        putsolid (SOLID_0, count);
+                        break;
+
+                case SOLID_1:
+#ifdef DEBUG
+                        fprintf (stderr, "Solid run 1, length: %d, pos %d\n", count, outcol);
+#endif
+                        putsolid (SOLID_1, count);
+                        break;
+                default:
+#ifdef DEBUG
+                        fprintf (stderr, "Pattern run, length: %d\n", count);
+#endif
+                        putpattern (*p, count);
+                        break;
+                }
+                outp = p = q;
+            }
+            else
+            {
+                p++;
+                col--;
+            }
+    }           
+    if (p > outp)
+         putstring (outp, p-outp);
+    if (ferror (stdout))
+      pm_error ("write error");
+}
+
+
 static void
 putrow( )
 {
@@ -173,7 +192,7 @@ putrow( )
     {
       unsigned char *temp;
       if (linerepeat != -1) /* Unless first line */
-	flushrow ();
+        flushrow ();
       /* Swap the pointers */
       temp = outrow; outrow = lastrow; lastrow = temp;
       linerepeat = 1;
@@ -183,64 +202,47 @@ putrow( )
     linerepeat++;
 }
 
-static void
-flushrow( )
-    {
-    register unsigned char *outp, *p, *q;
-    register int count;
-    int col = outmax;
 
-    if (linerepeat > 1)
-      {
-	/* Put out line repeat count */
-	fwrite ("\0\0\377", 3, 1, stdout);
-	putchar (linerepeat);
-      }
-    for (outp = p = lastrow; col > 0;)
+int
+main( int argc, char* argv[])
     {
-	    for (q = p, count=0; (count < col) && (*q == *p); q++,count++);
-	    if (count > MINRUN)
-	    {
-		if (p > outp)
-		{
-		    putstring (outp, p-outp);
-		    outp = p;
-		}
-		col -= count;
-		switch (*p)
-		{
-		case SOLID_0:
-#ifdef DEBUG
-/*			if (outcol > 0) */
-			fprintf (stderr, "Solid run 0, length: %d\n", count);
-#endif
-			putsolid (SOLID_0, count);
-			break;
+    FILE* ifp;
+    bit* bitrow;
+    int rows, cols, format, row, col;
 
-		case SOLID_1:
-#ifdef DEBUG
-			fprintf (stderr, "Solid run 1, length: %d, pos %d\n", count, outcol);
-#endif
-			putsolid (SOLID_1, count);
-			break;
-		default:
+    pbm_init( &argc, argv );
+
+    if ( argc > 2 )
+        pm_usage( "[pbmfile]" );
+
+    if ( argc == 2 )
+        ifp = pm_openr( argv[1] );
+    else
+        ifp = stdin;
+
+    pbm_readpbminit( ifp, &cols, &rows, &format );
+
+    if( rows>INT16MAX || cols>INT16MAX )
+      pm_error ("Input image is too large.");
+
+
+    bitrow = pbm_allocrow( cols );
+
+    putinit (rows, cols);
+    for ( row = 0; row < rows; ++row )
+        {
 #ifdef DEBUG
-			fprintf (stderr, "Pattern run, length: %d\n", count);
+        fprintf (stderr, "row %d\n", row);
 #endif
-			putpattern (*p, count);
-			break;
-		}
-		outp = p = q;
-	    }
-	    else
-	    {
-		p++;
-		col--;
-	    }
-    }		
-    if (p > outp)
-         putstring (outp, p-outp);
-    if (ferror (stdout))
-      pm_error ("write error");
-}
+        pbm_readpbmrow( ifp, bitrow, cols, format );
+        for ( col = 0; col < cols; ++col )
+            putbit( bitrow[col] );
+        putrow( );
+        }
+    flushrow ();
+
+    pm_close( ifp );
 
+
+    exit( 0 );
+    }
diff --git a/converter/pbm/pbmtoibm23xx.c b/converter/pbm/pbmtoibm23xx.c
index 334b649d..183d5419 100644
--- a/converter/pbm/pbmtoibm23xx.c
+++ b/converter/pbm/pbmtoibm23xx.c
@@ -84,7 +84,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = 0;
     opt.allowNegNum = 0;
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!xresSpec)
diff --git a/converter/pbm/pbmtolj.c b/converter/pbm/pbmtolj.c
index be28f635..0cceb4fe 100644
--- a/converter/pbm/pbmtolj.c
+++ b/converter/pbm/pbmtolj.c
@@ -94,7 +94,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 == 0) 
@@ -162,7 +162,7 @@ putinit(struct cmdlineInfo const cmdline) {
     /* Set raster graphics resolution */
     printf("\033*t%dR", cmdline.dpi);
 
-    /* Start raster graphics, relative adressing */
+    /* Start raster graphics, relative addressing */
     printf("\033*r1A");
 
     bitsperitem = 1;
@@ -545,7 +545,7 @@ main(int argc, char * argv[]) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
-    bool eof;
+    int eof;
 
     pbm_init(&argc, argv);
 
diff --git a/converter/pbm/pbmtomacp.c b/converter/pbm/pbmtomacp.c
index 600cc407..df5cbb0c 100644
--- a/converter/pbm/pbmtomacp.c
+++ b/converter/pbm/pbmtomacp.c
@@ -1,296 +1,443 @@
-/* pbmtomacp.c - read a portable bitmap and produce a MacPaint bitmap file
-**
-** Copyright (C) 1988 by Douwe vand der Schaaf.
-**
-** 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.
+/*=============================================================================
+                                  pbmtomacp
+===============================================================================
+  Read a PBM file and produce a MacPaint bitmap file
+
+  Copyright (C) 2015 by Akira Urushibata ("douso").
+
+  Replacement of a previous program of the same name written in 1988
+  by Douwe van der Schaaf (...!mcvax!uvapsy!vdschaaf).
+
+  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.
+=============================================================================*/
+
+/*
+
+  Implemention notes
+
+  Header size is 512 bytes.  There is no MacBinary header.
+
+  White margin which is added for input files with small dimensions
+  is treated separately from the active image raster.  The margins
+  are directly coded based on the number of rows/columns.
+
+  Output file size never exceeds 53072 bytes.  When -norle is specified,
+  output is always 53072 bytes.  It is conceivable that decoders which
+  examine the size of Macpaint files (for general validation or for
+  determination of header type and size) do exist.
+
+  The uncompressed output (-norle case) fully conforms to Macpaint
+  specifications.  No special treatment by the decoder is required.
 */
 
-#include <string.h>
+#include <assert.h>
 
 #include "pm_c_util.h"
 #include "pbm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "runlength.h"
 #include "macp.h"
 
-#define TRUE		1
-#define FALSE		0
-#define EQUAL		1
-#define UNEQUAL		0
-
 #define MIN3(a,b,c)     (MIN((MIN((a),(b))),(c)))
 
-static void fillbits ARGS(( bit **bits, bit **bitsr, int top, int left, int bottom, int right ));
-static void writemacp ARGS(( bit **bits ));
-static int packit ARGS(( bit *pb, bit *bits ));
-static void filltemp ARGS(( bit *dest, bit *src ));
-static void sendbytes ARGS(( bit *pb, register int npb ));
-static void header ARGS(( void ));
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line, in a form
+       easy for the program to use.
+    */
+    const char * inputFileName;  /* File name of input file */
+    unsigned int left;
+    unsigned int right;
+    unsigned int top;
+    unsigned int bottom;
+    unsigned int leftSpec;
+    unsigned int rightSpec;
+    unsigned int topSpec;
+    unsigned int bottomSpec;
+    bool         norle;
+};
 
-static FILE *fdout;
 
-int
-main(argc, argv)
-int argc;
-char *argv[];
-{ FILE *ifp;
-  register bit **bits, **bitsr;
-  int argn, rows, cols;
-  int left,bottom,right,top;
-  int lflg, rflg, tflg, bflg;
-  const char * const usage = "[-l left] [-r right] [-b bottom] [-t top] [pbmfile]";
-
-
-  pbm_init( &argc, argv );
-
-  argn = 1;
-  fdout = stdout;
-  lflg = rflg = tflg = bflg = 0;
-  left = right = top = bottom = 0;  /* To quiet compiler warning */
-
-  while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-  { switch ( argv[argn][1] )
-    { case 'l':
-      lflg++;
-      argn++;
-      left = atoi( argv[argn] );
-      break;
-
-      case 'r':
-      rflg++;
-      argn++;
-      right = atoi( argv[argn] );
-      break;
-
-      case 't':
-      tflg++;
-      argn++;
-      top = atoi( argv[argn] );
-      break;
-
-      case 'b':
-      bflg++;
-      argn++;
-      bottom = atoi( argv[argn] );
-      break;
-
-      case '?':
-      default:
-      pm_usage( usage );
+
+static void
+parseCommandLine(int                        argc,
+                 const char **        const 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.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;  /* malloc'ed */
+        /* Instructions to OptParseOptions3 on how to parse our options.  */
+    optStruct3 opt;
+
+    unsigned int norleSpec;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "left",     OPT_UINT,  &cmdlineP->left,
+            &cmdlineP->leftSpec,     0);
+    OPTENT3(0, "right",    OPT_UINT,  &cmdlineP->right,
+            &cmdlineP->rightSpec,    0);
+    OPTENT3(0, "top",      OPT_UINT,  &cmdlineP->top,
+            &cmdlineP->topSpec,      0);
+    OPTENT3(0, "bottom",   OPT_UINT,  &cmdlineP->bottom,
+            &cmdlineP->bottomSpec,   0);
+    OPTENT3(0, "norle", OPT_FLAG,  NULL,
+            &norleSpec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    cmdlineP->norle = norleSpec;
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+        if (argc-1 > 1)
+            pm_error("Program takes zero or one argument (filename).  You "
+                     "specified %d", argc-1);
     }
-    ++argn;
-  }
 
-  if ( argn == argc )
-  { ifp = stdin;
-  }
-  else
-  { ifp = pm_openr( argv[argn] );
-    ++argn;
-  }
+    free(option_def);
+}
 
-  if ( argn != argc )
-    pm_usage( usage );
 
-  bitsr = pbm_readpbm( ifp, &cols, &rows );
 
-  pm_close( ifp );
+struct CropPadDimensions {
+    unsigned int imageWidth;   /* Active image content */
+    unsigned int imageHeight;
+    unsigned int leftCrop;     /* Cols cropped off from input */
+    unsigned int topCrop;      /* Rows cropped off from input */
+    unsigned int topMargin;    /* White padding for output */
+    unsigned int bottomMargin;
+    unsigned int leftMargin;
+};
 
-  bits = pbm_allocarray( MAX_COLS, MAX_LINES );
 
-  if( !lflg )
-    left = 0;
 
-  if( rflg )
-    right = MIN3( right, cols - 1, left + MAX_COLS - 1 );
-  else
-    right = MIN( cols - 1,  left + MAX_COLS - 1 );
+static void
+calculateCropPad(struct CmdlineInfo         const cmdline,
+                 unsigned int               const cols,
+                 unsigned int               const rows,
+                 struct CropPadDimensions * const cropPadP) {
+/*--------------------------------------------------------------------------
+  Validate -left -right -top -bottom from command line.
+
+  Determine what rows, columns to take from input if any of these are
+  specified and return it as *cropPadP.
+
+  'cols and 'rows' are the dimensions of the input image.
+
+  Center image if it is smaller than the fixed Macpaint format size.
+----------------------------------------------------------------------------*/
+    unsigned int const left = cmdline.leftSpec ? cmdline.left : 0;
+    unsigned int const top  = cmdline.topSpec  ? cmdline.top  : 0;
+
+    unsigned int right, bottom, width, height;
+
+    if (cmdline.leftSpec) {
+        if (cmdline.rightSpec && left >= cmdline.right)
+            pm_error("-left value must be smaller than -right value");
+        else if (left + 1 > cols)
+            pm_error("Specified -left value is beyond right edge "
+                     "of input image");
+    }
+    if (cmdline.topSpec) {
+        if (cmdline.bottomSpec && top >= cmdline.bottom)
+            pm_error("-top value must be smaller than -bottom value");
+        else if (top + 1 > rows)
+            pm_error("Specified -top value is beyond bottom edge "
+                     "of input image");
+    }
+    if (cmdline.rightSpec) {
+        if (cmdline.right + 1 > cols)
+            pm_message("Specified -right value %u is beyond edge of "
+                       "input image", cmdline.right);
+
+        right = MIN3(cmdline.right, cols - 1, left + MACP_COLS - 1);
+    } else
+        right = MIN(cols - 1,  left + MACP_COLS - 1);
 
-  if( !tflg )
-    top = 0;
+    if (cmdline.bottomSpec) {
+        if (cmdline.bottom + 1 > rows)
+            pm_message("Specified -bottom value %u is beyond edge of "
+                       "input image", cmdline.bottom);
 
-  if( bflg )
-    bottom = MIN3( bottom, rows - 1, top + MAX_LINES - 1);
-  else
-    bottom = MIN( rows - 1, top + MAX_LINES - 1 );
+            bottom = MIN3(cmdline.bottom, rows - 1, top + MACP_ROWS - 1);
+    } else
+        bottom = MIN(rows - 1, top + MACP_ROWS - 1);
 
-    if( right <= left || left < 0 || right - left + 1 > MAX_COLS )
-      pm_error("error in right (= %d) and/or left (=%d)",right,left );
-    if( bottom <= top || top < 0 || bottom - top + 1 > MAX_LINES )
-      pm_error("error in bottom (= %d) and/or top (=%d)",bottom,top );
+    cropPadP->leftCrop = left;
+    cropPadP->topCrop  = top;
 
-  fillbits( bits, bitsr, top, left, bottom, right );
+    assert(right >= left);
 
-  writemacp( bits );
+    width = right - left + 1;
+    assert(width > 0 && width <= MACP_COLS);
 
-  exit( 0 );
+    cropPadP->leftMargin = (MACP_COLS - width) / 2;
 
+    if (width < cols)
+        pm_message("%u of %u input columns will be output", width, cols);
+
+    height = bottom - top + 1;
+    assert(height > 0 && height <= MACP_ROWS);
+
+    cropPadP->topMargin    = (MACP_ROWS - height) / 2;
+    cropPadP->bottomMargin = cropPadP->topMargin + height - 1;
+
+    if (height < rows)
+        pm_message("%u out of %u input rows will be output", height, rows);
+
+    cropPadP->imageWidth  = width;
+    cropPadP->imageHeight = height;
 }
 
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
 
-/* centreer het over te zenden plaatje in het MacPaint document
- *
- * Het plaatje wordt vanaf al of niet opgegeven (left, bottom)
- * in een pbm bitmap van de juist macpaint afmetingen gezet,
- * en eventueel afgekapt.
- */
+
 static void
-fillbits( bits, bitsr, top, left, bottom, right )
-bit **bits, **bitsr;
-int top, left, bottom, right;
-{ register bit *bi, *bir;
-  register int i, j;
-  register int bottomr, leftr, topr, rightr;
-  int width, height;
-
-  width = right - left + 1;
-  leftr = (MAX_COLS - width) / 2;
-  rightr = leftr + width - 1;
-
-  height = bottom - top + 1;
-  topr = ( MAX_LINES - height ) / 2;
-  bottomr = topr + height - 1;
-
-  for( i = 0; i < topr; i++ )
-  { bi = bits[i];
-    for( j = 0; j < MAX_COLS; j++ )
-      *bi++ = 0;
-  }
-
-  for( i = topr; i <= bottomr; i++ )
-  { bi = bits[i];
-    { for( j = 0; j < leftr; j++ )
-	*bi++ = 0;
-      bir = bitsr[ i - topr + top ];
-      for( j = leftr; j <= rightr; j++ )
-	*bi++ = bir[j - leftr + left];
-      for( j = rightr + 1; j < MAX_COLS; j++ )
-	*bi++ = 0;
-  } }
-
-  for( i = bottomr + 1; i < MAX_LINES; i++ )
-  { bi = bits[i];
-    for( j = 0; j < MAX_COLS; j++ )
-      *bi++ = 0;
-  }
-} /* fillbits */
-      
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+writeMacpHeader(FILE * const ofP) {
+
+    char const ch = 0x00;    /* header contains nothing */
+
+    unsigned int i;
+
+    for (i = 0; i < MACP_HEAD_LEN; ++i)
+        fputc(ch, ofP);
+}
+
+
 
 static void
-writemacp( bits )
-bit **bits;
-{ register int i;
-  bit pb[MAX_COLS * 2];
-  int npb;
-
-  header();
-  for( i=0; i < MAX_LINES; i++ )
-  { npb = packit( pb, bits[i] );
-    sendbytes( pb, npb );
-  }
-} /* writemacp */
-
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
-/* pack regel van MacPaint doc in Apple's format
- * return value = # of bytes in pb 
- */
-static int
-packit( pb, bits )
-     bit *pb, *bits;
-{ register int charcount, npb, newcount, flg;
-  bit temp[72];
-  bit *count, *srcb, *destb, save;
-
-  srcb = bits; destb = temp;
-  filltemp( destb, srcb );
-  srcb = temp;
-  destb = pb;
-  npb = 0;
-  charcount = BYTES_WIDE;
-  flg = EQUAL;
-  while( charcount ) { 
-      save = *srcb++;
-      charcount--;
-      newcount = 1;
-      while( charcount && (*srcb == save) ) { 
-          srcb++;
-          newcount++;
-          charcount--;
-      }
-      if( newcount > 2 ) { 
-          count = destb++;
-          *count = 257 - newcount;
-          *destb++ = save;
-          npb += 2;
-          flg = EQUAL;
-      } else { 
-          if( flg == EQUAL ) { 
-              count = destb++;
-              *count = newcount - 1;
-              npb++;
-          } else
-            *count += newcount;
-          while( newcount-- ) { 
-              *destb++ = save;
-              npb++;
-          }
-          flg = UNEQUAL;
-      } 
-  }
-  return npb;
-} /* packit */
-
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+writeMacpRowUnpacked(const bit  * const imageBits,
+                     unsigned int const leftMarginCharCt,
+                     unsigned int const imageColCharCt,
+                     FILE *       const ofP) {
+/*--------------------------------------------------------------------------
+  Encode (without compression) and output one row.  The row comes divided into
+  three parts: left margin, image, right margin.
+----------------------------------------------------------------------------*/
+    char const marginByte = 0x00;  /* White bits for margin */
+    unsigned int const rightMarginCharCt =
+        MACP_COLCHARS - leftMarginCharCt - imageColCharCt;
+    
+    unsigned int i;
+
+    fputc(MACP_COLCHARS - 1, ofP);
+
+    for (i = 0; i < leftMarginCharCt; ++i)
+        fputc(marginByte, ofP);
+
+    if (imageColCharCt > 0)
+        fwrite(imageBits, 1, imageColCharCt, ofP);
+
+    for (i = 0; i < rightMarginCharCt; ++i)
+        fputc(marginByte, ofP);
+}
+
+
 
 static void
-filltemp( dest, src )
-bit *dest, *src;
-{ register unsigned char ch, zero, acht;
-  register int i, j;
-
-  zero = '\0';
-  acht = 8;
-  i = BYTES_WIDE;
-  while( i-- )
-  { ch = zero; 
-    j = acht;
-    while( j-- )
-    { ch <<= 1;
-      if( *src++ )
-	ch++;
+writeMacpRowPacked(const bit  * const packedBits,
+                   unsigned int const leftMarginCharCt,
+                   unsigned int const imageColCharCt,
+                   unsigned int const rightMarginCharCt,
+                   FILE *       const ofP) {
+/*--------------------------------------------------------------------------
+  Encode one row and write it to *ofP.
+
+  As in the unpacked case, the row comes divided into three parts: left
+  margin, image, right margin.  Unlike the unpacked case we need to know both
+  the size of the packed data and the size of the right margin.
+----------------------------------------------------------------------------*/
+    char const marginByte = 0x00;  /* White bits for margin */
+
+    if (leftMarginCharCt > 0) {
+        fputc(257 - leftMarginCharCt, ofP);
+        fputc(marginByte, ofP);
     }
-    *dest++ = ch;
-  }
-} /* filltemp */
 
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
+    if (imageColCharCt > 0)
+        fwrite(packedBits, 1, imageColCharCt, ofP);
+
+    if (rightMarginCharCt > 0) {
+        fputc(257 - rightMarginCharCt, ofP);
+        fputc(marginByte, ofP);
+    }
+}
+
+
 
 static void
-sendbytes( pb, npb )
-bit *pb;
-register int npb;
-{ register bit *b;
+writeMacpRow(bit        * const imageBits,
+             unsigned int const leftMarginCharCt,
+             unsigned int const imageColCharCt,
+             bool         const norle,
+             FILE *       const ofP) {
+/*--------------------------------------------------------------------------
+  Write the row 'imageBits' to Standard Output.
+
+  Write it packed, unless packing would lead to unnecessary bloat or 'norle'
+  is true.
+----------------------------------------------------------------------------*/
+    if (norle)
+        writeMacpRowUnpacked(imageBits, leftMarginCharCt, imageColCharCt, ofP);
+    else {
+        unsigned int const rightMarginCharCt =
+            MACP_COLCHARS - leftMarginCharCt - imageColCharCt;
+        unsigned char packedBits[MACP_COLCHARS+1];
+        size_t packedImageLength;
+
+        if (pm_rlenc_maxbytes(MACP_COLCHARS, PM_RLE_PACKBITS)
+            > MACP_COLCHARS + 1)
+            pm_error("INTERNAL ERROR: RLE buffer too small");
+
+        pm_rlenc_compressbyte(imageBits, packedBits, PM_RLE_PACKBITS,
+                              imageColCharCt,  &packedImageLength);
+
+        if (packedImageLength +
+            (leftMarginCharCt  > 0 ? 1 : 0) * 2 +
+            (rightMarginCharCt > 0 ? 1 : 0) * 2
+            < MACP_COLCHARS) {
+            /* It's smaller compressed, so do that */
+            writeMacpRowPacked(packedBits, leftMarginCharCt,
+                               packedImageLength, rightMarginCharCt, ofP);
+        } else { /* Extremely rare */
+            /* It's larger compressed, so do it uncompressed.  See note
+               at top of file.
+            */
+            writeMacpRowUnpacked(imageBits, leftMarginCharCt, imageColCharCt,
+                                 ofP);
+        }
+    }
+}
 
-  b = pb;
-  while( npb-- )
-    (void) putc( *b++, fdout );
-} /* sendbytes */
 
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - */
 
 static void
-header()
-{ register int i;
-  register char ch;
-
-  /* header contains nothing ... */
-  ch = '\0';
-  for(i = 0; i < HEADER_LENGTH; i++ )
-    (void) putc( ch, fdout );
-} /* header */
+encodeRowsWithShift(bit *                    const bitrow,
+                    FILE *                   const ifP,
+                    int                      const inCols,
+                    int                      const format,
+                    bool                     const norle,
+                    struct CropPadDimensions const cropPad,
+                    FILE *                   const ofP) {
+/*--------------------------------------------------------------------------
+  Shift input rows to put only specified columns to output.  Add padding on
+  left and right if necessary.
+
+  No shift if the input image is the exact size (576 columns) of the Macpaint
+  format.  If the input image is too wide and -left was not specified, extra
+  content on the right is discarded.
+----------------------------------------------------------------------------*/
+    unsigned int const offset     =
+        (cropPad.leftMargin + 8 - cropPad.leftCrop % 8) % 8;
+    unsigned int const leftTrim   =
+        cropPad.leftMargin % 8;
+    unsigned int const rightTrim  =
+        (8 - (leftTrim + cropPad.imageWidth) % 8 ) % 8;
+    unsigned int const startChar  =
+        (cropPad.leftCrop + offset) / 8;
+    unsigned int const imageCharCt =
+        pbm_packed_bytes(leftTrim + cropPad.imageWidth);
+    unsigned int const leftMarginCharCt =
+        cropPad.leftMargin / 8;
+
+    unsigned int row;
+
+    for (row = 0; row < cropPad.imageHeight; ++row) {
+        pbm_readpbmrow_bitoffset(ifP, bitrow, inCols, format, offset);
+        
+        /* Trim off fractional margin portion in first byte of image data */
+        if (leftTrim > 0) {
+            bitrow[startChar] <<= leftTrim;
+            bitrow[startChar] >>= leftTrim;
+        }
+        /* Do the same with bits in last byte of relevant image data */
+        if (rightTrim > 0) {
+            bitrow[startChar + imageCharCt - 1] >>= rightTrim;
+            bitrow[startChar + imageCharCt - 1] <<= rightTrim;
+        }
+
+        writeMacpRow(&bitrow[startChar], leftMarginCharCt,
+                     imageCharCt, norle, ofP);
+    }
+}
+
+
+
+static void
+writeMacp(unsigned int             const cols,
+          unsigned int             const rows,
+          int                      const format,
+          FILE *                   const ifP,
+          bool                     const norle,
+          struct CropPadDimensions const cropPad,
+          FILE *                   const ofP) {
+
+    unsigned int row, skipRow;
+    bit * bitrow;
+
+    writeMacpHeader(ofP);
+
+    /* Write top padding */
+    for (row = 0; row < cropPad.topMargin; ++row)
+        writeMacpRow(NULL, MACP_COLCHARS, 0, norle, ofP);
+
+    /* Allocate PBM row with one extra byte for the shift case. */
+    bitrow = pbm_allocrow_packed(cols + 8);
+
+    for (skipRow = 0; skipRow < cropPad.topCrop; ++skipRow)
+        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+
+    encodeRowsWithShift(bitrow, ifP, cols, format, norle, cropPad, ofP);
+
+    pbm_freerow_packed(bitrow);
+
+    /* Add bottom padding */
+    for (row = cropPad.bottomMargin + 1; row < MACP_ROWS; ++row)
+        writeMacpRow(NULL, MACP_COLCHARS, 0, norle, ofP);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    FILE * ifP;
+    int rows, cols;
+    int format;
+    struct CmdlineInfo cmdline;
+    struct CropPadDimensions cropPad;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    calculateCropPad(cmdline, cols, rows, &cropPad);
+
+    writeMacp(cols, rows, format, ifP, cmdline.norle, cropPad, stdout);
+
+    pm_close(ifP);
+
+    return 0;
+}
+
diff --git a/converter/pbm/pbmtomatrixorbital.c b/converter/pbm/pbmtomatrixorbital.c
index 96e1406a..41f8e260 100644
--- a/converter/pbm/pbmtomatrixorbital.c
+++ b/converter/pbm/pbmtomatrixorbital.c
@@ -1,3 +1,5 @@
+#include <stdio.h>
+
 #include "pbm.h"
 
 /* By Bryan Henderson, San Jose CA 2003.09.06.
@@ -12,10 +14,10 @@
 
 
 static void
-generateMo(FILE * const ofP, 
-           bit ** const bits,
-           int    const cols,
-           int    const rows) {
+generateMo(FILE *       const ofP, 
+           bit **       const bits,
+           unsigned int const cols,
+           unsigned int const rows) {
 
     unsigned int col;
 
@@ -51,37 +53,40 @@ generateMo(FILE * const ofP,
 
 
 int
-main(int argc, char * argv[]) {
+main(int argc, const char ** argv) {
 
-    FILE* ifp;
-    bit** bits;
+    FILE * ifP;
+    bit ** bits;
     int rows, cols;
     const char * inputFilename;
 
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     if (argc-1 > 1)
-        pm_error("Too many arguments (%d).  The only valid argument is an "
+        pm_error("Too many arguments (%u).  The only valid argument is an "
                  "input file name.", argc-1);
     else if (argc-1 == 1) 
         inputFilename = argv[1];
     else
         inputFilename = "-";
 
-    ifp = pm_openr(inputFilename);
+    ifP = pm_openr(inputFilename);
     
-    bits = pbm_readpbm(ifp, &cols, &rows);
+    bits = pbm_readpbm(ifP, &cols, &rows);
 
     if (rows > 255)
-        pm_error("Image is too high:  %d rows.  Max height: 255 rows", rows);
+        pm_error("Image is too high:  %u rows.  Max height: 255 rows", rows);
     if (cols > 255)
-        pm_error("Image is too wide:  %d cols.  Max width: 255 cols", cols);
+        pm_error("Image is too wide:  %u cols.  Max width: 255 cols", cols);
 
     generateMo(stdout, bits, cols, rows);
     
-    pm_close(ifp);
+    pm_close(ifP);
 
     pbm_freearray(bits, rows);
 
-    exit(0);
+    return 0;
 }
+
+
+
diff --git a/converter/pbm/pbmtomgr.c b/converter/pbm/pbmtomgr.c
index d12e6635..e8e30148 100644
--- a/converter/pbm/pbmtomgr.c
+++ b/converter/pbm/pbmtomgr.c
@@ -89,11 +89,7 @@ main(int argc,
         size_t bytesWritten;
 
         pbm_readpbmrow_packed(ifP, bitrow, cols, format);
-        
-        if (padright > 0) {
-            bitrow[bytesPerRow-1] >>= padright;
-            bitrow[bytesPerRow-1] <<= padright;
-        }
+        pbm_cleanrowend_packed(bitrow, cols);
 
         bytesWritten = fwrite(bitrow, 1, bytesPerRow, stdout);
         if (bytesWritten != bytesPerRow )
diff --git a/converter/pbm/pbmtonokia.c b/converter/pbm/pbmtonokia.c
index b8057393..bf3b9e41 100644
--- a/converter/pbm/pbmtonokia.c
+++ b/converter/pbm/pbmtonokia.c
@@ -4,6 +4,7 @@
    Copyright information is at end of file.
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
 #include <string.h>
 #include <assert.h>
@@ -45,7 +46,7 @@ uppercase(const char * const subject) {
 
     if (buffer == NULL)
         pm_error("Out of memory allocating buffer for uppercasing a "
-                 "%u-character string", strlen(subject));
+                 "%u-character string", (unsigned)strlen(subject));
     else {
         unsigned int i;
 
@@ -69,7 +70,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -92,7 +93,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (fmtSpec) {
@@ -117,8 +118,8 @@ parseCommandLine(int argc, char ** argv,
     if (netSpec) {
         if (strlen(netOpt) != 6)
             pm_error("-net option must be 6 hex digits long.  "
-                     "You specified %u characters", strlen(netOpt));
-        else if (!strishex(netOpt))
+                     "You specified %u characters", (unsigned)strlen(netOpt));
+        else if (!pm_strishex(netOpt))
             pm_error("-net option must be hexadecimal.  You specified '%s'",
                      netOpt);
         else
@@ -131,7 +132,7 @@ parseCommandLine(int argc, char ** argv,
     else if (strlen(cmdlineP->txt) > 120)
         pm_error("Text message is longer (%u characters) than "
                  "the 120 characters allowed by the format.",
-                 strlen(cmdlineP->txt));
+                 (unsigned)strlen(cmdlineP->txt));
 
     if (argc-1 == 0) 
         cmdlineP->inputFileName = "-";
@@ -147,7 +148,7 @@ parseCommandLine(int argc, char ** argv,
 static void
 freeCmdline(struct cmdlineInfo const cmdline) {
 
-    strfree(cmdline.networkCode);
+    pm_strfree(cmdline.networkCode);
 }
 
 
@@ -253,7 +254,7 @@ convertToHexNpm(bit **       const image,
 
         unsigned int it;
 
-        fprintf(ofP, "00%04X", len);
+        fprintf(ofP, "00%04X", (unsigned)len);
 
         for (it = 0; it < len; ++it)
             fprintf(ofP, "%02X", text[it]);
diff --git a/converter/pbm/pbmtopi3.c b/converter/pbm/pbmtopi3.c
index 1dbf1a71..791bcb50 100644
--- a/converter/pbm/pbmtopi3.c
+++ b/converter/pbm/pbmtopi3.c
@@ -1,4 +1,4 @@
-/* pbmtopi3.c - read a portable bitmap and produce a Atari Degas .pi3 file
+/* pbmtopi3.c - read a PBM image and produce a Atari Degas .pi3 file
 **
 ** Module created from other pbmplus tools by David Beckemeyer.
 **
@@ -12,107 +12,87 @@
 ** implied warranty.
 */
 
+/* Output file should always be 32034 bytes. */
+
 #include <stdio.h>
-#include "pbm.h"
 #include "pm_c_util.h"
+#include "pbm.h"
 
-static void putinit ARGS(( void ));
-static void putbit ARGS(( bit b ));
-static void putrest ARGS(( void ));
-static void putitem ARGS(( void ));
-
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int inrows, incols, format, padright, row, col;
-    int const outcols = 640;
-    int const outrows = 400;
-
-    pbm_init( &argc, argv );
-
-    if ( argc > 2 )
-	pm_usage( "[pbmfile]" );
-
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
-
-    pbm_readpbminit( ifp, &incols, &inrows, &format );
-    bitrow = pbm_allocrow( MAX(incols, outcols) );
-
-    /* Compute padding to round cols up to 640 */
-    if(incols < outcols)
-        padright = outcols - incols;
-    else 
-        padright = 0;
-
-    putinit( );
-    for ( row = 0; row < MIN(inrows, outrows); ++row )
-	{
-	pbm_readpbmrow( ifp, bitrow, incols, format );
-        for ( col = 0, bP = bitrow; col < MIN(incols, outcols); ++col, ++bP )
-	    putbit( *bP );
-	for ( col = 0; col < padright; ++col )
-	    putbit( 0 );
-        }
-    while (row++ < outrows)
-	for ( col = 0; col < outcols; ++col)
-	    putbit( 0 );
-
-    pm_close( ifp );
-
-    putrest( );
-
-    exit( 0 );
-    }
 
-static char item;
-static short bitsperitem, bitshift;
 
 static void
-putinit( )
-    {
-    int i;
-    if (pm_writebigshort (stdout, (short) 2) == -1
-	|| pm_writebigshort (stdout, (short) 0x777) == -1)
-      pm_error ("write error");
-    for (i = 1; i < 16; i++)
-      if (pm_writebigshort (stdout, (short) 0) == -1)
-	pm_error ("write error");
-    item = 0;
-    bitsperitem = 0;
-    bitshift = 7;
+putinit(FILE * const ofP)  {
+
+    unsigned int i;
+
+    pm_writebigshort(ofP, (short) 2);
+    pm_writebigshort(ofP, (short) 0x777);
+
+    for (i = 1; i < 16; ++i) {
+        pm_writebigshort (ofP, (short) 0);
     }
+}
 
-static void
-putbit( bit b )
-    {
-    if (bitsperitem == 8)
-	putitem( );
-    ++bitsperitem;
-    if ( b == PBM_BLACK )
-	item += 1 << bitshift;
-    --bitshift;
+
+
+int
+main(int argc, const char ** argv) {
+
+    unsigned int const outRows = 400;
+    unsigned int const outCols = 640;
+    unsigned int const outColByteCt = pbm_packed_bytes(outCols);
+
+    FILE * ifP;
+
+    int inRows, inCols, format;
+    unsigned int row;
+    unsigned int inColByteCt;
+    unsigned int i;
+    bit * bitrow;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        ifP = stdin;
+    else  {
+        ifP = pm_openr(argv[1]);
+
+        if (argc-1 > 1)
+            pm_error("Too many arguments.  The only possible argument "
+                     "is the input file name");
     }
 
-static void
-putrest( )
-    {
-    if ( bitsperitem > 0 )
-	putitem( );
+    pbm_readpbminit(ifP, &inCols, &inRows, &format);
+
+    inColByteCt = pbm_packed_bytes(inCols);
+
+    bitrow = pbm_allocrow_packed(MAX(outCols, inCols));
+    
+    /* Add padding to round cols up to 640 */
+    for (i = inColByteCt; i < outColByteCt; ++i)
+        bitrow[i] = 0x00;
+
+    putinit(stdout);
+
+    for (row = 0; row < MIN(inRows, outRows); ++row) {
+        pbm_readpbmrow_packed(ifP, bitrow, inCols, format);
+        pbm_cleanrowend_packed(bitrow, inCols);
+        fwrite (bitrow, outColByteCt, 1, stdout);
     }
+    pm_close(ifP);
 
-static void
-putitem( )
-    {
-    putc (item, stdout);
-    item = 0;
-    bitsperitem = 0;
-    bitshift = 7;
+    if (row < outRows)  {
+        unsigned int i;
+
+        /* Clear entire row */
+        for (i = 0; i < outColByteCt; ++i)
+            bitrow[i] = 0x00;
+
+        while (row++ < outRows)
+            fwrite(bitrow, outColByteCt, 1, stdout);
     }
+
+    pbm_freerow_packed(bitrow);
+
+    return 0;
+}
diff --git a/converter/pbm/pbmtopk.c b/converter/pbm/pbmtopk.c
index fc94f855..3948ae0d 100644
--- a/converter/pbm/pbmtopk.c
+++ b/converter/pbm/pbmtopk.c
@@ -1,7 +1,12 @@
 /*
   pbmtopk, adapted from "pxtopk.c by tomas rokicki" by AJCD 1/8/90
   
-  compile with: cc -o pbmtopk pbmtopk.c -lm -lpbm
+  References (retrieved May 31 2015):
+  Packed (PK) Font File Format 
+  https://www.tug.org/TUGboat/tb06-3/tb13pk.pdf
+
+  Tex Font Metric Files (TFM)
+  https://www.tug.org/TUGboat/tb06-1/tb11gf.pdf
 */
 
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
@@ -26,6 +31,29 @@
 #define MAXPARAMS 30
 #define NAMELENGTH 80
 
+/*-----------------------------------------------------------------------
+Macros to handle fixed point numbers
+
+This program uses uses fixed-point numbers to store data where
+normally a floating-point data type (float or double) would be
+employed.
+
+Numbers that contain fractions are stored as signed integers.
+The 20 least-significant bits are for the fractional part, the rest
+(12 bits assuming that int is 32 bit) are for the integer part.
+The technical term for this is "Q20" or "Q12.20" notation.
+
+Float/double data is converted to Q20 fixed point by multiplying
+by 2^20 (= 1048576).  The opposite conversion is conducted by
+dividing by 2^20.
+
+The Q20 data must be within the range -16 < r < 16.  The reason
+behind this restriction is unclear.  The program generally writes
+Q20 data to the output files in 32 bits.  (Exception: in function
+shipchar() there is a provision to write Q20 data in 24 bits,
+provided that 24 bits is sufficient.)
+---------------------------------------------------------------------*/
+
 #define fixword(d) ((int)((double)(d)*1048576))
 #define unfixword(f) ((double)(f) / 1048576)
 #define fixrange(f) ((f) < 16777216 && (f) > -16777216)
diff --git a/converter/pbm/pbmtoppa/Makefile b/converter/pbm/pbmtoppa/Makefile
index 5f205230..cf31ded6 100644
--- a/converter/pbm/pbmtoppa/Makefile
+++ b/converter/pbm/pbmtoppa/Makefile
@@ -9,17 +9,18 @@ include $(BUILDDIR)/config.mk
 
 all: pbmtoppa
 
-BINARIES = pbmtoppa
+PORTBINARIES = pbmtoppa
+
+BINARIES = $(PORTBINARIES)
 
 MERGEBINARIES = $(BINARIES)
 
-OBJECTS = pbmtoppa.o ppa.o pbm.o cutswath.o
-MERGE_OBJECTS = pbmtoppa.o2 ppa.o pbm.o cutswath.o
+ADDL_OBJECTS = ppa.o pbm.o cutswath.o
 
-include $(SRCDIR)/common.mk
+OBJECTS = pbmtoppa.o $(ADDL_OBJECTS)
 
-pbmtoppa: $(OBJECTS) $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o pbmtoppa $(OBJECTS) \
-	  -lm $(shell $(LIBOPT) $(NETPBMLIB)) $(LDFLAGS) $(LDLIBS) \
-	  $(RPATH) $(LADD)
+MERGE_OBJECTS = pbmtoppa.o2 $(ADDL_OBJECTS)
+
+include $(SRCDIR)/common.mk
 
+pbmtoppa: $(OBJECTS)
diff --git a/converter/pbm/pbmtopsg3.c b/converter/pbm/pbmtopsg3.c
index 54d0a0a0..8163b70a 100644
--- a/converter/pbm/pbmtopsg3.c
+++ b/converter/pbm/pbmtopsg3.c
@@ -60,7 +60,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;
     opt.allowNegNum = FALSE;
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
 
     if (argc-1 == 0)
         cmdlineP->inputFilespec = "-";
@@ -323,7 +323,7 @@ doPages(FILE *         const ifP,
         unsigned int * const pagesP,
         double         const dpi) {
 
-    bool eof;
+    int eof;
     unsigned int pagesDone;
 
     eof = FALSE;
diff --git a/converter/pbm/pbmtoptx.c b/converter/pbm/pbmtoptx.c
index 8cd60326..c0fb0f80 100644
--- a/converter/pbm/pbmtoptx.c
+++ b/converter/pbm/pbmtoptx.c
@@ -12,84 +12,79 @@
 
 #include "pbm.h"
 
-static void putinit ARGS(( void ));
-static void putbit ARGS(( bit b ));
-static void putrest ARGS(( void ));
-static void putitem ARGS(( void ));
+/* Follwing is obtained by reversing bit order (MFS-LFS) and adding 64. */
+/* Note the two escape sequences: \\ and \x7f . */
 
-int
-main( argc, argv )
-int argc;
-char *argv[];
-    {
-    FILE *ifp;
-    register bit *bitrow, *bP;
-    int rows, cols, format, row, col;
-    const char * const usage = "[pbmfile]";
-
-    pbm_init( &argc, argv );
-
-    if ( argc > 2 )
-	pm_usage( usage );
-
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
-
-    pbm_readpbminit( ifp, &cols, &rows, &format );
-    bitrow = pbm_allocrow( cols );
-
-    putinit( );
-    for ( row = 0; row < rows; row++ )
-	{
-	pbm_readpbmrow( ifp, bitrow, cols, format );
-        for ( col = 0, bP = bitrow; col < cols; col++, bP++ )
-	    putbit( *bP );
-	putrest( );
-	putchar( 5 );
-	putchar( '\n' );
-        }
+static unsigned char const ptxchar[64] = 
+  "@`PpHhXxDdTtLl\\|BbRrJjZzFfVvNn^~AaQqIiYyEeUuMm]}CcSsKk[{GgWwOo_\x7f";
 
-    pm_close( ifp );
-    
-    exit( 0 );
-    }
 
-static char item;
-static int bitsperitem, bitshift;
 
 static void
-putinit( )
-    {
-    bitsperitem = 0;
-    item = 64;
-    bitshift = 0;
-    }
+putBitrow(const bit *  const bitrow,
+          unsigned int const cols) {
+/*----------------------------------------------------------------------------
+  Pick up items in 6 bit units from bitrow and convert each to ptx format.
+----------------------------------------------------------------------------*/
+    unsigned int itemCnt;
 
-static void
-putbit( bit b )
-    {
-    if ( bitsperitem == 6 )
-	putitem( );
-    if ( b == PBM_BLACK )
-	item += 1 << bitshift;
-    bitsperitem++;
-    bitshift++;
+    for (itemCnt = 0; itemCnt * 6 < cols; ++itemCnt) {
+        unsigned int const byteCnt = (itemCnt * 6) / 8;
+        bit const byteCur  = bitrow[byteCnt];
+        bit const byteNext = bitrow[byteCnt + 1];
+        
+        unsigned int item;
+
+        switch (itemCnt % 4) {
+        case 0: item = byteCur >> 2;                 break;
+        case 1: item = byteCur << 4 | byteNext >> 4; break;
+        case 2: item = byteCur << 2 | byteNext >> 6; break;
+        case 3: item = byteCur;                      break;
+        }
+        putchar(ptxchar[item & 0x3f]);
     }
+    putchar(5); putchar('\n');  /* end of row mark */
+}
 
-static void
-putrest( )
-    {
-    if ( bitsperitem > 0 )
-	putitem( );
+
+
+int
+main(int argc, const char ** argv)  {
+
+    FILE * ifP;
+    bit * bitrow;
+    int rows, cols, format;
+    unsigned int row;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        ifP = stdin;
+    else {
+        ifP = pm_openr(argv[1]);
+        
+        if (argc-1 > 1)
+            pm_error("Too many arguments.  The only possible argument is "
+                     "the input fil name");
     }
 
-static void
-putitem( )
-    {
-    putchar( item );
-    bitsperitem = 0;
-    item = 64;
-    bitshift = 0;
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    bitrow = pbm_allocrow_packed(cols + 8);
+
+    bitrow[pbm_packed_bytes(cols)] = 0x00;
+
+    for (row = 0; row < rows; ++row) {
+        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+        pbm_cleanrowend_packed(bitrow, cols);
+        putBitrow(bitrow, cols);
     }
+
+    pbm_freerow_packed(bitrow);
+    pm_close(ifP);
+    
+    return 0;
+}
+
+
+
diff --git a/converter/pbm/pbmtoicon.c b/converter/pbm/pbmtosunicon.c
index d5fefb76..95deab7c 100644
--- a/converter/pbm/pbmtoicon.c
+++ b/converter/pbm/pbmtosunicon.c
@@ -1,4 +1,4 @@
-/* pbmtoicon.c - read a PBM image and produce a Sun icon file
+/* pbmtosunicon.c - read a PBM image and produce a Sun icon file
 **
 ** Copyright (C) 1988 by Jef Poskanzer.
 **
@@ -17,41 +17,46 @@
    Retired bitwise transformation functions.
 */
 
-#include "wordaccess.h"
+#include "pm_config.h"
 #include "pbm.h"
 
-static unsigned short int itemBuff[8];
-static unsigned int itemCnt;    /* takes values 0 to 8 */
-FILE * putFp;
+static struct ItemPutter {
+    unsigned short int itemBuff[8];
+    unsigned int       itemCnt;    /* takes values 0 to 8 */
+    FILE *             putFp;
+} ip;
 
 
 
 static void
 putinit(FILE * const ofP) {
-    putFp = ofP;
-    itemCnt = 0;
+    ip.putFp   = ofP;
+    ip.itemCnt = 0;
 }
 
 
 
 static void
-putitem(wordint const item) {
+putitem(uint16_t const item) {
 
-    if (itemCnt == 8 ) {
+    if (ip.itemCnt == 8 ) {
         /* Buffer is full.  Write out one line. */
         int rc;
     
-        rc = fprintf(putFp,
+        rc = fprintf(ip.putFp,
                      "\t0x%04x,0x%04x,0x%04x,0x%04x,"
                      "0x%04x,0x%04x,0x%04x,0x%04x,\n",
-                     itemBuff[0],itemBuff[1],itemBuff[2],itemBuff[3],
-                     itemBuff[4],itemBuff[5],itemBuff[6],itemBuff[7]);
+                     ip.itemBuff[0], ip.itemBuff[1],
+                     ip.itemBuff[2], ip.itemBuff[3],
+                     ip.itemBuff[4], ip.itemBuff[5],
+                     ip.itemBuff[6], ip.itemBuff[7]);
         if (rc < 0)        
            pm_error("fprintf() failed to write Icon bitmap");
            
-        itemCnt = 0;
+        ip.itemCnt = 0;
     }
-    itemBuff[itemCnt++] = item & 0xffff;  /* Only lower 16 bits are used */
+    ip.itemBuff[ip.itemCnt++] = item & 0xffff;
+        /* Only lower 16 bits are used */
 }
 
 
@@ -61,10 +66,11 @@ putterm(void) {
 
     unsigned int i;
 
-    for (i = 0; i < itemCnt; ++i) {
+    for (i = 0; i < ip.itemCnt; ++i) {
         int rc;
-        rc = fprintf(putFp, "%s0x%04x%c", i == 0  ? "\t" : "", itemBuff[i],
-                     i == itemCnt - 1 ? '\n' : ',');
+        rc = fprintf(ip.putFp, "%s0x%04x%c", i == 0  ? "\t" : "",
+                     ip.itemBuff[i],
+                     i == ip.itemCnt - 1 ? '\n' : ',');
         if (rc < 0)        
             pm_error("fprintf() failed to write Icon bitmap");
     }
@@ -98,58 +104,31 @@ writeIcon(FILE *       const ifP,
           int          const format,
           FILE *       const ofP) {
 
-    unsigned int const wordintSize = sizeof(wordint) * 8;
-        /* wordintSize is usually 32 or 64 bits.  Must be at least 24. */
     unsigned int const items = (cols + 15) / 16;
-    unsigned int const bitrowBytes = pbm_packed_bytes(cols);
     unsigned int const pad = items * 16 - cols;
-    /* 'padleft' is added to the output.  'padbyte' is for cleaning
-       the input
-    */
-    unsigned int const padleft = pad / 2;
-    unsigned int const padbyte = bitrowBytes * 8 - cols;
-    unsigned int const shift   = (wordintSize - 24) + padleft;
-    
-    unsigned char * bitbuffer;
-    unsigned char * bitrow;
+
+    unsigned char * const bitrow = pbm_allocrow_packed(items * 16);
     unsigned int row;
 
-    bitbuffer = pbm_allocrow_packed(cols + wordintSize);
-    bitrow = &bitbuffer[1];
-    bitbuffer[0] = 0;
-    bitrow[bitrowBytes] = 0;
-    
+    bitrow[0] = bitrow[items * 2 - 1] = 0;
+
     writeIconHeader(ofP, cols + pad, rows);
 
     putinit(ofP);
 
     for (row = 0; row < rows; ++row) {
         unsigned int itemSeq;
-        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
 
-        /* Clear post-data junk in final partial byte */
-        if (padbyte > 0) {
-            bitrow[bitrowBytes-1] >>= padbyte;
-            bitrow[bitrowBytes-1] <<= padbyte;
-        }
-        
+        pbm_readpbmrow_bitoffset(ifP, bitrow, cols, format, pad/2);
+
         for (itemSeq = 0; itemSeq < items; ++itemSeq) {
-            /* Scoop up bits, shift-align, send to format & print function.
-    
-               An item is 16 bits, typically spread over 3 bytes due to
-               left-padding.  We use wordint here to scoop up 4 (or more)
-               consecutive bytes.  An item always resides within the higher
-               24 bits of each scoop.  It is essential to use wordint
-               (or rather the wordaccess function bytesToWordInt() ); 
-               simple long, uint_32t, etc. do not work for they are not
-               shift-tolerant.
-            */
+            /* Read bits from bitrow, send to format & print function. */
             
-            wordint const scoop = bytesToWordint(&bitbuffer[itemSeq*2]);
-            putitem (scoop >> shift);
+            putitem((bitrow[itemSeq*2]<<8) + bitrow[itemSeq*2+1]);
         }
     }
-    putterm();    
+    putterm();
+    pbm_freerow_packed(bitrow);
 }
 
 
diff --git a/converter/pbm/pbmtoxbm.c b/converter/pbm/pbmtoxbm.c
index 340642ce..14c6b85e 100644
--- a/converter/pbm/pbmtoxbm.c
+++ b/converter/pbm/pbmtoxbm.c
@@ -23,6 +23,7 @@
 #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 "pm_c_util.h"
@@ -35,7 +36,7 @@
 
 enum xbmVersion { X10, X11 };
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -46,8 +47,8 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int                 argc, 
-                 char **             argv,
-                 struct cmdlineInfo *cmdlineP ) {
+                 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.  
@@ -58,8 +59,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 pm_optParseOptions3 on how to parse our options. */
 
     optStruct3 opt;
     unsigned int option_def_index;
@@ -76,14 +77,14 @@ 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);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!nameSpec)
         cmdlineP->name = NULL;
     else if (strlen(cmdlineP->name) > 56)
         pm_error("Image name too long: %d chars. (max 56)",
-                 strlen(cmdlineP->name));
+                 (unsigned)strlen(cmdlineP->name));
     else if (!ISALPHA(cmdlineP->name[0]) && cmdlineP->name[0] !='_')
         pm_error("Image name '%s' starts with non-alphabet character.",
                   cmdlineP->name);
@@ -111,6 +112,7 @@ parseCommandLine(int                 argc,
             pm_error("Program takes zero or one argument (filename).  You "
                      "specified %u", argc-1);
     }
+    free(option_def);
 }
 
 
@@ -242,18 +244,22 @@ puttermX10(void) {
 
     unsigned int i;
 
+    assert(itemCnt % 2 == 0);
+
     for (i = 0; i < itemCnt; i += 2) {
         int rc;
 
+        assert(i + 1 < itemCnt);
+
         rc = printf("%s0x%02x%02x%s",
                     (i == 0) ? " " : "",
                     itemBuff[i+1],
                     itemBuff[i], 
-                    (i == itemCnt - 2) ? "" : ",");
+                    (i + 2 >= itemCnt) ? "" : ",");
         if (rc < 0)        
-            pm_error("Error writing end of X10 bitmap raster.  "
+            pm_error("Error writing Item %u at end of X10 bitmap raster.  "
                      "printf() failed with errno %d (%s)",
-                     errno, strerror(errno));
+                     i, errno, strerror(errno));
     }
 }
 
@@ -270,12 +276,12 @@ puttermX11(void) {
         rc = printf("%s0x%02x%s",
                     (i == 0)  ? " " : "",
                     itemBuff[i],
-                    (i == itemCnt - 1) ? "" : ",");
+                    (i + 1 >= itemCnt) ? "" : ",");
 
         if (rc < 0)        
-            pm_error("Error writing end of X11 bitmap raster.  "
+            pm_error("Error writing Item %u at end of X11 bitmap raster.  "
                      "printf() failed with errno %d (%s)",
-                     errno, strerror(errno));
+                     i, errno, strerror(errno));
     }
 }
 
@@ -319,8 +325,8 @@ writeXbmHeader(enum xbmVersion const xbmVersion,
                unsigned int    const height,
                FILE *          const ofP) {
 
-    printf("#define %s_width %d\n", name, width);
-    printf("#define %s_height %d\n", name, height);
+    printf("#define %s_width %u\n", name, width);
+    printf("#define %s_height %u\n", name, height);
     printf("static %s %s_bits[] = {\n",
            xbmVersion == X10 ? "short" : "char",
            name);
@@ -337,8 +343,7 @@ convertRaster(FILE *          const ifP,
               enum xbmVersion const xbmVersion) {
               
     unsigned int const bitsPerUnit = xbmVersion == X10 ? 16 : 8;   
-    unsigned int const padright =
-        ((cols + bitsPerUnit - 1 ) / bitsPerUnit) * bitsPerUnit - cols;
+    unsigned int const padright = ROUNDUP(cols, bitsPerUnit) - cols;
         /* Amount of padding to round cols up to the nearest multiple of 
            8 (if x11) or 16 (if x10).
         */
@@ -352,21 +357,15 @@ convertRaster(FILE *          const ifP,
     bitrow = pbm_allocrow_packed(cols + padright);
     
     for (row = 0; row < rows; ++row) {
-        int const bitrowInBytes = pbm_packed_bytes(cols);
-        int const padrightIn    = bitrowInBytes * 8 - cols;
-
         unsigned int i;
 
         pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+        pbm_cleanrowend_packed(bitrow, cols);
 
-        if (padrightIn > 0) {
-            bitrow[bitrowInBytes - 1] >>= padrightIn;
-            bitrow[bitrowInBytes - 1] <<= padrightIn;
-        }
-
-        if (padright >= 8)
+        if (padright >= 8) {
+            assert(bitrowBytes > 0);
             bitrow[bitrowBytes-1] = 0x00;
-
+        }
         for (i = 0; i < bitrowBytes; ++i)
             putitem(bitrow[i]);
     }
@@ -379,15 +378,15 @@ convertRaster(FILE *          const ifP,
 
 
 int
-main(int    argc,
-     char * argv[]) {
+main(int           argc,
+     const char ** argv) {
 
-    struct cmdlineInfo cmdline; 
+    struct CmdlineInfo cmdline; 
     FILE * ifP;
     int rows, cols, format;
     const char * name;
 
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
     if (cmdline.name == NULL) 
@@ -403,9 +402,11 @@ main(int    argc,
 
     convertRaster(ifP, cols, rows, format, stdout, cmdline.xbmVersion);
 
-    strfree(name);
+    pm_strfree(name);
     pm_close(ifP);
 
     return 0;
 }
 
+
+
diff --git a/converter/pbm/pbmtoybm.c b/converter/pbm/pbmtoybm.c
index 508e8e92..27ce6cb1 100644
--- a/converter/pbm/pbmtoybm.c
+++ b/converter/pbm/pbmtoybm.c
@@ -9,100 +9,89 @@
 ** copyright notice and this permission notice appear in supporting
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.
+**
+** Feb 2010 afu
+** Added dimension check to prevent short int from overflowing
+** Changed code style (ANSI-style function definitions, etc.)
 */
 
 #include <stdio.h>
+
+#include "pm.h"
 #include "pbm.h"
+#include "bitreverse.h"
 
 #define YBM_MAGIC  ( ( '!' << 8 ) | '!' )
+#define INT16MAX 32767
+
+static void
+putinit(int const cols,
+        int const rows) {
+
+    pm_writebigshort(stdout, YBM_MAGIC);
+    pm_writebigshort(stdout, cols);
+    pm_writebigshort(stdout, rows);
+}
+
 
-static void putinit ARGS(( int cols, int rows ));
-static void putbit ARGS(( bit b ));
-static void putrest ARGS(( void ));
-static void putitem ARGS(( void ));
 
 int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int rows, cols, format, padright, row, col;
-
-
-    pbm_init( &argc, argv );
-
-    if ( argc > 2 )
-	pm_usage( "[pbmfile]" );
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
-
-    pbm_readpbminit( ifp, &cols, &rows, &format );
-    bitrow = pbm_allocrow( cols );
+main(int argc, const char *argv[]) {
+
+    FILE * ifP;
+    bit * bitrow;
+    int rows;
+    int cols;
+    int format;
+    unsigned int row;
+    const char * inputFileName;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        inputFileName = "-";
+    else {
+        inputFileName = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("Too many arguments.  The only argument is the optional "
+                     "input file name");
+    }
+
+    ifP = pm_openr(inputFileName);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    if (rows > INT16MAX || cols > INT16MAX)
+        pm_error("Input image is too large.");
+
+    bitrow = pbm_allocrow_packed(cols + 8);
     
-    /* Compute padding to round cols up to the nearest multiple of 16. */
-    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
+    putinit(cols, rows);
+
+    bitrow[pbm_packed_bytes(cols + 8) - 1] = 0x00;
+    for (row = 0; row < rows; ++row) {
+        uint16_t *   const itemrow = (uint16_t *) bitrow;
+        unsigned int const itemCt   = (cols + 15) / 16;
 
-    putinit( cols, rows );
-    for ( row = 0; row < rows; ++row )
-	{
-	pbm_readpbmrow( ifp, bitrow, cols, format );
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-	    putbit( *bP );
-	for ( col = 0; col < padright; ++col )
-	    putbit( 0 );
-        }
+        unsigned int i;
 
-    if ( ifp != stdin )
-	fclose( ifp );
+        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+        pbm_cleanrowend_packed(bitrow, cols);
 
-    putrest( );
+        for (i = 0; i < pbm_packed_bytes(cols); ++i)
+            bitrow[i] = bitreverse[bitrow[i]];
 
-    exit( 0 );
+        for (i = 0; i < itemCt; ++i)
+            pm_writebigshort(stdout, itemrow[i]);
     }
 
-static long item;
-static int bitsperitem, bitshift;
+    pbm_freerow_packed(bitrow);
 
-static void
-putinit( cols, rows )
-    int cols, rows;
-    {
-    pm_writebigshort( stdout, YBM_MAGIC );
-    pm_writebigshort( stdout, cols );
-    pm_writebigshort( stdout, rows );
-    item = 0;
-    bitsperitem = 0;
-    bitshift = 0;
-    }
+    if (ifP != stdin)
+        fclose(ifP);
 
-static void
-putbit( bit b )
-    {
-    if ( bitsperitem == 16 )
-	putitem( );
-    ++bitsperitem;
-    if ( b == PBM_BLACK )
-	item += 1 << bitshift;
-    ++bitshift;
-    }
+    return 0;
+}
 
-static void
-putrest( )
-    {
-    if ( bitsperitem > 0 )
-	putitem( );
-    }
 
-static void
-putitem( )
-    {
-    pm_writebigshort( stdout, item );
-    item = 0;
-    bitsperitem = 0;
-    bitshift = 0;
-    }
diff --git a/converter/pbm/pbmtozinc.c b/converter/pbm/pbmtozinc.c
index 2df39f0d..a89b8c9f 100644
--- a/converter/pbm/pbmtozinc.c
+++ b/converter/pbm/pbmtozinc.c
@@ -1,4 +1,4 @@
-/* pbmtozinc.c - read a portable bitmap and produce an bitmap file
+/* pbmtozinc.c - read a PBM image and produce a bitmap file
 **               in the format used by the Zinc Interface Library (v1.0)
 **               November 1990.
 **
@@ -21,108 +21,164 @@
 #include <stdio.h>
 #include <string.h>
 
+#include "mallocvar.h"
 #include "nstring.h"
 #include "pbm.h"
 
-int
-main(int argc, char * argv[]) {
-
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int rows, cols, format, padright, row;
-    register int col;
-    char name[100];
-    char* cp;
-    int itemsperline;
-    register int bitsperitem;
-    register int item;
-    int firstitem;
-    const char * const hexchar = "084c2a6e195d3b7f";
-
-    pbm_init( &argc, argv );
-
-    if ( argc > 2 )
-        pm_usage( "[pbmfile]" );
-
-    if ( argc == 2 )
-	{
-        ifp = pm_openr( argv[1] );
-        strcpy( name, argv[1] );
-        if ( streq( name, "-" ) )
-            strcpy( name, "noname" );
-
-        if ( ( cp = strchr( name, '.' ) ) != 0 )
+static void
+parseCommandLine(int           const argc,
+                 const char ** const argv,
+                 const char ** const inputFileNameP) {
+
+    if (argc-1 > 0) {
+        *inputFileNameP = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("To many arguments: %u.  "
+                     "The only possible argument is the "
+                     "name of the input file", argc-1);
+    } else
+        *inputFileNameP = "-";
+}
+
+
+
+static const char *
+imageName(const char * const inputFileName) {
+/*----------------------------------------------------------------------------
+   The image name to put in the Zinc file, based on the input file name
+   'inputFileName' ("-" to indicate Standard Input).
+
+   Result is newly malloc'ed space that Caller must free.
+-----------------------------------------------------------------------------*/
+    const char * retval;
+
+    if (streq(inputFileName, "-"))
+        pm_asprintf(&retval, "noname");
+    else {
+        char * nameBuf;
+        char * cp;
+
+        MALLOCARRAY_NOFAIL(nameBuf, strlen(inputFileName) + 1);
+
+        strcpy(nameBuf, inputFileName);
+
+        cp = strchr(nameBuf, '.' );
+        if (cp)
             *cp = '\0';
-	}
-    else
-	{
-        ifp = stdin;
-        strcpy( name, "noname" );
-	}
-
-    pbm_readpbminit( ifp, &cols, &rows, &format );
-    bitrow = pbm_allocrow( cols );
-
-    /* Compute padding to round cols up to the nearest multiple of 16. */
-    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
-
-    printf( "USHORT %s[] = {\n",name);
-    printf( "  %d\n", cols );
-    printf( "  %d\n", rows );
-
-    itemsperline = 0;
-    bitsperitem = 0;
-    item = 0;
-    firstitem = 1;
-
-#define PUTITEM \
-    { \
-    if ( firstitem ) \
-	firstitem = 0; \
-    else \
-	putchar( ',' ); \
-    if ( itemsperline == 11 ) \
-	{ \
-	putchar( '\n' ); \
-	itemsperline = 0; \
-	} \
-    if ( itemsperline == 0 ) \
-	putchar( ' ' ); \
-    ++itemsperline; \
-    putchar('0'); \
-    putchar('x'); \
-    putchar(hexchar[item & 15]); \
-    putchar(hexchar[(item >> 4) & 15]); \
-    putchar(hexchar[(item >> 8) & 15]); \
-    putchar(hexchar[item >> 12]); \
-    bitsperitem = 0; \
-    item = 0; \
+
+        retval = nameBuf;
     }
+    return retval;
+}
 
-#define PUTBIT(b) \
-    { \
-    if ( bitsperitem == 16 ) \
-	PUTITEM; \
-    if ( (b) == PBM_BLACK ) \
-	item += 1 << bitsperitem; \
-    ++bitsperitem; \
+
+
+typedef struct {
+    unsigned int itemsperline;
+    uint16_t     item;
+    unsigned int firstitem;
+} Packer;
+
+
+
+static void
+packer_init(Packer * const packerP) {
+
+    packerP->itemsperline = 0;
+    packerP->firstitem = 1;
+}
+
+
+
+static void
+packer_putitem(Packer *      const packerP,
+               unsigned char const hi,
+               unsigned char const lo) {
+
+    if (packerP->firstitem)
+        packerP->firstitem = 0;
+    else
+        putchar(',');
+
+    if (packerP->itemsperline == 11) {
+        putchar('\n');
+        packerP->itemsperline = 0;
     }
+    if (packerP->itemsperline == 0)
+        putchar(' ');
+
+    ++packerP->itemsperline;
 
-    for ( row = 0; row < rows; ++row )
-	{
-        pbm_readpbmrow( ifp, bitrow, cols, format );
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-            PUTBIT( *bP );
-        for ( col = 0; col < padright; ++col )
-            PUTBIT( 0 );
+    printf ("0x%02x%02x", hi, lo);
+
+}
+
+
+
+static void
+writeRaster(FILE *       const ifP,
+            unsigned int const rows,
+            unsigned int const cols,
+            int          const format) {
+
+    unsigned char * const bitrow = pbm_allocrow_packed(cols + 8);
+
+    Packer packer;
+    unsigned int row;
+
+    packer_init(&packer);
+
+    bitrow[pbm_packed_bytes(cols+8) -1 ] = 0x00;
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int const itemCt = (cols + 15 ) / 16;
+
+        unsigned int i;
+
+        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+
+        pbm_cleanrowend_packed(bitrow, cols);
+
+        for (i = 0; i < itemCt; ++i) {
+            packer_putitem(&packer, bitrow[2*i+0], bitrow[2*i+1]);
+        }
     }
+    pbm_freerow_packed(bitrow);
+}
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+    const char * inputFileName;
+    FILE * ifP;
+    int rows, cols;
+    int format;
+    const char * name;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &inputFileName);
+
+    ifP = pm_openr(inputFileName);
+
+    name = imageName(inputFileName);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    printf("USHORT %s[] = {\n", name);
+    printf("  %d\n", cols);
+    printf("  %d\n", rows);
+
+    writeRaster(ifP, rows, cols, format);
+
+    printf("};\n");
+
+    pm_close(ifP);
 
-    pm_close( ifp );
-    
-    if ( bitsperitem > 0 )
-        PUTITEM;
-    printf( "};\n" );
+    pm_strfree(name);
 
     return 0;
 }
diff --git a/converter/pbm/pi3topbm.c b/converter/pbm/pi3topbm.c
index 8b3b21e3..17b07d6f 100644
--- a/converter/pbm/pi3topbm.c
+++ b/converter/pbm/pi3topbm.c
@@ -22,91 +22,147 @@
  */
 
 #include <stdio.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
 #include "pbm.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;  /* Filename of input file */
+    unsigned int debug;
+};
+
+
+
+static void 
+parseCommandLine(int argc, 
+                 const char ** argv, 
+                 struct CmdlineInfo * const cmdlineP) {
+/* --------------------------------------------------------------------------
+   Parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   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;
+    optStruct3 opt;
+        /* Instructions to pm_optParseOptions3 on how to parse our options. */
+    unsigned int option_def_index;
+  
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "debug",    OPT_FLAG,    NULL,       &cmdlineP->debug,       0);
+  
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;   /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 < 1) 
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("Program takes zero or one argument (filename).  You "
+                     "specified %u", argc-1);
+    }
+}
+
+
+
+static void
+readAndValidateHeader(FILE * const ifP,
+                      bool   const debug,
+                      bool * const reverseP) {
+
+    short item;
+
+    pm_readbigshort(ifP, &item);
+
+    if (debug)
+        pm_message("resolution is %d", item);
+
+    /* only handles hi-rez 640x400 */
+    if (item != 2)
+        pm_error("bad resolution %d", item);
+
+    pm_readbigshort(ifP, &item);
+
+    *reverseP = (item == 0);
+
+    {
+        unsigned int i;
+
+        for (i = 1; i < 16; ++i)
+            pm_readbigshort (ifP, &item);
+    }
+}
+
+
+
 int
-main(argc, argv)
-	int             argc;
-	char           *argv[];
-{
-	int             debug = 0;
-	FILE           *f;
-	int             x;
-	int             i, k;
-	int             c;
-	int		rows, cols;
-	bit		*bitrow;
-	short res;
-	int black, white;
-	const char * const usage = "[-debug] [pi3file]";
-	int argn = 1;
-
-	pbm_init( &argc, argv );
-
-	while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0')
-	  {
-	    if (pm_keymatch(argv[1], "-debug", 2))
-	      debug = 1;
-	    else
-	      pm_usage (usage);
-	    ++argn;
-	  }
-
-	if (argn == argc)
-	    f = stdin;
-	else
-	  {
-	    f = pm_openr (argv[argn]);
-	    ++argn;
-	  }
-
-	if (argn != argc)
-	  pm_usage (usage);
-
-	if (pm_readbigshort (f, &res) == -1)
-		pm_error ("EOF / read error");
-
-	if (debug)
-		pm_message ("resolution is %d", res);
-
-	/* only handles hi-rez 640x400 */
-	if (res != 2)
-		pm_error( "bad resolution" );
-
-	pm_readbigshort (f, &res);
-	if (res == 0)
-	  {
-	    black = PBM_WHITE;
-	    white = PBM_BLACK;
-	  }
-	else
-	  {
-	    black = PBM_BLACK;
-	    white = PBM_WHITE;
-	  }
-
-	for (i = 1; i < 16; i++)
-	  if (pm_readbigshort (f, &res) == -1)
-	    pm_error ("EOF / read error");
-
-	cols = 640;
-	rows = 400;
-	pbm_writepbminit( stdout, cols, rows, 0 );
-	bitrow = pbm_allocrow( cols );
-
-	for (i = 0; i < rows; ++i) {
-		x = 0;
-		while (x < cols) {
-			if ((c = getc(f)) == EOF)
-				pm_error( "end of file reached" );
-			for (k = 0x80; k; k >>= 1) {
-				bitrow[x] = (k & c) ? black : white;
-				++x;
-			}
-		}
-		pbm_writepbmrow( stdout, bitrow, cols, 0 );
-	}
-	pm_close( f );
-	pm_close( stdout );
-	exit(0);
+main(int argc, const char ** argv) {
+
+    unsigned int const rows = 400;
+    unsigned int const cols = 640;
+
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    unsigned int row;
+    bit * bitrow;
+    bool reverse;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    readAndValidateHeader(ifP, cmdline.debug, &reverse);
+
+    pbm_writepbminit(stdout, cols, rows, 0);
+
+    bitrow = pbm_allocrow_packed(cols);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int const colChars = cols / 8;
+
+        unsigned int bytesReadCt;
+
+        bytesReadCt = fread(bitrow, cols / 8, 1, ifP);
+        if (bytesReadCt != 1) {
+            if (feof(ifP))
+                pm_error( "EOF reached while reading image data" );
+            else
+                pm_error("read error while reading image data");
+        }
+
+        if (reverse) {
+            /* flip all pixels */
+            unsigned int colChar;
+            for (colChar = 0; colChar < colChars; ++colChar)
+                bitrow[colChar] = ~bitrow[colChar];
+        }
+        pbm_writepbmrow_packed(stdout, bitrow, cols, 0);
+    }
+
+    pbm_freerow_packed(bitrow);
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
 }
diff --git a/converter/pbm/thinkjettopbm.l b/converter/pbm/thinkjettopbm.l
index 71501596..5de4f2bb 100644
--- a/converter/pbm/thinkjettopbm.l
+++ b/converter/pbm/thinkjettopbm.l
@@ -47,8 +47,15 @@
    undefined.  (Simply leaving them undefined typically works anyway, but it
    is a problem if you use compiler options that say to fail when someone
    uses a macro he failed to define).
+
+   We'd like to define YY_NO_UNPUT so as not to generate the unput function,
+   which we don't use, and avoid a compiler warning about defining and not
+   using it.  Alas, Flex 2.5.35 ignores YY_NO_UNPUT and generates the unput
+   function anyway.  So we have to have a bogus reference to silence the
+   unused function compiler warning.  And that means we have to generate
+   the function always.  Flex 2.5.4 does respect YY_NO_UNPUT.
 */
-#define YY_NO_UNPUT
+#define YY_NO_INPUT 1
 #define YY_STACK_USED 0
 #define YY_ALWAYS_INTERACTIVE 0
 #define YY_NEVER_INTERACTIVE 0
@@ -151,7 +158,7 @@ parseCommandLine(int argc, char ** const argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc(100*sizeof(optEntry));
-        /* Instructions to OptParseOptions3 on how to parse our options.
+        /* Instructions to pm_OptParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -164,7 +171,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
@@ -200,6 +207,8 @@ main (int argc, char **argv)
     }
     debugFlag = cmdline.debug;
     yylex ();
+    if (0)
+        yyunput(0, NULL);  /* defeat compiler warning about unused fn */
     return 0;
 }
 
diff --git a/converter/pbm/xbmtopbm.c b/converter/pbm/xbmtopbm.c
index 9505ba67..bbf4e395 100644
--- a/converter/pbm/xbmtopbm.c
+++ b/converter/pbm/xbmtopbm.c
@@ -170,7 +170,7 @@ getXbmHeader(FILE *         const ifP,
             if (strlen(line) == MAX_LINE - 1)
                 pm_error("A line in the input file is %u characters long.  "
                          "%u is the maximum we can handle",
-                         strlen(line), MAX_LINE-1);
+                         (unsigned)strlen(line), MAX_LINE-1);
 
             parseWidthHeightLine(line, &gotWidth, widthP, &gotHeight, heightP);
 
@@ -362,12 +362,8 @@ main(int    argc,
         
         for (i = 0; i < bytesPerRow; ++i)
             bitrow[i] = bitreverse[*p++];
-            
-        if (cols % 8 > 0) {
-            bitrow[bytesPerRow-1] >>= 8 - cols % 8;
-            bitrow[bytesPerRow-1] <<= 8 - cols % 8;
-        }
-            
+
+        pbm_cleanrowend_packed(bitrow, cols);
         pbm_writepbmrow_packed(stdout, bitrow, cols, 0);
     }
 
diff --git a/converter/pbm/ybmtopbm.c b/converter/pbm/ybmtopbm.c
index 739e168a..2a429086 100644
--- a/converter/pbm/ybmtopbm.c
+++ b/converter/pbm/ybmtopbm.c
@@ -10,104 +10,101 @@
 ** implied warranty.
 */
 
-#include <stdio.h>
+#include "pm.h"
 #include "pbm.h"
+#include "bitreverse.h"
 
-static void getinit ARGS(( FILE* file, short* colsP, short* rowsP, short* depthP, short* padrightP ));
-static bit getbit ARGS(( FILE* file ));
+static short const ybmMagic = ( ( '!' << 8 ) | '!' );
 
-#define YBM_MAGIC  ( ( '!' << 8 ) | '!' )
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    short rows, cols, padright, row, col;
-    short depth;
 
-    pbm_init( &argc, argv );
+static void
+getinit(FILE *  const ifP,
+        short * const colsP,
+        short * const rowsP,
+        short * const depthP) {
 
-    if ( argc > 2 )
-	pm_usage( "[ybmfile]" );
+    short magic;
+    int rc;
 
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
+    rc = pm_readbigshort(ifP, &magic);
+    if (rc == -1)
+        pm_error("EOF / read error");
 
-    getinit( ifp, &cols, &rows, &depth, &padright );
-    if ( depth != 1 )
-	pm_error(
-	    "YBM file has depth of %d, must be 1",
-	    (int) depth );
+    if (magic != ybmMagic)
+        pm_error("bad magic number in YBM file");
 
-    pbm_writepbminit( stdout, cols, rows, 0 );
-    bitrow = pbm_allocrow( cols );
+    rc = pm_readbigshort(ifP, colsP);
+    if (rc == -1 )
+        pm_error("EOF / read error");
 
-    for ( row = 0; row < rows; ++row )
-	{
-	/* Get data. */
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-	    *bP = getbit( ifp );
-	/* Discard line padding */
-        for ( col = 0; col < padright; ++col )
-	    (void) getbit( ifp );
-	pbm_writepbmrow( stdout, bitrow, cols, 0 );
-	}
+    rc = pm_readbigshort(ifP, rowsP);
+    if (rc == -1)
+        pm_error("EOF / read error");
 
-    pm_close( ifp );
-    pm_close( stdout );
+    *depthP = 1;
+}
 
-    exit( 0 );
-    }
 
-static int item;
-static int bitsperitem, bitshift;
 
-static void
-getinit( file, colsP, rowsP, depthP, padrightP )
-    FILE* file;
-    short* colsP;
-    short* rowsP;
-    short* depthP;
-    short* padrightP;
-    {
-    short magic;
 
-    if ( pm_readbigshort( file, &magic ) == -1 )
-	pm_error( "EOF / read error" );
-    if ( magic != YBM_MAGIC )
-	pm_error( "bad magic number in YBM file" );
-    if ( pm_readbigshort( file, colsP ) == -1 )
-	pm_error( "EOF / read error" );
-      if ( pm_readbigshort( file, rowsP ) == -1 )
-	pm_error( "EOF / read error" );
 
-    *depthP = 1;
-    *padrightP = ( ( *colsP + 15 ) / 16 ) * 16 - *colsP;
-    bitsperitem = 0;
+
+int
+main(int argc, const char * argv[]) {
+
+    FILE * ifP;
+    bit * bitrow;
+    short rows, cols;
+    unsigned int row;
+    short depth;
+    const char * inputFile;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        inputFile = "-";
+    else {
+        inputFile = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("Too many arguments.  The only argument is the optional "
+                     "input file name");
     }
 
-static bit
-getbit( file )
-    FILE* file;
-    {
-    bit b;
-
-    if ( bitsperitem == 0 )
-	{
-	item = getc(file) | getc(file)<<8;
-	if ( item == EOF )
-	    pm_error( "EOF / read error" );
-	bitsperitem = 16;
-	bitshift = 0;
-	}
-    b = ( ( item >> bitshift) & 1 ) ? PBM_BLACK : PBM_WHITE;
-    --bitsperitem;
-    ++bitshift;
-    return b;
+    ifP = pm_openr(inputFile);
+
+    getinit(ifP, &cols, &rows, &depth);
+    if (depth != 1)
+        pm_error("YBM file has depth of %u, must be 1", (unsigned)depth);
+    
+    pbm_writepbminit(stdout, cols, rows, 0);
+
+    bitrow = pbm_allocrow_packed(cols + 8);
+
+    for (row = 0; row < rows; ++row) {
+        uint16_t *   const itemrow = (uint16_t *) bitrow;
+        unsigned int const itemCt  = (cols + 15) / 16;
+
+        unsigned int i;
+
+        /* Get raster. */
+        for (i = 0; i < itemCt; ++i) {
+            short int item;
+            pm_readbigshort(ifP, &item);
+            itemrow[i] = (uint16_t) item; 
+        }
+
+        for (i = 0; i < pbm_packed_bytes(cols); ++i)
+            bitrow[i] = bitreverse[bitrow[i]];
+
+        pbm_cleanrowend_packed(bitrow, cols);
+        pbm_writepbmrow_packed(stdout, bitrow, cols, 0);
     }
+
+    pbm_freerow_packed(bitrow);
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/converter/pgm/Makefile b/converter/pgm/Makefile
index b109683b..f7ff341e 100644
--- a/converter/pgm/Makefile
+++ b/converter/pgm/Makefile
@@ -8,8 +8,8 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 include $(BUILDDIR)/config.mk
 
 PORTBINARIES =	asciitopgm bioradtopgm fstopgm hipstopgm \
-		lispmtopgm pgmtofs pgmtolispm pgmtopgm \
-	        psidtopgm spottopgm sbigtopgm
+		lispmtopgm pgmtofs pgmtolispm pgmtopgm pgmtosbig pgmtost4 \
+	        psidtopgm spottopgm sbigtopgm st4topgm
 MATHBINARIES =	rawtopgm
 BINARIES =	$(PORTBINARIES) $(MATHBINARIES)
 
diff --git a/converter/pgm/asciitopgm.c b/converter/pgm/asciitopgm.c
index a3a5bd48..f4179de8 100644
--- a/converter/pgm/asciitopgm.c
+++ b/converter/pgm/asciitopgm.c
@@ -1,4 +1,4 @@
-/* asciitopgm.c - read an ASCII graphics file and produce a portable graymap
+/* asciitopgm.c - read an ASCII graphics file and produce a PGM
 **
 ** Copyright (C) 1989 by Wilson H. Bent, Jr
 **
@@ -16,11 +16,11 @@
 #include <string.h>
 
 #include "pm_c_util.h"
-#include "pgm.h"
 #include "mallocvar.h"
 #include "nstring.h"
+#include "pgm.h"
 
-static char gmap [128] = {
+static unsigned int const gmap [128] = {
 /*00 nul-bel*/  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 /*08 bs -si */  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 /*10 dle-etb*/  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -39,28 +39,160 @@ static char gmap [128] = {
 /*78  x -del*/  0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
 };
 
-static gray maxval = 127;
+static gray const maxval = 127;
+
+
+
+static void
+zeroObuf(unsigned int * const obuf,
+         unsigned int   const cols) {
+
+    unsigned int col;
+    for (col = 0; col < cols; ++col)
+        obuf[col] = 0;
+}
+
+
+
+static void
+convertRowToPgm(const unsigned int * const obuf,
+                unsigned int         const cols,
+                gray                 const maxval,
+                gray *               const grayrow) {
+
+    unsigned int col;
+
+    for (col = 0; col < cols; ++col)
+        grayrow[col] = maxval - MIN(maxval, obuf[col]);
+}
+
+
+
+static bool warnedNonAscii;
+
+static unsigned int
+darknessOfChar(char const c) {
+
+    unsigned char asciifiedC;
+
+    if (c & 0x80) {       /* !isascii(c) */
+        if (!warnedNonAscii) {
+            pm_message("Warning: non-ASCII char(s) in input");
+            warnedNonAscii = true;
+        }
+        asciifiedC = c & 0x7f;      /* toascii(c) */
+    } else
+        asciifiedC = c;
+
+    return gmap[asciifiedC];
+}
+
+
+
+static void
+convertAsciiToPgm(FILE *         const ifP,
+                  unsigned int   const cols,
+                  unsigned int   const rows,
+                  unsigned int   const divisor,
+                  gray           const maxval,
+                  gray **        const grays,
+                  unsigned int * const obuf) {
+
+    unsigned int outRow;
+    unsigned int outCursor;
+    bool beginningOfImage;
+    bool beginningOfLine;
+    bool warnedTrunc;
+    bool eof;
+
+    zeroObuf(obuf, cols);
+
+    warnedNonAscii = false;
+    warnedTrunc = false;
+    outRow = 0;
+    outCursor = 0;
+    beginningOfImage = true;
+    beginningOfLine = true;
+    eof = false;
+    while (outRow < rows && !eof) {
+        int c;
+
+        c = getc(ifP);
+
+        if (c == EOF)
+            eof = true;
+        else {
+            if (beginningOfLine) {
+                if (c == '+') {
+                    /* + at start of line means rest of line 
+                       overstrikes previous
+                    */
+                    c = getc(ifP);
+                    if (c == EOF)
+                        eof = true;
+                } else {
+                    if (!beginningOfImage) {
+                        /* Output previous line, move to next */
+
+                        convertRowToPgm(obuf, cols, maxval, grays[outRow]);
+
+                        zeroObuf(obuf, cols);
+
+                        ++outRow;
+                    }
+                }
+                outCursor = 0;
+                beginningOfLine = false;
+            }
+            if (!eof) {
+                if (c == '\n')
+                    beginningOfLine = true;
+                else {
+                    if (outRow < rows && outCursor < cols)
+                        obuf[outCursor++] += darknessOfChar(c) / divisor;
+                    else {
+                        if (!warnedTrunc) {
+                            pm_message("Warning: "
+                                       "truncating image to %u columns "
+                                       "x %u rows", cols, rows);
+                            warnedTrunc = true;
+                        }
+                    }
+                }
+            }
+        }
+        beginningOfImage = false;
+    }
+    while (outRow < rows) {
+        /* Output previous line, move to next */
+
+        convertRowToPgm(obuf, cols, maxval, grays[outRow]);
+
+        zeroObuf(obuf, cols);
+
+        ++outRow;
+    }
+}
+
+
 
 int
-main( argc, argv )
-    int argc;
-    char *argv[];
-{
-    FILE *ifd;
-    register gray **grays;
-    int argn, row;
-    register int c, i;
-    int rows = 0, cols = 0;
-    int divisor = 1;
-    bool warned;
-    int *obuf;
+main(int argc, const char ** argv) {
+
+    FILE * ifP;
+    gray ** grays;
+    int argn;
+    unsigned int rows, cols;
+    unsigned int divisor;
+    unsigned int * obuf;  /* malloced */
     const char * const usage = "[-d <val>] height width [asciifile]";
-    char trunc;
-
-    pgm_init( &argc, argv );
 
-    warned = FALSE;
+    pm_proginit(&argc, argv);
 
+    rows = 0;  /* initial value */
+    cols = 0;  /* initial value */
+    divisor = 1; /* initial value */
+    
     argn = 1;
 
     if ( argc < 3 || argc > 6 )
@@ -72,7 +204,7 @@ main( argc, argv )
         {
             if ( argc == argn + 1 )
                 pm_usage( usage );
-            if ( sscanf( argv[argn+1], "%d", &divisor ) != 1 )
+            if ( sscanf( argv[argn+1], "%u", &divisor ) != 1 )
                 pm_usage( usage );
             argn += 2;
         }
@@ -80,9 +212,9 @@ main( argc, argv )
             pm_usage( usage );
     }
 
-    if ( sscanf( argv[argn++], "%d", &rows ) != 1 )
+    if ( sscanf( argv[argn++], "%u", &rows ) != 1 )
         pm_usage( usage );
-    if ( sscanf( argv[argn++], "%d", &cols ) != 1 )
+    if ( sscanf( argv[argn++], "%u", &cols ) != 1 )
         pm_usage( usage );
     if ( rows < 1 )
         pm_error( "height is less than 1" );
@@ -93,86 +225,24 @@ main( argc, argv )
         pm_usage( usage );
 
     if ( argc == argn + 1 )
-        ifd = pm_openr( argv[argn] );
+        ifP = pm_openr(argv[argn]);
     else
-        ifd = stdin;
-
-    /* Here's where the work is done:
-     * - Usually, the graymap value of the input char is summed into grays
-     * - For a 'normal' newline, the current row is adjusted by the divisor
-     *   and the current row is incremented
-     * - If the first char in the input line is a '+', then the current row
-     *   stays the same to allow 'overstriking'
-     * NOTE that we assume the user specified a sufficiently large width!
-     */
-    MALLOCARRAY( obuf, cols );
-    if ( obuf == NULL )
-        pm_error( "Unable to allocate memory for %d columns.", cols);
-    else {
-        unsigned int col;
-        for (col = 0; col < cols; ++col) obuf[col] = 0;
-    }
-    grays = pgm_allocarray( cols, rows );
-    row = i = trunc = 0;
-    while ( row < rows )
-    {
-        switch (c = getc (ifd))
-        {
-        case EOF:
-            goto line_done;
-        case '\n':
-	newline:
-	    trunc = 0;
-            if ((c = getc (ifd)) == EOF)
-                goto line_done;
-            if (c == '+')
-                i = 0;
-            else
-            {
-            line_done:
-                for (i = 0; i < cols; ++i)
-                    grays[row][i] = maxval - (obuf[i] / divisor);
-                {
-                    unsigned int col;
-                    for (col = 0; col < cols; ++col) obuf[col] = 0;
-                }
-                i = 0;
-                ++row;
-                if ( row >= rows )
-                    break;
-		if (c == '\n')
-		    goto newline;
-                else if (c != EOF)
-                    obuf[i++] += gmap[c];
-            }
-            break;
-        default:
-	    if (i == cols)
-	    {
-		if (! trunc)
-		{
-		    pm_message("Warning: row %d being truncated at %d columns",
-			       row+1, cols);
-		    trunc = 1;
-		}
-		continue;
-	    }
-            if (c > 0x7f)       /* !isascii(c) */
-            {
-                if (!warned)
-                {
-                    pm_message("Warning: non-ASCII char(s) in input");
-                    warned = TRUE;
-                }
-                c &= 0x7f;      /* toascii(c) */
-            }
-            obuf[i++] += gmap[c];
-            break;
-        }
-    }
-    pm_close( ifd );
+        ifP = stdin;
+
+    MALLOCARRAY(obuf, cols);
+    if (obuf == NULL)
+        pm_error("Unable to allocate memory for %u columns", cols);
+
+    grays = pgm_allocarray(cols, rows);
+
+    convertAsciiToPgm(ifP, cols, rows, divisor, maxval, grays, obuf);
+
+    pm_close(ifP);
+
+    pgm_writepgm(stdout, grays, cols, rows, maxval, 0);
 
-    pgm_writepgm( stdout, grays, cols, rows, maxval, 0 );
+    free(obuf);
+    pgm_freearray(grays, rows);
 
     return 0;
 }
diff --git a/converter/pgm/fstopgm.c b/converter/pgm/fstopgm.c
index 2c636f41..1f574604 100644
--- a/converter/pgm/fstopgm.c
+++ b/converter/pgm/fstopgm.c
@@ -12,127 +12,144 @@
 
 #include <string.h>
 
+#include "pm.h"
 #include "pgm.h"
 
-static int gethexit ARGS(( FILE* ifp ));
+
+
+static int
+gethexit(FILE * const ifP) {
+
+    for ( ; ; ) {
+        unsigned int const i = getc(ifP);
+
+        if (i == EOF)
+            pm_error("EOF / read error");
+        else {
+            char const c = (char) i;
+            if (c >= '0' && c <= '9')
+                return c - '0';
+            else if (c >= 'A' && c <= 'F')
+                return c - 'A' + 10;
+            else if (c >= 'a' && c <= 'f')
+                return c - 'a' + 10;
+            else {
+                /* Ignore - whitespace. */
+            }
+        }
+    }
+}
+
+
+
+static void
+warnNonsquarePixels(unsigned int const cols,
+                    unsigned int const xcols,
+                    unsigned int const rows,
+                    unsigned int const xrows) {
+    
+    const char * const baseMsg = "warning, non-square pixels";
+
+    if (pm_have_float_format()) {
+        float const rowratio = (float) xrows / (float) rows;
+        float const colratio = (float) xcols / (float) cols;
+
+        pm_message("%s; to fix do a 'pamscale -%cscale %g'",
+                   baseMsg,
+                   rowratio > colratio ? 'y' : 'x',
+                   rowratio > colratio ? 
+                   rowratio / colratio : colratio / rowratio);
+    } else
+        pm_message("%s", baseMsg);
+}
+
+
 
 int
-main( argc, argv )
-int argc;
-char *argv[];
-    {
-    FILE *ifp;
-    register gray **grays, *gP;
-    int argn, row;
-    register int col;
-    int maxval;
-    int rows = 0, cols = 0, depth = 0, xrows = 0, xcols = 0, xdepth = 0;
+main(int argc, const char ** argv) {
+
+    FILE * ifP;
+    gray ** grays;
+    int argn;
+    int row;
+    gray maxval;
+    int rows, cols, depth;
+    int xrows, xcols, xdepth;
 #define STRSIZE 1000
-    char buf[STRSIZE], firstname[STRSIZE], lastname[STRSIZE], email[STRSIZE];
 
+    pm_proginit(&argc, argv);
 
-    pgm_init( &argc, argv );
+    rows = 0;
+    cols = 0;
+    depth = 0;
+
+    xrows = 0;
+    xcols = 0;
+    xdepth = 0;
 
     argn = 1;
 
-    if ( argn < argc )
-	{
-	ifp = pm_openr( argv[argn] );
-	argn++;
-	}
-    else
-	ifp = stdin;
+    if (argn < argc) {
+        ifP = pm_openr(argv[argn]);
+        argn++;
+    } else
+        ifP = stdin;
 
-    if ( argn != argc )
-	pm_usage( "[fsfile]" );
+    if (argn != argc)
+        pm_error("Too many arguments.  The only argument is the file name");
 
     /* Read the FaceSaver(tm) header. */
-    for ( ; ; )
-	{
-	if ( fgets( buf, STRSIZE, ifp ) == (char *) 0 )
-	    pm_error( "error reading header" );
-
-	/* Blank line ends header. */
-	if ( strlen( buf ) == 1 )
-	    break;
-
-	if ( sscanf( buf, "FirstName: %[^\n]", firstname ) == 1 )
-	    ;
-	else if ( sscanf( buf, "LastName: %[^\n]", lastname ) == 1 )
-	    ;
-	else if ( sscanf( buf, "E-mail: %[^\n]", email ) == 1 )
-	    ;
-	else if ( sscanf( buf, "PicData: %d %d %d\n",
-			  &cols, &rows, &depth ) == 3 )
-	    {
-	    if ( depth != 8 )
-		pm_error(
-		    "can't handle 'PicData' depth other than 8" );
-	    }
-	else if ( sscanf( buf, "Image: %d %d %d\n",
-			  &xcols, &xrows, &xdepth ) == 3 )
-	    {
-	    if ( xdepth != 8 )
-		pm_error(
-		    "can't handle 'Image' depth other than 8" );
-	    }
-	}
-    if ( cols <= 0 || rows <= 0 )
-	pm_error( "invalid header" );
-    maxval = pm_bitstomaxval( depth );
-    if ( maxval > PGM_OVERALLMAXVAL )
-	pm_error( "depth %d is too large.  Our maximum is %d",
-              maxval, PGM_OVERALLMAXVAL);
-    if ( xcols != 0 && xrows != 0 && ( xcols != cols || xrows != rows ) )
-	{
-	float rowratio, colratio;
-
-	rowratio = (float) xrows / (float) rows;
-	colratio = (float) xcols / (float) cols;
-	pm_message(
-	    "warning, non-square pixels; to fix do a 'pamscale -%cscale %g'",
-	    rowratio > colratio ? 'y' : 'x',
-	    rowratio > colratio ? rowratio / colratio : colratio / rowratio );
-	}
-
-    /* Now read the hex bits. */
-    grays = pgm_allocarray( cols, rows );
-    for ( row = rows - 1; row >= 0; row--)
-	{
-	for ( col = 0, gP = grays[row]; col < cols; col++, gP++ )
-	    {
-	    *gP = gethexit( ifp ) << 4;
-	    *gP += gethexit( ifp );
-	    }
-	}
-    pm_close( ifp );
-
-    /* And write out the graymap. */
-    pgm_writepgm( stdout, grays, cols, rows, (gray) maxval, 0 );
-    pm_close( stdout );
-
-    exit( 0 );
+    for ( ; ; ) {
+        char buf[STRSIZE];
+        char firstname[STRSIZE];
+        char lastname[STRSIZE];
+        char email[STRSIZE];
+
+        char * const rc = fgets(buf, STRSIZE, ifP);
+
+        if (rc  == NULL)
+            pm_error("error reading header");
+
+        /* Blank line ends header. */
+        if (strlen(buf) == 1)
+            break;
+
+        if (sscanf(buf, "FirstName: %[^\n]", firstname) == 1);
+        else if (sscanf(buf, "LastName: %[^\n]", lastname) == 1);
+        else if (sscanf(buf, "E-mail: %[^\n]", email ) == 1);
+        else if (sscanf(buf, "PicData: %d %d %d\n",
+                        &cols, &rows, &depth ) == 3) {
+            if (depth != 8)
+                pm_error("can't handle 'PicData' depth other than 8");
+        } else if (sscanf(buf, "Image: %d %d %d\n",
+                          &xcols, &xrows, &xdepth ) == 3) {
+            if (xdepth != 8)
+                pm_error("can't handle 'Image' depth other than 8");
+        }
     }
-
-static int
-gethexit( ifp )
-FILE *ifp;
-    {
-    register int i;
-    register char c;
-
-    for ( ; ; )
-	{
-	i = getc( ifp );
-	if ( i == EOF )
-	    pm_error( "EOF / read error" );
-	c = (char) i;
-	if ( c >= '0' && c <= '9' )
-	    return c - '0';
-	else if ( c >= 'A' && c <= 'F' )
-	    return c - 'A' + 10;
-	else if ( c >= 'a' && c <= 'f' )
-	    return c - 'a' + 10;
-	/* Else ignore - whitespace. */
-	}
+    if (cols <= 0 || rows <= 0)
+        pm_error("invalid header");
+    maxval = pm_bitstomaxval(depth);
+    if (maxval > PGM_OVERALLMAXVAL)
+        pm_error("depth %d is too large.  Our maximum is %d",
+                 maxval, PGM_OVERALLMAXVAL);
+    if (xcols != 0 && xrows != 0 && (xcols != cols || xrows != rows))
+        warnNonsquarePixels(cols, xcols, rows, xrows);
+
+    /* Read the hex bits. */
+    grays = pgm_allocarray(cols, rows);
+    for (row = rows - 1; row >= 0; --row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            grays[row][col] =  gethexit(ifP) << 4;
+            grays[row][col] += gethexit(ifP);
+        }
     }
+    pm_close(ifP);
+
+    pgm_writepgm(stdout, grays, cols, rows, maxval, 0);
+    pm_close(stdout);
+
+    return 0;
+}
+
diff --git a/converter/pgm/pgmtolispm.c b/converter/pgm/pgmtolispm.c
index abb85494..7d931fb3 100644
--- a/converter/pgm/pgmtolispm.c
+++ b/converter/pgm/pgmtolispm.c
@@ -14,129 +14,167 @@
 **   usually a color image; but a color map is not written in the file, so we
 **   treat this as a graymap instead.  To convert a color image to Lispm 
 **   format, you must convert it to a pgm, and hand-edit a color map...  Ick.
+**
+** Feb 2010 afu
+** Added dimension check to prevent short int from overflowing
+** Changed code style (ANSI-style function definitions, etc.)
 */
 
-#include <stdio.h>
+#include "pm.h"
 #include "pgm.h"
 
 #define LISPM_MAGIC  "This is a BitMap file"
+#define INT16MAX 32767
 
-static void putinit ARGS(( int cols, int rows, int depth ));
-static int depth_to_word_size ARGS(( int depth ));
-static void putval ARGS(( gray b ));
-static void putrest ARGS(( void ));
-static void putitem ARGS(( void ));
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    gray *grayrow;
-    register gray* gP;
-    int rows, cols, depth, format, padright, row, col;
-    gray maxval;
+static unsigned int item;
+static unsigned int bitsperitem, maxbitsperitem, bitshift;
 
+static unsigned int
+depth_to_word_size(unsigned int const depth) {
 
-    pgm_init( &argc, argv );
+    /* Lispm architecture specific - if a bitmap is written    */
+    /* out with a depth of 5, it really has a depth of 8, and  */
+    /* is stored that way in the file.                         */
 
-    if ( argc > 2 )
-	pm_usage( "[pgmfile]" );
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
+    unsigned int const wordSize = 
+        depth ==  1 ?  1 :
+        depth ==  2 ?  2 :
+        depth <=  4 ?  4 :
+        depth <=  8 ?  8 :
+        depth <= 16 ? 16 :
+        depth <= 32 ? 32 :
+        0;
 
-    pgm_readpgminit( ifp, &cols, &rows, &maxval, &format );
-    grayrow = pgm_allocrow( cols );
-    depth = pm_maxvaltobits( maxval );
+    if (wordSize == 0)
+        pm_error("depth was %u, which is not in the range 1-32", depth);
 
-    /* Compute padding to round cols up to the nearest multiple of 32. */
-    padright = ( ( cols + 31 ) / 32 ) * 32 - cols;
+    return wordSize;
+}
 
-    putinit( cols, rows, depth );
-    for ( row = 0; row < rows; ++row )
-	{
-	pgm_readpgmrow( ifp, grayrow, cols, maxval, format );
-        for ( col = 0, gP = grayrow; col < cols; ++col, ++gP )
-	    putval( *gP );
-	for ( col = 0; col < padright; ++col )
-	    putval( 0 );
-        }
 
-    pm_close( ifp );
 
-    putrest( );
+static void
+putinit(unsigned int const cols,
+        unsigned int const rows,
+        unsigned int const depth) {
 
-    exit( 0 );
-    }
+    unsigned int const cols32 = ((cols + 31 ) / 32) * 32;
 
-static unsigned int item;
-static unsigned int bitsperitem, maxbitsperitem, bitshift;
+    unsigned int i;
 
-static void
-putinit( cols, rows, depth )
-    int cols, rows, depth;
-    {
-    int i;
-    int cols32 = ( ( cols + 31 ) / 32 ) * 32;	/* Lispms are able to write bit files that are not mod32 wide, but we   */
-						/* don't.  This should be ok, since bit arrays which are not mod32 wide */
-    printf(LISPM_MAGIC);			/* are pretty useless on a lispm (can't hand them to bitblt).		*/
-    pm_writelittleshort( stdout, cols );
-    pm_writelittleshort( stdout, rows );
-    pm_writelittleshort( stdout, cols32 );
+    /* Lispms are able to write bit files that are not mod32 wide, but we   */
+    /* don't.  This should be ok, since bit arrays which are not mod32 wide */
+    /* are pretty useless on a lispm (can't hand them to bitblt).           */
+
+    if (rows > INT16MAX || cols > INT16MAX || cols32 > INT16MAX)
+        pm_error("Input image is too large.");
+
+    printf(LISPM_MAGIC);
+
+    pm_writelittleshort(stdout, cols);
+    pm_writelittleshort(stdout, rows);
+    pm_writelittleshort(stdout, cols32);
     putchar(depth & 0xFF);
 
-    for ( i = 0; i < 9; ++i )
-	putchar( 0 );	/* pad bytes */
+    for (i = 0; i < 9; ++i)
+        putchar(0);   /* pad bytes */
 
-    item = 0;
-    bitsperitem = 0;
-    maxbitsperitem = depth_to_word_size( depth );
-    bitshift = 0;
-    }
+    item           = 0;
+    bitsperitem    = 0;
+    maxbitsperitem = depth_to_word_size(depth);
+    bitshift       = 0;
+}
 
-static int
-depth_to_word_size (depth)	/* Lispm architecture specific - if a bitmap is written    */
-  int depth;			/* out with a depth of 5, it really has a depth of 8, and  */
-{				/* is stored that way in the file.			   */
-    if (depth==0 || depth==1)	return ( 1);
-    else if (depth ==  2)	return ( 2);
-    else if (depth <=  4)	return ( 4);
-    else if (depth <=  8)	return ( 8);
-    else if (depth <= 16)	return (16);
-    else if (depth <= 32)	return (32);
-    else {
-      pm_error( "depth was %d, which is not in the range 1-32", depth );
-      return(-1);  /* Should never reach here */
-  }
+
+
+static void
+putitem(void) {
+
+    pm_writelittlelong(stdout, ~item);
+
+    item        = 0;
+    bitsperitem = 0;
+    bitshift    = 0;
 }
 
 
 
 static void
-putval( gray b )
-    {
-    if ( bitsperitem == 32 )
-	putitem( );
-    item = item | ( b << bitshift );
+putval(gray const b) {
+
+    if (bitsperitem == 32)
+        putitem();
+
+    item        = item | (b << bitshift);
     bitsperitem = bitsperitem + maxbitsperitem;
-    bitshift = bitshift + maxbitsperitem;
-    }
+    bitshift    = bitshift + maxbitsperitem;
+}
+
+
 
 static void
-putrest( )
-    {
-    if ( bitsperitem > 0 )
-	putitem( );
+putrest(void) {
+
+    if (bitsperitem > 0)
+        putitem();
+}
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+    FILE * ifP;
+    gray * grayrow;
+    int rows;
+    int cols;
+    unsigned int depth;
+    int format;
+    unsigned int padright;
+    unsigned int row;
+    gray maxval;
+    const char * inputFile;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        inputFile = "-";
+    else {
+        inputFile = argv[1];
+
+        if (argc-1 > 2)
+            pm_error("Too many arguments.  The only argument is the optional "
+                     "input file name");
     }
 
-static void
-putitem( )
-    {
-    pm_writelittlelong( stdout, ~item );
-    item = 0;
-    bitsperitem = 0;
-    bitshift = 0;
+    ifP = pm_openr(inputFile);
+
+    pgm_readpgminit(ifP, &cols, &rows, &maxval, &format);
+
+    grayrow = pgm_allocrow(cols);
+    depth = pm_maxvaltobits(maxval);
+
+    /* Compute padding to round cols up to the nearest multiple of 32. */
+    padright = ((cols + 31) / 32) * 32 - cols;
+
+    putinit(cols, rows, depth);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        pgm_readpgmrow(ifP, grayrow, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col)
+            putval(grayrow[col]);
+
+        for (col = 0; col < padright; ++col)
+            putval(0);
     }
+
+    pm_close(ifP);
+
+    putrest();
+
+    return 0;
+}
diff --git a/converter/pgm/pgmtosbig.c b/converter/pgm/pgmtosbig.c
new file mode 100644
index 00000000..0a302dd8
--- /dev/null
+++ b/converter/pgm/pgmtosbig.c
@@ -0,0 +1,130 @@
+/*=============================================================================
+                                 pgmtosbig
+===============================================================================
+
+  This program converts from PGM to a simple subset of SBIG.
+
+  By Bryan Henderson January 19, 2015.
+
+  Contributed to the public domain by its author.
+=============================================================================*/
+#include <string.h>
+
+#include "pm.h"
+#include "nstring.h"
+#include "pgm.h"
+
+
+
+#define SBIG_HEADER_LENGTH  2048      /* File header length */
+
+#define CTLZ "\x1A"
+
+
+struct SbigHeader {
+/*----------------------------------------------------------------------------
+   The information in an SBIG file header.
+
+   This is only the information this program cares about; the header
+   may have much more information in it.
+-----------------------------------------------------------------------------*/
+    unsigned int height;
+    unsigned int width;
+    unsigned int saturationLevel;
+};
+
+
+
+static void
+addUintParm(char *       const buffer,
+            const char * const name,
+            unsigned int const value) {
+
+    const char * line;
+
+    pm_asprintf(&line, "%s=%u\n\r", name, value);
+
+    strcat(buffer, line);
+
+    pm_strfree(line);
+}
+
+
+
+static void
+writeSbigHeader(FILE *            const ofP,
+                struct SbigHeader const hdr) {
+
+    char buffer[SBIG_HEADER_LENGTH];
+
+    memset(&buffer[0], 0x00, sizeof(buffer));
+
+    buffer[0] = '\0';
+
+    /* N.B. LF-CR instead of CRLF.  That's what the spec says. */
+
+    strcat(buffer, "ST-6 Image\n\r" );
+
+    addUintParm(buffer, "Height", hdr.height);
+
+    addUintParm(buffer, "Width", hdr.width);
+
+    addUintParm(buffer, "Sat_level", hdr.saturationLevel);
+
+    strcat(buffer, "End\n\r" CTLZ);
+
+    fwrite(buffer, 1, sizeof(buffer), ofP);
+}
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+    FILE * ifP;
+    gray * grayrow;
+    int rows;
+    int cols;
+    int format;
+    struct SbigHeader hdr;
+    unsigned int row;
+    gray maxval;
+    const char * inputFile;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        inputFile = "-";
+    else {
+        inputFile = argv[1];
+
+        if (argc-1 > 2)
+            pm_error("Too many arguments.  The only argument is the optional "
+                     "input file name");
+    }
+
+    ifP = pm_openr(inputFile);
+
+    pgm_readpgminit(ifP, &cols, &rows, &maxval, &format);
+
+    grayrow = pgm_allocrow(cols);
+
+    hdr.height = rows;
+    hdr.width = cols;
+    hdr.saturationLevel = maxval;
+
+    writeSbigHeader(stdout, hdr);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        pgm_readpgmrow(ifP, grayrow, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col)
+            pm_writelittleshort(stdout, grayrow[col]);
+    }
+
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/pgm/pgmtost4.c b/converter/pgm/pgmtost4.c
new file mode 100644
index 00000000..fa101ac9
--- /dev/null
+++ b/converter/pgm/pgmtost4.c
@@ -0,0 +1,104 @@
+/*=============================================================================
+                                 pgmtost4
+===============================================================================
+
+  This program converts from PGM to a simple subset of SBIG ST-4.
+
+  By Bryan Henderson January 19, 2015.
+
+  Contributed to the public domain by its author.
+=============================================================================*/
+#include <string.h>
+
+#include "pm.h"
+#include "nstring.h"
+#include "pam.h"
+
+
+
+static unsigned int const st4Height = 165;
+static unsigned int const st4Width  = 192;
+static unsigned int const st4Maxval = 255;
+
+
+
+static void
+writeSt4Footer(FILE * const ofP) {
+
+    const char * const comment = "This was created by Pgmtost4";
+    char buffer[192];
+
+    memset(buffer, ' ', sizeof(buffer));  /* initial value */
+
+    buffer[0] = 'v';
+
+    memcpy(&buffer[  0], "v", 1);
+    memcpy(&buffer[  1], comment, strlen(comment));
+    memcpy(&buffer[ 79], "         7", 10);
+    memcpy(&buffer[ 89], "         8", 10);
+    memcpy(&buffer[ 99], "         9", 10);
+    memcpy(&buffer[109], "        10", 10);
+
+    fwrite(buffer, 1, sizeof(buffer), ofP);
+}
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+    FILE * ifP;
+    tuple * tuplerow;
+    struct pam inpam;
+    unsigned int row;
+    const char * inputFile;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        inputFile = "-";
+    else {
+        inputFile = argv[1];
+
+        if (argc-1 > 2)
+            pm_error("Too many arguments.  The only argument is the optional "
+                     "input file name");
+    }
+
+    ifP = pm_openr(inputFile);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    if (inpam.height != st4Height)
+        pm_error("Image is wrong height for ST-4 SBIG: %u pixels.  "
+                 "Must be %u", inpam.height, st4Height);
+
+    if (inpam.width != st4Width)
+        pm_error("Image is wrong width for ST-4 SBIG: %u pixels.  "
+                 "Must be %u", inpam.width, st4Width);
+    
+    /* Really, we should just scale to maxval 255.  There are library routines
+       for that, but we're too lazy even for that, since nobody is really
+       going to use this program.
+    */
+    if (inpam.maxval != st4Maxval)
+        pm_error("Image is wrong maxval for ST-4 SBIG: %u.  "
+                 "Must be %u", (unsigned)inpam.maxval, st4Maxval);
+
+    tuplerow = pnm_allocpamrow(&inpam);
+
+    for (row = 0; row < inpam.height; ++row) {
+        unsigned int col;
+
+        pnm_readpamrow(&inpam, tuplerow);
+
+        for (col = 0; col < inpam.width; ++col)
+            pm_writechar(stdout, (char)tuplerow[col][0]);
+    }
+
+    writeSt4Footer(stdout);
+
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/pgm/rawtopgm.c b/converter/pgm/rawtopgm.c
index 0180a02c..2e5fbb7d 100644
--- a/converter/pgm/rawtopgm.c
+++ b/converter/pgm/rawtopgm.c
@@ -13,8 +13,9 @@
 #include <math.h>
 
 #include "pm_c_util.h"
-#include "pgm.h"
+#include "mallocvar.h"
 #include "shhopt.h"
+#include "pgm.h"
 
 struct cmdline_info {
     /* All the information the user supplied in the command line,
@@ -37,82 +38,93 @@ struct cmdline_info {
 
 static void
 parse_command_line(int argc, char ** argv,
-                   struct cmdline_info *cmdline_p) {
+                   struct cmdline_info *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optStruct *option_def = malloc(100*sizeof(optStruct));
-        /* Instructions to OptParseOptions2 on how to parse our options.
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
          */
-    optStruct2 opt;
+    optStruct3 opt;
 
     unsigned int option_def_index;
 
-    option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENTRY(0,   "bottomfirst",   OPT_FLAG,   &cmdline_p->bottomfirst,    0);
-    OPTENTRY(0,   "bt",            OPT_FLAG,   &cmdline_p->bottomfirst,    0);
-    OPTENTRY(0,   "topbottom",     OPT_FLAG,   &cmdline_p->bottomfirst,    0);
-    OPTENTRY(0,   "tb",            OPT_FLAG,   &cmdline_p->bottomfirst,    0);
-    OPTENTRY(0,   "headerskip",    OPT_UINT,   &cmdline_p->headerskip,     0);
-    OPTENTRY(0,   "rowskip",       OPT_FLOAT,  &cmdline_p->rowskip,        0);
-    OPTENTRY(0,   "bpp",           OPT_INT,    &cmdline_p->bpp,            0);
-    OPTENTRY(0,   "littleendian",  OPT_FLAG,   &cmdline_p->littleendian,   0);
-    OPTENTRY(0,   "maxval",        OPT_UINT,   &cmdline_p->maxval,         0);
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "bottomfirst",   OPT_FLAG,   &cmdlineP->bottomfirst,
+            NULL,   0);
+    OPTENT3(0,   "bt",            OPT_FLAG,   &cmdlineP->bottomfirst,
+            NULL,   0);
+    OPTENT3(0,   "topbottom",     OPT_FLAG,   &cmdlineP->bottomfirst,
+            NULL,   0);
+    OPTENT3(0,   "tb",            OPT_FLAG,   &cmdlineP->bottomfirst,
+            NULL,   0);
+    OPTENT3(0,   "headerskip",    OPT_UINT,   &cmdlineP->headerskip,
+            NULL,   0);
+    OPTENT3(0,   "rowskip",       OPT_FLOAT,  &cmdlineP->rowskip,
+            NULL,   0);
+    OPTENT3(0,   "bpp",           OPT_INT,    &cmdlineP->bpp,
+            NULL,   0);
+    OPTENT3(0,   "littleendian",  OPT_FLAG,   &cmdlineP->littleendian,
+            NULL,   0);
+    OPTENT3(0,   "maxval",        OPT_UINT,   &cmdlineP->maxval,
+            NULL,   0);
 
     /* Set the defaults */
-    cmdline_p->bottomfirst = FALSE;
-    cmdline_p->headerskip = 0;
-    cmdline_p->rowskip = 0.0;
-    cmdline_p->bpp = 1;
-    cmdline_p->littleendian = 0;
-    cmdline_p->maxval = -1;
+    cmdlineP->bottomfirst = FALSE;
+    cmdlineP->headerskip = 0;
+    cmdlineP->rowskip = 0.0;
+    cmdlineP->bpp = 1;
+    cmdlineP->littleendian = 0;
+    cmdlineP->maxval = -1;
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions2(&argc, argv, opt, 0);
-        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 == 0) {
-        cmdline_p->inputFileName = "-";
-        cmdline_p->autosize = TRUE;
+        cmdlineP->inputFileName = "-";
+        cmdlineP->autosize = TRUE;
     } else if (argc-1 == 1) {
-        cmdline_p->inputFileName = argv[1];
-        cmdline_p->autosize = TRUE;
+        cmdlineP->inputFileName = argv[1];
+        cmdlineP->autosize = TRUE;
     } else if (argc-1 == 2) {
-        cmdline_p->inputFileName = "-";
-        cmdline_p->autosize = FALSE;
-        cmdline_p->width = pm_parse_width(argv[1]);
-        cmdline_p->height = pm_parse_height(argv[2]);
+        cmdlineP->inputFileName = "-";
+        cmdlineP->autosize = FALSE;
+        cmdlineP->width = pm_parse_width(argv[1]);
+        cmdlineP->height = pm_parse_height(argv[2]);
     } else if (argc-1 == 3) {
-        cmdline_p->inputFileName = argv[3];
-        cmdline_p->autosize = FALSE;
-        cmdline_p->width = pm_parse_width(argv[1]);
-        cmdline_p->height = pm_parse_height(argv[2]);
+        cmdlineP->inputFileName = argv[3];
+        cmdlineP->autosize = FALSE;
+        cmdlineP->width = pm_parse_width(argv[1]);
+        cmdlineP->height = pm_parse_height(argv[2]);
     } else
         pm_error("Program takes zero, one, two, or three arguments.  You "
                  "specified %d", argc-1);
 
-    if (cmdline_p->bpp != 1 && cmdline_p->bpp != 2) 
+    if (cmdlineP->bpp != 1 && cmdlineP->bpp != 2) 
         pm_error("Bytes per pixel (-bpp) must be 1 or 2.  You specified %d.",
-                 cmdline_p->bpp);
+                 cmdlineP->bpp);
 
-    if (cmdline_p->maxval == 0)
+    if (cmdlineP->maxval == 0)
         pm_error("Maxval (-maxval) may not be zero.");
 
-    if (cmdline_p->maxval > 255 && cmdline_p->bpp == 1)
+    if (cmdlineP->maxval > 255 && cmdlineP->bpp == 1)
         pm_error("You have specified one byte per pixel, but a maxval "
-                 "too large to fit in one byte: %d", cmdline_p->maxval);
-    if (cmdline_p->maxval > 65535)
+                 "too large to fit in one byte: %d", cmdlineP->maxval);
+    if (cmdlineP->maxval > 65535)
         pm_error("Maxval must be less than 65536.  You specified %d.",
-                 cmdline_p->maxval);
+                 cmdlineP->maxval);
 
-    if (cmdline_p->rowskip && cmdline_p->autosize)
+    if (cmdlineP->rowskip && cmdlineP->autosize)
         pm_error("If you specify -rowskip, you must also give the image "
                  "dimensions.");
-    if (cmdline_p->rowskip && cmdline_p->bottomfirst)
+    if (cmdlineP->rowskip && cmdlineP->bottomfirst)
         pm_error("You canot specify both -rowskip and -bottomfirst.  This is "
                  "a limitation of this program.");
 
diff --git a/converter/pgm/sbigtopgm.c b/converter/pgm/sbigtopgm.c
index dd4e921a..3c223c47 100644
--- a/converter/pgm/sbigtopgm.c
+++ b/converter/pgm/sbigtopgm.c
@@ -1,210 +1,353 @@
 /*
-
     sbigtopgm.c - read a Santa Barbara Instruments Group CCDOPS file
 
-    Note: All SBIG CCD astronomical cameras produce 14 bits or
-	  (the ST-4 and ST-5) or 16 bits (ST-6 and later) per pixel.
-
-		  Copyright (C) 1998 by John Walker
-		       http://www.fourmilab.ch/
+    Note: All SBIG CCD astronomical cameras produce 14 bits
+    (the ST-4 and ST-5) or 16 bits (ST-6 and later) per pixel.
 
     If you find yourself having to add functionality included subsequent
     to the implementation of this program, you can probably find
     documentation of any changes to the SBIG file format on their
     Web site: http://www.sbig.com/
 
+    Copyright (C) 1998 by John Walker
+    http://www.fourmilab.ch/
+
     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
+    notice appear in supporting documentation.  This software is
     provided "as is" without express or implied warranty.
-
 */
 
 #include <string.h>
 
-#include "pgm.h"
+#include "pm_c_util.h"
+#include "mallocvar.h"
 #include "nstring.h"
+#include "shhopt.h"
+#include "pm.h"
+#include "pgm.h"
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to as as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to pm_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 OPTENT3 */
+    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 */
+    
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others */
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("Too many arguments.  The only possible argument is the "
+                     "optional input file name");
+    }
+}
+
+
 
 #define SBIG_HEADER_LENGTH  2048      /* File header length */
 
-/*  looseCanon	--  Canonicalize a line from the file header so
-		    items more sloppily formatted than those
-		    written by CCDOPS are still accepted.  */
 
-static void looseCanon(cp)
-  char *cp;
-{
-    char *op = cp;
+
+static void
+looseCanon(char * const cpArg) {
+/*----------------------------------------------------------------------------
+  Canonicalize a line from the file header so items more sloppily formatted
+  than those written by CCDOPS are still accepted.
+
+  Remove all whitespace and make all letters lowercase.
+
+  Note that the SBIG Type 3 format specification at www.sbig.com in January
+  2015 says header parameter names are capitalized like 'Height'; we change
+  that to "height".
+
+  The spec also says the line ends with LF, then CR (yes, really).  Assuming
+  Caller separates lines at LF, that means we see CR at the beginning of all
+  lines but the first.  We remove that.
+-----------------------------------------------------------------------------*/
+    char * cp;
+    char * op;
     char c;
-    
+
+    cp = cpArg;  /* initial value */
+    op = cpArg;  /* initial value */
+
     while ((c = *cp++) != 0) {
-	if (!ISSPACE(c)) {
-	    if (ISUPPER(c)) {
-		c = tolower(c);
-	    }
-	    *op++ = c;
-	}
+        if (!ISSPACE(c)) {
+            if (ISUPPER(c))
+                c = tolower(c);
+            *op++ = c;
+        }
     }
-    *op++ = 0;
+    *op++ = '\0';
 }
 
-int main(argc, argv)
-  int argc;
-  char* argv[];
-{
-    FILE *ifp;
-    gray *grayrow;
-    register gray *gP;
-    int argn, row;
-    register int col;
-    int maxval;
-    int comp, rows, cols;
-    char header[SBIG_HEADER_LENGTH+1];
-    char *hdr;
-    static char camera[80] = "ST-?";
-
-    pgm_init(&argc, argv);
-
-    argn = 1;
-
-    if (argn < argc) {
-	ifp = pm_openr(argv[argn]);
-	argn++;
-    } else {
-	ifp = stdin;
-    }
 
-    if (argn != argc)
-        pm_usage( "[sbigfile]" );
 
-    if (fread(header, SBIG_HEADER_LENGTH, 1, ifp) < 1) {
-        pm_error("error reading SBIG file header");
-    }
-    header[SBIG_HEADER_LENGTH] = '\0';
+struct SbigHeader {
+/*----------------------------------------------------------------------------
+   The information in an SBIG file header.
+
+   This is only the information this program cares about; the header
+   may have much more information in it.
+-----------------------------------------------------------------------------*/
+    unsigned int rows;
+    unsigned int cols;
+    unsigned int maxval;
+    bool isCompressed;
+    const char * cameraType;
+        /* Null means information not in header */
+};
+
 
-    /*	Walk through the header and parse relevant parameters.	*/
 
-    comp = -1;
-    cols = -1;
-    rows = -1;
+static void
+readSbigHeader(FILE *              const ifP,
+               struct SbigHeader * const sbigHeaderP) {
 
-    /*	The SBIG header specification equivalent to maxval is
+    size_t rc;
+    bool gotCompression;
+    bool gotWidth;
+    bool gotHeight;
+    char * buffer;  /* malloced */
+    char * cursor;
+    bool endOfHeader;
+
+    MALLOCARRAY_NOFAIL(buffer, SBIG_HEADER_LENGTH + 1);
+
+    rc = fread(buffer, SBIG_HEADER_LENGTH, 1, ifP);
+
+    if (rc < 1)
+        pm_error("error reading SBIG file header");
+
+    buffer[SBIG_HEADER_LENGTH] = '\0';
+
+    /*  The SBIG header specification equivalent to maxval is
         "Sat_level", the saturation level of the image.  This
-	specification is optional, and was not included in files
-	written by early versions of CCDOPS. It was introduced when it
-	became necessary to distinguish 14-bit images with a Sat_level
-	of 16383 from 16-bit images which saturate at 65535.  In
-	addition, co-adding images or capturing with Track and
-	Accumulate can increase the saturation level.  Since files
+        specification is optional, and was not included in files
+        written by early versions of CCDOPS. It was introduced when it
+        became necessary to distinguish 14-bit images with a Sat_level
+        of 16383 from 16-bit images which saturate at 65535.  In
+        addition, co-adding images or capturing with Track and
+        Accumulate can increase the saturation level.  Since files
         which don't have a Sat_level line in the header were most
-	probably written by early drivers for the ST-4 or ST-5, it
-	might seem reasonable to make the default for maxval 16383,
-	the correct value for those cameras.  I chose instead to use
-	65535 as the default because the overwhelming majority of
+        probably written by early drivers for the ST-4 or ST-5, it
+        might seem reasonable to make the default for maxval 16383,
+        the correct value for those cameras.  I chose instead to use
+        65535 as the default because the overwhelming majority of
         cameras in use today are 16 bit, and it's possible some
         non-SBIG software may omit the "optional" Sat_level
-	specification.	Also, no harm is done if a larger maxval is
-	specified than appears in the image--a simple contrast stretch
-	will adjust pixels to use the full 0 to maxval range.  The
-	converse, pixels having values greater than maxval, results in
-	an invalid file which may cause problems in programs which
-	attempt to process it.	*/
-
-    maxval = 65535;
-
-    hdr = header;
-
-    for (;;) {
-        char *cp = strchr(hdr, '\n');
-
-	if (cp == NULL) {
-            pm_error("malformed SBIG file header at character %d", hdr - header);
-	}
-	*cp = 0;
-        if (strncmp(hdr, "ST-", 3) == 0  ||
-            (hdr == &header[0] && strstr(hdr,"Image") != NULL)) {
-            char *ep = strchr(hdr + 3, ' ');
-
-	    if (ep != NULL) {
-		*ep = 0;
-		STRSCPY(camera, hdr);
+        specification.  Also, no harm is done if a larger maxval is
+        specified than appears in the image--a simple contrast stretch
+        will adjust pixels to use the full 0 to maxval range.  The
+        converse, pixels having values greater than maxval, results in
+        an invalid file which may cause problems in programs which
+        attempt to process it.
+
+         According to the official specification, the camera type name is the
+         first item in the header, and may or may not start with "ST-".  But
+         this program has historically had an odd method of detecting camera
+         type, which allows any string starting with "ST-" anywhere in the
+         header, and for now we leave that undisturbed.  2015.05.27.
+    */
+
+    gotCompression = false;  /* initial value */
+    gotWidth       = false;  /* initial value */
+    gotHeight      = false;  /* initial value */
+
+    sbigHeaderP->maxval = 65535;  /* initial assumption */
+    sbigHeaderP->cameraType = NULL;  /* initial assumption */
+
+    for (cursor = &buffer[0], endOfHeader = false; !endOfHeader;) {
+        char * const cp = strchr(cursor, '\n');
+
+        if (cp == NULL) {
+            pm_error("malformed SBIG file header at character %u",
+                     (unsigned)(cursor - &buffer[0]));
+        }
+        *cp = '\0';
+        if (strneq(cursor, "ST-", 3) ||
+            (cursor == &buffer[0] && strstr(cursor,"Image") != NULL)) {
+
+            char * const ep = strchr(cursor + 3, ' ');
+
+            if (ep != NULL) {
+                *ep = '\0';
+                sbigHeaderP->cameraType = pm_strdup(cursor);
                 *ep = ' ';
-	    }
-	}
-	looseCanon(hdr);
-        if (strncmp(hdr, "st-", 3) == 0) {
-            comp = strstr(hdr, "compressed") != NULL;
-        } else if (strncmp(hdr, "height=", 7) == 0) {
-	    rows = atoi(hdr + 7);
-        } else if (strncmp(hdr, "width=", 6) == 0) {
-	    cols = atoi(hdr + 6);
-        } else if (strncmp(hdr, "sat_level=", 10) == 0) {
-	    maxval = atoi(hdr + 10);
-        } else if (streq(hdr, "end")) {
-	    break;
-	}
-	hdr = cp + 1;
+            }
+        }
+        
+        looseCanon(cursor);
+            /* Convert from standard SBIG to an internal format */
+
+        if (strneq(cursor, "st-", 3) || cursor == &buffer[0]) {
+            sbigHeaderP->isCompressed =
+                 (strstr(cursor, "compressedimage") != NULL);
+            gotCompression = true;
+        } else if (strneq(cursor, "height=", 7)) {
+            sbigHeaderP->rows = atoi(cursor + 7);
+            gotHeight = true;
+        } else if (strneq(cursor, "width=", 6)) {
+            sbigHeaderP->cols = atoi(cursor + 6);
+            gotWidth = true;
+        } else if (strneq(cursor, "sat_level=", 10)) {
+            sbigHeaderP->maxval = atoi(cursor + 10);
+        } else if (streq("end", cursor)) {
+            endOfHeader = true;
+        }
+        cursor = cp + 1;
     }
 
-    if ((comp == -1) || (rows == -1) || (cols == -1)) {
-        pm_error("required specification missing from SBIG file header");
-    }
+    if (!gotCompression)
+        pm_error("Required 'ST-*' specification missing "
+                 "from SBIG file header");
+    if (!gotHeight)
+        pm_error("required 'height=' specification missing"
+                 "from SBIG file header");
+    if (!gotWidth)
+        pm_error("required 'width=' specification missing "
+                 "from SBIG file header");
+}
 
-    fprintf(stderr, "SBIG %s %dx%d %s image, saturation level = %d.\n",
-        camera, cols, rows, comp ? "compressed" : "uncompressed", maxval);
 
-    if (maxval > PGM_OVERALLMAXVAL) {
-        pm_error("Saturation level (%d levels) is too large.\n"
-                 "This program's limit is %d.", maxval, PGM_OVERALLMAXVAL);
+
+static void
+termSbigHeader(struct SbigHeader const sbigHeader) {
+
+    if (sbigHeader.cameraType)
+        pm_strfree(sbigHeader.cameraType);
+}
+
+
+
+static void
+writeRaster(FILE *            const ifP,
+            struct SbigHeader const hdr,
+            FILE *            const ofP) {
+
+    gray * grayrow;
+    unsigned int row;
+
+    grayrow = pgm_allocrow(hdr.cols);
+
+    for (row = 0; row < hdr.rows; ++row) {
+        bool compthis;
+        unsigned int col;
+
+        if (hdr.isCompressed) {
+            unsigned short rowlen;        /* Compressed row length */
+
+            pm_readlittleshortu(ifP, &rowlen);
+            
+            /*  If compression results in a row length >= the uncompressed
+                row length, that row is output uncompressed.  We detect this
+                by observing that the compressed row length is equal to
+                that of an uncompressed row.
+            */
+
+            if (rowlen == hdr.cols * 2)
+                compthis = false;
+            else
+                compthis = hdr.isCompressed;
+        } else
+            compthis = hdr.isCompressed;
+
+        for (col = 0; col < hdr.cols; ++col) {
+            unsigned short g;
+
+            if (compthis) {
+                if (col == 0) {
+                    pm_readlittleshortu(ifP, &g);
+                } else {
+                    int const delta = getc(ifP);
+
+                    if (delta == 0x80)
+                        pm_readlittleshortu(ifP, &g);
+                    else
+                        g += ((signed char) delta);
+                }
+            } else
+                pm_readlittleshortu(ifP, &g);
+            grayrow[col] = g;
+        }
+        pgm_writepgmrow(ofP, grayrow, hdr.cols, hdr.maxval, 0);
     }
 
-    pgm_writepgminit(stdout, cols, rows, (gray) maxval, 0);
-    grayrow = pgm_allocrow(cols);
-
-#define DOSINT(fp) ((getc(fp) & 0xFF) | (getc(fp) << 8))
-
-    for (row = 0; row < rows; row++) {
-	int compthis = comp;
-
-	if (comp) {
-	    int rowlen = DOSINT(ifp); /* Compressed row length */
-
-	    /*	If compression results in a row length >= the uncompressed
-		row length, that row is output uncompressed.  We detect this
-		by observing that the compressed row length is equal to
-		that of an uncompressed row.  */
-
-	    if (rowlen == cols * 2) {
-		compthis = 0;
-	    }
-	}
-	for (col = 0, gP = grayrow; col < cols; col++, gP++) {
-	    gray g;
-
-	    if (compthis) {
-
-		if (col == 0) {
-		    g = DOSINT(ifp);
-		} else {
-		    int delta = getc(ifp);
-
-		    if (delta == 0x80) {
-			g = DOSINT(ifp);
-		    } else {
-			g += ((signed char) delta);
-		    }
-		}
-	    } else {
-		g = DOSINT(ifp);
-	    }
-	    *gP = g;
-	}
-	pgm_writepgmrow(stdout, grayrow, cols, (gray) maxval, 0);
+    pgm_freerow(grayrow);
+}
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    FILE * ifP;
+    struct CmdlineInfo cmdline;
+    struct SbigHeader hdr;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    readSbigHeader(ifP, &hdr);
+
+    pm_message("SBIG '%s' %ux%u %s image, saturation level = %u",
+               (hdr.cameraType ? hdr.cameraType : "ST-?"),
+               hdr.cols, hdr.rows,
+               hdr.isCompressed ? "compressed" : "uncompressed",
+               hdr.maxval);
+
+    if (hdr.maxval > PGM_OVERALLMAXVAL) {
+        pm_error("Saturation level (%u levels) is too large"
+                 "This program's limit is %u.", hdr.maxval, PGM_OVERALLMAXVAL);
     }
-    pm_close(ifp);
+
+    pgm_writepgminit(stdout, hdr.cols, hdr.rows, hdr.maxval, 0);
+
+    writeRaster(ifP, hdr, stdout);
+
+    termSbigHeader(hdr);
+
+    pm_close(ifP);
     pm_close(stdout);
 
     return 0;
 }
+
+
+
diff --git a/converter/pgm/st4topgm.c b/converter/pgm/st4topgm.c
new file mode 100644
index 00000000..8e4660a3
--- /dev/null
+++ b/converter/pgm/st4topgm.c
@@ -0,0 +1,260 @@
+/*=============================================================================
+                               st4topgm
+===============================================================================
+
+  Convert an SBIG ST-4 image (not to be confused with the more sophisticated
+  SBIG format that every other SBIG camera produces) to PGM.
+
+  By Bryan Henderson January 2015.
+
+  Contributed to the public domain by its author.
+
+  This program was intended to substitute for the program of the same name in
+  the Debian version of Netpbm, by Justin Pryzby <justinpryzby@users.sf.net>
+  in December 2003.
+
+=============================================================================*/
+#include <string.h>
+
+#include "pm_config.h"
+#include "pm_c_util.h"
+#include "pm.h"
+#include "pam.h"
+
+
+
+static unsigned int const st4Height = 165;
+static unsigned int const st4Width  = 192;
+static unsigned int const st4Maxval = 255;
+
+
+
+static void
+validateFileSize(FILE * const ifP) {
+/*----------------------------------------------------------------------------
+   Abort program if *ifP is not the proper size for an ST-4 SBIG file.
+
+   Don't change file position.
+-----------------------------------------------------------------------------*/
+    pm_filepos const st4FileSize = (st4Height+1) * st4Width;
+
+    pm_filepos oldFilePos;
+    pm_filepos endFilePos;
+
+    pm_tell2(ifP, &oldFilePos, sizeof(endFilePos));
+
+    fseek(ifP, 0, SEEK_END);
+
+    pm_tell2(ifP, &endFilePos, sizeof(endFilePos));
+
+    pm_seek2(ifP, &oldFilePos, sizeof(oldFilePos));
+
+    if (endFilePos != st4FileSize)
+        pm_error("File is the wrong size for an ST-4 SBIG file.  "
+                 "It is %u bytes; it should be %u bytes",
+                 (unsigned)endFilePos, (unsigned)st4FileSize);
+}
+
+
+static void
+writeRaster(FILE *       const ifP,
+            struct pam * const pamP) {
+
+    tuple * tuplerow;
+    unsigned int row;
+
+    tuplerow = pnm_allocpamrow(pamP);
+
+    for (row = 0; row < st4Height; ++row) {
+        unsigned int col;
+
+        for (col = 0; col < st4Width; ++col) {
+            char c;
+
+            pm_readchar(ifP, &c);
+
+            tuplerow[col][0] = (unsigned char)c;
+        }
+        pnm_writepamrow(pamP, tuplerow);
+    }
+
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+struct St4Footer {
+/*----------------------------------------------------------------------------
+   The information in an ST-4 SBIG footer.
+-----------------------------------------------------------------------------*/
+    /* Note that numerical information is in decimal text, because we're lazy.
+    */
+
+    char comment[78+1];
+    char exposureTime[10+1];
+    char focalLength[10+1];
+    char apertureArea[10+1];
+    char calibrationFactor[10+1];
+};
+
+
+
+static void
+stripTrailing(char * const arg) {
+
+    if (strlen(arg) > 0) {
+        char * p;
+        for (p = arg + strlen(arg); p > arg && *(p-1) == ' '; --p);
+
+        *p = '\0';
+    }
+}
+
+
+
+static void
+stripLeading(char * const arg) {
+
+    const char * p;
+
+    for (p = &arg[0]; *p == ' '; ++p);
+
+    if (p > arg)
+        memmove(arg, p, strlen(p) + 1);
+}
+
+
+
+static void
+readFooter(FILE *             const ifP,
+           struct St4Footer * const footerP) {
+/*----------------------------------------------------------------------------
+   Read the footer of the ST-4 image from *ifP, assuming *ifP is positioned
+   to the footer.
+
+   Return its contents as *footerP.
+-----------------------------------------------------------------------------*/
+    char buffer[192];
+    size_t bytesReadCt;
+
+    /* The footer is laid out as follows.
+
+       off len description
+       --- --- -----------
+       000   1 Signature: 'v'
+       001  78 Freeform comment
+       079  10 Exposure time in 1/100s of a second
+       089  10 Focal length in inches
+       099  10 Aperture area in square inches
+       109  10 Calibration factor
+       119  73 Reserved
+
+       Note tha the footer is the same length as a raster row.
+    */
+
+    bytesReadCt = fread(buffer, 1, sizeof(buffer), ifP);
+
+    if (bytesReadCt != 192)
+        pm_error("Failed to read footer of image");
+
+    if (buffer[0] != 'v')
+        pm_error("Input is not an ST-4 file.  We know because the "
+                 "signature byte (first byte of the footer) is not 'v'");
+
+    buffer[191] = '\0';
+    memmove(footerP->comment, &buffer[1], 78);
+    footerP->comment[78] = '\0';
+    stripTrailing(footerP->comment);
+
+    memmove(footerP->exposureTime, &buffer[79], 10);
+    footerP->exposureTime[10] = '\0';
+    stripLeading(footerP->exposureTime);
+
+    memmove(footerP->focalLength, &buffer[89], 10);
+    footerP->focalLength[10] = '\0';
+    stripLeading(footerP->focalLength);
+
+    memmove(footerP->apertureArea, &buffer[99], 10);
+    footerP->apertureArea[10] = '\0';
+    stripLeading(footerP->apertureArea);
+
+    memmove(footerP->calibrationFactor, &buffer[109], 10);
+    footerP->calibrationFactor[10] = '\0';
+    stripLeading(footerP->calibrationFactor);
+}
+
+
+
+static void
+reportFooter(struct St4Footer const footer) {
+
+	pm_message("Comment:                 %s", footer.comment);
+
+	pm_message("Exposure time (1/100 s): %s", footer.exposureTime);
+
+	pm_message("Focal length (in):       %s", footer.focalLength);
+
+	pm_message("Aperture area (sq in):   %s", footer.apertureArea);
+
+	pm_message("Calibration factor:      %s", footer.calibrationFactor);
+}
+
+
+
+int
+main(int argc, const char **argv) {
+
+    FILE * ifP;
+    const char * inputFileName;
+    struct pam outpam;
+    struct St4Footer footer;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        inputFileName = "-";
+    else {
+        inputFileName = argv[1];
+        if (argc-1 > 1)
+            pm_error("Too many arguments: %u.  "
+                     "The only possible argument is the "
+                     "optional input file name", argc-1);
+    }        
+
+    /* We check the file size to catch the common problem of the input not
+       being valid ST-4 SBIG input.  Unlike most formats, this one does not
+       have any signature at the head of the file.
+
+       More checks on the validity of the format happens when we process
+       the image footer.
+    */
+
+    ifP = pm_openr_seekable(inputFileName);
+
+    validateFileSize(ifP);
+
+    outpam.size = sizeof(outpam);
+    outpam.len = PAM_STRUCT_SIZE(maxval);
+    outpam.file = stdout;
+    outpam.format = PGM_FORMAT;
+    outpam.plainformat = false;
+    outpam.height = st4Height;
+    outpam.width = st4Width;
+    outpam.depth = 1;
+    outpam.maxval = st4Maxval;
+
+    pnm_writepaminit(&outpam);
+
+    writeRaster(ifP, &outpam);
+
+    readFooter(ifP, &footer);
+
+    reportFooter(footer);
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
+
+
diff --git a/converter/ppm/411toppm.c b/converter/ppm/411toppm.c
index 6ece4c4b..eb2372a5 100644
--- a/converter/ppm/411toppm.c
+++ b/converter/ppm/411toppm.c
@@ -30,7 +30,7 @@
 
    Bryan Henderson reworked the program to use the Netpbm libraries to
    create the PPM output and follow some other Netpbm conventions.
-   2001-03-03.  Bryan's contribution is public domain.  
+   2001-03-03.  Bryan's contribution is public domain.
 */
 /*
  * Copyright (c) 1995 The Regents of the University of California.
@@ -56,22 +56,21 @@
  * HEADER FILES *
  *==============*/
 #include <stdio.h>
-#include <stdlib.h>
 
 #include "pm_c_util.h"
-#include "ppm.h"
-#include "shhopt.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "ppm.h"
 
 typedef unsigned char uint8;
 
 #define CHOP(x)     ((x < 0) ? 0 : ((x > 255) ? 255 : x))
 
-struct cmdline_info {
+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 * inputFileName;
     int width;
     int height;
 };
@@ -79,38 +78,42 @@ struct cmdline_info {
 
 
 static void
-parse_command_line(int argc, char ** argv,
-                   struct cmdline_info *cmdline_p) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
 
-    optStruct *option_def = malloc(100*sizeof(optStruct));
+    optEntry * option_def;
         /* Instructions to OptParseOptions2 on how to parse our options.
          */
-    optStruct2 opt;
+    optStruct3 opt;
 
     unsigned int option_def_index;
 
-    option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENTRY(0,   "width",      OPT_INT,    &cmdline_p->width,          0);
-    OPTENTRY(0,   "height",     OPT_INT,    &cmdline_p->height,         0);
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "width",      OPT_INT,    &cmdlineP->width,  NULL,   0);
+    OPTENT3(0,   "height",     OPT_INT,    &cmdlineP->height, NULL,   0);
 
     /* Set the defaults */
-    cmdline_p->width = 64;
-    cmdline_p->height = 48;
+    cmdlineP->width = 64;
+    cmdlineP->height = 48;
 
     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 */
     
-    optParseOptions2(&argc, argv, opt, 0);
-        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (cmdline_p->width <= 0)
+    if (cmdlineP->width <= 0)
         pm_error("-width must be positive.");
-    if (cmdline_p->height <= 0)
+    if (cmdlineP->width %4 != 0)
+        pm_error("-width must be a multiple of 4.");
+    if (cmdlineP->height <= 0)
         pm_error("-height must be positive.");
 
     if (argc > 2)
@@ -118,145 +121,111 @@ parse_command_line(int argc, char ** argv,
                  "You supplied %d", argc-1);
     else {
         if (argc > 1)
-            cmdline_p->input_filespec = argv[1];
+            cmdlineP->inputFileName = argv[1];
         else
-            cmdline_p->input_filespec = "-";
+            cmdlineP->inputFileName = "-";
     }
+    free(option_def);
 }
 
 
 
-static void 
-ReadYUV(FILE *fpointer, const int width, const int height, 
-        uint8 ** const orig_y, uint8 ** const orig_cb, 
-        uint8 ** const orig_cr) {
+static void
+ReadYUV(FILE  * const ifP,
+        uint8 * const inbuff) {
+
+    size_t bytesRead;
 
-    int y, x, i;
+    bytesRead = fread(inbuff, 1, 6, ifP);
 
-    for (y = 0; y < height; y++) {
-        for (x = 0, i = 0; x < width; x+=4, i++) {
-            fread(orig_y[y]+x , 1, 4, fpointer);
-            fread(orig_cb[y]+i, 1, 1, fpointer);
-            fread(orig_cr[y]+i, 1, 1, fpointer);
-        }
-    }
+    if (bytesRead != 6 ) {
+        if (feof(ifP))
+            pm_error("Premature end of input.");
+        else
+            pm_error("Error reading input.");
+     }
 }
 
 
 
 static void
-AllocYCC(const int width, const int height,
-         uint8 *** const orig_yP, uint8 *** const orig_crP, 
-         uint8 *** const orig_cbP) {
-    int y;
-
-    MALLOCARRAY_NOFAIL(*orig_yP, height);
-    for (y = 0; y < height; y++) 
-        MALLOCARRAY_NOFAIL((*orig_yP)[y], width);
-
-    MALLOCARRAY_NOFAIL(*orig_crP, height);
-    for (y = 0; y < height; y++) 
-        MALLOCARRAY_NOFAIL((*orig_crP)[y], width/4);
-
-    MALLOCARRAY_NOFAIL(*orig_cbP, height);
-    for (y = 0; y < height; y++) 
-        MALLOCARRAY_NOFAIL((*orig_cbP)[y], width/4);
-}
+YUVtoPPM(FILE  * const ifP,
+         int     const width,
+         int     const height,
+         pixel * const pixrow ) {
 
+    unsigned int col;
 
+    for (col = 0; col < width; ++col) {
 
-static void 
-YUVtoPPM(const int width, const int height, 
-         uint8 ** const orig_y, 
-         uint8 ** const orig_cb,
-         uint8 ** const orig_cr,
-         pixel ** const ppm_image
-         ) {
-    int     **Y, **U, **V;
-    int    y;
+        uint8 inbuff[6];
 
-    /* first, allocate tons of memory */
+        uint8 * const origY  = &inbuff[0];
+        uint8 * const origCb = &inbuff[4];
+        uint8 * const origCr = &inbuff[5];
+        int   y, u, v;
+        int32_t tempR, tempG, tempB;
+        pixval r, g, b;
 
-    MALLOCARRAY_NOFAIL(Y, height);
-    for (y = 0; y < height; y++) 
-        MALLOCARRAY_NOFAIL(Y[y], width);
-    
-    MALLOCARRAY_NOFAIL(U, height);
-    for (y = 0; y < height; y++) 
-        MALLOCARRAY_NOFAIL(U[y], width / 4);
-
-    MALLOCARRAY_NOFAIL(V, height);
-    for (y = 0; y < height; y++) 
-        MALLOCARRAY_NOFAIL(V[y], width/4);
-
-	for ( y = 0; y < height; y ++ ) {
-        int x;
-	    for ( x = 0; x < width/4; x ++ ) {
-            U[y][x] = orig_cb[y][x] - 128;
-            V[y][x] = orig_cr[y][x] - 128;
-	    }
-    }
-	for ( y = 0; y < height; y ++ ) {
-        int x;
-	    for ( x = 0; x < width; x ++ )
-	    {
-            Y[y][x] = orig_y[y][x] - 16;
-	    }
-    }
-    for ( y = 0; y < height; y++ ) {
-        int x;
-        for ( x = 0; x < width; x++ ) {
-            pixval r, g, b;
-            long   tempR, tempG, tempB;
-            /* look at yuvtoppm source for explanation */
-            
-            tempR = 104635*V[y][x/4];
-            tempG = -25690*U[y][x/4] + -53294 * V[y][x/4];
-            tempB = 132278*U[y][x/4];
-            
-            tempR += (Y[y][x]*76310);
-            tempG += (Y[y][x]*76310);
-            tempB += (Y[y][x]*76310);
-            
-            r = CHOP((int)(tempR >> 16));
-            g = CHOP((int)(tempG >> 16));
-            b = CHOP((int)(tempB >> 16));
-
-            PPM_ASSIGN(ppm_image[y][x], r, g, b);
+        if (col % 4 == 0) {
+            ReadYUV(ifP, inbuff);
+            u = origCb[0] - 128;
+            v = origCr[0] - 128;
         }
+
+        y = origY[col % 4] - 16;
+
+        tempR = 104635 * v              + y * 76310;
+        tempG = -25690 * u + -53294 * v + y * 76310;
+        tempB = 132278 * u              + y * 76310;
+
+        r = CHOP((int)(tempR >> 16));
+        g = CHOP((int)(tempG >> 16));
+        b = CHOP((int)(tempB >> 16));
+        
+        PPM_ASSIGN(pixrow[col], r, g, b);
     }
-    /* We really should free the Y, U, and V arrays now */
 }
 
 
 
 int
-main(int argc, char **argv) {
-    FILE *infile;
-    struct cmdline_info cmdline;
-    uint8 **orig_y, **orig_cb, **orig_cr;
-    pixel **ppm_image;
+main(int argc, const char **argv) {
 
-    ppm_init(&argc, argv);
+    pixval const maxval = 255;
+    struct CmdlineInfo cmdline;
+    FILE  * ifP;
+    pixel * pixrow;
+    unsigned int row;
 
-    parse_command_line(argc, argv, &cmdline);
+    pm_proginit(&argc, argv);
 
-    AllocYCC(cmdline.width, cmdline.height, &orig_y, &orig_cr, &orig_cb);
-    ppm_image = ppm_allocarray(cmdline.width, cmdline.height);
+    parseCommandLine(argc, argv, &cmdline);
 
-    pm_message("Reading (%dx%d):  %s\n", cmdline.width, cmdline.height, 
-               cmdline.input_filespec);
-    infile = pm_openr(cmdline.input_filespec);
-    ReadYUV(infile, cmdline.width, cmdline.height, orig_y, orig_cb, orig_cr);
-    pm_close(infile);
+    pixrow = ppm_allocrow(cmdline.width);
 
-    YUVtoPPM(cmdline.width, cmdline.height, orig_y, orig_cb, orig_cr, 
-             ppm_image);
+    pm_message("Reading (%ux%u): '%s'", cmdline.width, cmdline.height,
+               cmdline.inputFileName);
 
-    ppm_writeppm(stdout, ppm_image, cmdline.width, cmdline.height, 255, 0);
+    ifP = pm_openr(cmdline.inputFileName);
 
-    ppm_freearray(ppm_image, cmdline.height);
+    ppm_writeppminit(stdout, cmdline.width, cmdline.height, maxval, 0);
+
+    for (row = 0; row < cmdline.height; ++row) {
+        YUVtoPPM(ifP, cmdline.width, cmdline.height, pixrow);
+        ppm_writeppmrow(stdout, pixrow, cmdline.width, maxval, 0);
+    }
+
+    if (fgetc(ifP) != EOF)
+        pm_message("Extraneous data at end of image.");
+
+    pm_close(ifP);
+    ppm_freerow(pixrow);
 
     return 0;
 }
 
+/*
+   By default a .411 file is width=64, height=48, 4608 bytes.
+   There is no header.
+*/
diff --git a/converter/ppm/Makefile b/converter/ppm/Makefile
index adc3a400..003ef8d3 100644
--- a/converter/ppm/Makefile
+++ b/converter/ppm/Makefile
@@ -12,15 +12,16 @@ SUBDIRS = hpcdtoppm ppmtompeg
 PORTBINARIES =	411toppm eyuvtoppm gouldtoppm ilbmtoppm imgtoppm \
 		leaftoppm mtvtoppm neotoppm \
 		pcxtoppm pc1toppm pi1toppm picttoppm pjtoppm \
-		ppmtoacad ppmtoarbtxt \
+		ppmtoacad ppmtoapplevol ppmtoarbtxt ppmtoascii \
 		ppmtobmp ppmtoeyuv ppmtogif ppmtoicr ppmtoilbm \
 		ppmtoleaf ppmtolj ppmtomitsu ppmtoneo \
 		ppmtopcx ppmtopi1 ppmtopict ppmtopj \
-		ppmtopjxl ppmtoppm ppmtopuzz ppmtorgb3 ppmtosixel ppmtoterm \
+		ppmtopjxl ppmtoppm ppmtopuzz ppmtorgb3 \
+		ppmtosixel ppmtospu ppmtoterm \
 		ppmtowinicon ppmtoxpm ppmtoyuv ppmtoyuvsplit \
 		qrttoppm rawtoppm rgb3toppm spctoppm \
 		sputoppm tgatoppm winicontoppm ximtoppm xpmtoppm xvminitoppm \
-		yuvtoppm yuvsplittoppm
+		yuvsplittoppm yuvtoppm 
 
 MATHBINARIES = sldtoppm 
 
diff --git a/converter/ppm/eyuvtoppm.c b/converter/ppm/eyuvtoppm.c
index 33d57409..910a125b 100644
--- a/converter/ppm/eyuvtoppm.c
+++ b/converter/ppm/eyuvtoppm.c
@@ -36,9 +36,9 @@
 #include <unistd.h>
 
 #include "pm_c_util.h"
-#include "ppm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
+#include "ppm.h"
 
 typedef unsigned char uint8;
 
@@ -46,11 +46,11 @@ typedef unsigned char uint8;
 
 
 
-struct cmdline_info {
+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;  /* Filespecs of input file */
+    const char * inputFileName;  /* Name of input file */
     unsigned int width;
     unsigned int height;
 };
@@ -59,11 +59,13 @@ struct cmdline_info {
 
 static void
 parseCommandLine(int argc, char ** argv,
-                 struct cmdline_info *cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 
     optStruct3 opt;   /* Set by OPTENT3 */
     unsigned int option_def_index;
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3('w', "width",     OPT_UINT,  &cmdlineP->width,   NULL,         0);
@@ -77,7 +79,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = TRUE;
     opt.allowNegNum = FALSE;
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
 
     if (cmdlineP->width == 0)
         pm_error("The width cannot be zero.");
@@ -92,32 +94,34 @@ parseCommandLine(int argc, char ** argv,
 
 
     if (argc-1 == 0) 
-        cmdlineP->input_filespec = "-";
+        cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
-                 "specified %d", argc-1);
+                 "specified %u", argc-1);
     else
-        cmdlineP->input_filespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
 
+    free(option_def);
 }
 
 
 
 static uint8 ** 
-AllocUint8Array(unsigned int const cols, unsigned int const rows) {
+allocUint8Array(unsigned int const cols,
+                unsigned int const rows) {
 
-    uint8 **retval;
-    unsigned int y;
+    uint8 ** retval;
+    unsigned int row;
 
     MALLOCARRAY(retval, rows);
     if (retval == NULL)
-        pm_error("Unable to allocate storage for %d x %d byte array.",
+        pm_error("Unable to allocate storage for %u x %u byte array.",
                  cols, rows);
 
-    for (y = 0; y < rows; y++) {
-        MALLOCARRAY(retval[y], cols);
-        if (retval[y] == NULL)
-            pm_error("Unable to allocate storage for %d x %d byte array.",
+    for (row = 0; row < rows; ++row) {
+        MALLOCARRAY(retval[row], cols);
+        if (retval[row] == NULL)
+            pm_error("Unable to allocate storage for %u x %u byte array.",
                      cols, rows);
     }
     return retval;
@@ -125,216 +129,191 @@ AllocUint8Array(unsigned int const cols, unsigned int const rows) {
 
 
 
-static int ** 
-AllocIntArray(unsigned int const cols, unsigned int const rows) {
+static void 
+freeUint8Array(uint8 **     const array,
+               unsigned int const rows) {
 
-    int **retval;
-    unsigned int y;
+    unsigned int row;
 
-    MALLOCARRAY(retval, rows);
-    if (retval == NULL)
-        pm_error("Unable to allocate storage for %d x %d byte array.",
-                 cols, rows);
+    for (row = 0; row < rows; ++row)
+        free(array[row]);
 
-    for (y = 0; y < rows; y++) {
-        MALLOCARRAY(retval[y], cols);
-        if (retval[y] == NULL)
-            pm_error("Unable to allocate storage for %d x %d byte array.",
-                     cols, rows);
-    }
-    return retval;
+    free(array);
 }
 
 
 
 static void
-allocateStorage(int const cols, int const rows,
-                int *** const YP, int *** const UP, int *** const VP,
-                pixel *** const pixelsP, 
-                uint8 *** const orig_yP, uint8 *** const orig_cbP,
-                uint8 *** const orig_crP) {
-
-    *YP = AllocIntArray(cols, rows);
-    *UP = AllocIntArray(cols, rows);
-    *VP = AllocIntArray(cols, rows);
-
-    *pixelsP = ppm_allocarray(cols, rows);
-
-    *orig_yP  = AllocUint8Array(cols, rows);
-    *orig_cbP = AllocUint8Array(cols, rows);
-    *orig_crP = AllocUint8Array(cols, rows);
-}
-
-
-
-static void 
-FreeArray(void ** const array, unsigned int const rows) {
-
-    unsigned int y;
-
-    for (y = 0; y < rows; y++)
-        free(array[y]);
-    free(array);
+allocateStorage(unsigned int const cols,
+                unsigned int const rows,
+                uint8 ***    const orig_yP,
+                uint8 ***    const orig_cbP,
+                uint8 ***    const orig_crP) {
+
+    *orig_yP  = allocUint8Array(cols, rows);
+    *orig_cbP = allocUint8Array(cols, rows);
+    *orig_crP = allocUint8Array(cols, rows);
 }
 
 
 
 static void
-freeStorage(int const rows,
-            int ** const Y, int ** const U, int ** const V,
-            pixel ** const pixels, 
-            uint8 ** const orig_y, uint8 ** const orig_cb,
-            uint8 ** const orig_cr) {
+freeStorage(unsigned int const rows,
+            uint8 **     const orig_y,
+            uint8 **     const orig_cb,
+            uint8 **     const orig_cr) {
     
-    FreeArray((void**) orig_y, rows); 
-    FreeArray((void**) orig_cb, rows); 
-    FreeArray((void**) orig_cr, rows);
-
-    ppm_freearray(pixels, rows);
+    freeUint8Array(orig_y,  rows); 
+    freeUint8Array(orig_cb, rows); 
+    freeUint8Array(orig_cr, rows);
 
-    FreeArray((void**) Y, rows);
-    FreeArray((void**) U, rows);
-    FreeArray((void**) V, rows);
 }
 
 
 
 static void 
-YUVtoPPM(unsigned int const cols, unsigned int const rows,
-         uint8 ** const orig_y, uint8 ** const orig_cb, uint8 ** const orig_cr,
-         pixel ** const pixels, 
-         int ** const Y, int ** const U, int ** const V) {
+YUVtoPPM(FILE *       const ofP,
+         unsigned int const cols,
+         unsigned int const rows,
+         uint8 **     const orig_y,
+         uint8 **     const orig_cb,
+         uint8 **     const orig_cr) { 
 /*----------------------------------------------------------------------------
    Convert the YUV image in arrays orig_y[][], orig_cb[][], and orig_cr[][]
-   to a PPM image in the array (already allocated) pixels[][].
-
-   Use the preallocated areas Y[][], U[][], and V[][] for working space.
+   to a PPM image and write it to file *ofP.
 -----------------------------------------------------------------------------*/
+    pixel * const pixrow = ppm_allocrow(cols);
     
-    int y;
+    unsigned int row;
 
-    for ( y = 0; y < rows/2; y ++ ) {
-        int x;
-        for ( x = 0; x < cols/2; x ++ ) {
-            U[y][x] = orig_cb[y][x] - 128;
-            V[y][x] = orig_cr[y][x] - 128;
-        }
-    }
+    ppm_writeppminit(ofP, cols, rows, 255, FALSE);
 
-    for ( y = 0; y < rows; y ++ ) {
-        int x;
-        for ( x = 0; x < cols; x ++ ) 
-            Y[y][x] = orig_y[y][x] - 16;
-    }
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
 
-    for ( y = 0; y < rows; y++ ) {
-        int x;
-        for ( x = 0; x < cols; x++ ) {
+        for (col = 0; col < cols; ++col) {
+            int const y =  orig_y[row][col] - 16;
+            int const u =  orig_cb[row/2][col/2] - 128;
+            int const v =  orig_cr[row/2][col/2] - 128;
             long   tempR, tempG, tempB;
-            int     r, g, b;
+            int    r, g, b;
             /* look at yuvtoppm source for explanation */
 
-            tempR = 104635*V[y/2][x/2];
-            tempG = -25690*U[y/2][x/2] + -53294 * V[y/2][x/2];
-            tempB = 132278*U[y/2][x/2];
-
-            tempR += (Y[y][x]*76310);
-            tempG += (Y[y][x]*76310);
-            tempB += (Y[y][x]*76310);
+            tempR = 104635*v + 76310*y;
+            tempG = -25690*u + -53294*v + 76310*y;
+            tempB = 132278*u + 76310*y;
             
             r = CHOP((int)(tempR >> 16));
             g = CHOP((int)(tempG >> 16));
             b = CHOP((int)(tempB >> 16));
             
-            PPM_ASSIGN(pixels[y][x], r, g, b);
+            PPM_ASSIGN(pixrow[col], r, g, b);
         }
+        ppm_writeppmrow(stdout, pixrow, cols, 255, FALSE);
     }
+    ppm_freerow(pixrow);
 }
 
 
 
 static void 
-ReadYUV(FILE * const yuvfile,
-        unsigned int const cols, unsigned int const rows,
-        uint8 ** const orig_y, 
-        uint8 ** const orig_cb, 
-        uint8 ** const orig_cr,
-        bool * const eofP) {
-
-    unsigned int y;
-    int c;
-
-    c = fgetc(yuvfile);
-    if (c < 0)
-        *eofP = TRUE;
-    else {
-        *eofP = FALSE;
-        ungetc(c, yuvfile);
+ReadYUV(FILE *       const ifP,
+        unsigned int const cols,
+        unsigned int const rows,
+        uint8 **     const orig_y, 
+        uint8 **     const orig_cb, 
+        uint8 **     const orig_cr,
+        bool *       const eofP) {
+
+    unsigned int row;
+    unsigned int totalRead;
+    bool eof;
+
+    eof = false;  /* initial value */
+    totalRead = 0;  /* initial value */
+
+    for (row = 0; row < rows && !eof; ++row) {        /* Y */
+        size_t bytesRead;
+
+        bytesRead = fread(orig_y[row], 1, cols, ifP);
+        totalRead += bytesRead;
+        if (bytesRead != cols)
+            eof = true;
     }
-    if (!*eofP) {
-        for (y = 0; y < rows; y++)            /* Y */
-            fread(orig_y[y], 1, cols, yuvfile);
         
-        for (y = 0; y < rows / 2; y++)            /* U */
-            fread(orig_cb[y], 1, cols / 2, yuvfile);
+    for (row = 0; row < rows / 2 && !eof; ++row) {  /* U */
+        size_t bytesRead;
+
+        bytesRead = fread(orig_cb[row], 1, cols / 2, ifP);
+        totalRead += bytesRead;
+        if (bytesRead != cols / 2)
+            eof = true;
+    }
         
-        for (y = 0; y < rows / 2; y++)            /* V */
-            fread(orig_cr[y], 1, cols / 2, yuvfile);
-        if (feof(yuvfile))
-            pm_error("Premature end of file reading EYUV input file");
+    for (row = 0; row < rows / 2 && !eof; ++row) { /* V */
+        size_t bytesRead;
+
+        bytesRead = fread(orig_cr[row], 1, cols / 2, ifP);
+        totalRead += bytesRead;
+        if (bytesRead != cols / 2)
+            eof = true;
     }
+
+    if (eof) {
+        if (totalRead == 0)
+            *eofP = TRUE;
+        else
+            pm_error("Premature end of file reading EYUV input file");
+    } else
+        *eofP = FALSE;
 }
 
 
 
 int
-main(int argc, char **argv) {
+main(int argc, const char **argv) {
 
-    FILE *ifp;
-    struct cmdline_info cmdline;
+    FILE * ifP;
+    struct CmdlineInfo cmdline;
     unsigned int frameSeq;
 
     /* The following are addresses of malloc'ed storage areas for use by
        subroutines.
     */
-    int ** Y;
-    int ** U;
-    int ** V;
-    uint8 **orig_y, **orig_cb, **orig_cr;
-    pixel ** pixels;
+    uint8 ** orig_y;
+    uint8 ** orig_cb;
+    uint8 ** orig_cr;
+    bool eof;
 
-    ppm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
-    parseCommandLine(argc, argv, &cmdline);
+    parseCommandLine(argc, (char **)argv, &cmdline);
 
-    /* Allocate all the storage once, to save time. */
+    /* Allocate all the storage at once, to save time. */
     allocateStorage(cmdline.width, cmdline.height,
-                    &Y, &U, &V, &pixels, &orig_y, &orig_cb, &orig_cr);
+                    &orig_y, &orig_cb, &orig_cr);
 
-    ifp = pm_openr(cmdline.input_filespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
-    for (frameSeq = 0; !feof(ifp); frameSeq++) {
-        bool eof;
+    for (frameSeq = 0, eof = false; !eof; ++frameSeq) {
 
-        ReadYUV(ifp, cmdline.width, cmdline.height, 
+        ReadYUV(ifP, cmdline.width, cmdline.height, 
                 orig_y, orig_cb, orig_cr, &eof);
+
         if (!eof) {
             pm_message("Converting Frame %u", frameSeq);
 
-            YUVtoPPM(cmdline.width, cmdline.height, orig_y, orig_cb, orig_cr, 
-                     pixels, Y, U, V);
-            ppm_writeppm(stdout, pixels, cmdline.width, cmdline.height, 
-                         255, FALSE);
-        }
+            YUVtoPPM(stdout, cmdline.width, cmdline.height,
+                     orig_y, orig_cb, orig_cr);
+        } else if (frameSeq == 0)
+            pm_error("Empty EYUV input file");
     }
 
-    freeStorage(cmdline.height, Y, U, V, pixels, orig_y, orig_cb, orig_cr);
-
-    pm_close(ifp);
-    exit(0);
-}
-
+    freeStorage(cmdline.height, orig_y, orig_cb, orig_cr);
 
+    pm_close(ifP);
 
+    return 0;
+}
 
 
diff --git a/converter/ppm/hpcdtoppm/pcdovtoppm b/converter/ppm/hpcdtoppm/pcdovtoppm
index 1f8b3006..dbf6f53d 100755
--- a/converter/ppm/hpcdtoppm/pcdovtoppm
+++ b/converter/ppm/hpcdtoppm/pcdovtoppm
@@ -195,7 +195,7 @@ if [ ${#imagefiles[*]} -gt 0 ] ; then
     rowfiles="$rowfiles $rowfile"
 fi
 
-if [ ${#rowfiles[*]} == 1 ]; then
+if [ ${#rowfiles[*]} = 1 ]; then
     cat $rowfiles
 else
     if [ "$colors" = "n" ] ; then
diff --git a/converter/ppm/ilbm.h b/converter/ppm/ilbm.h
index 68657956..dbe47758 100644
--- a/converter/ppm/ilbm.h
+++ b/converter/ppm/ilbm.h
@@ -23,7 +23,7 @@ typedef struct {
 #define mskNone                 0
 #define mskHasMask              1
 #define mskHasTransparentColor  2
-#define mskLasso                3       /* not supported */
+#define mskLasso                3       /* can't handle this */
 #define mskMAXKNOWN             mskLasso
 static const char * mskNAME[] = { 
     "none", "mask plane", "transparent color", "lasso" 
@@ -127,8 +127,8 @@ typedef struct {
 #define CLUT_RED    1
 #define CLUT_GREEN  2
 #define CLUT_BLUE   3
-#define CLUT_HUE    4   /* not supported */
-#define CLUT_SAT    5   /* not supported */
+#define CLUT_HUE    4   /* can't handle this */
+#define CLUT_SAT    5   /* can't handle this */
 
 
 /* unofficial DCOL chunk for direct-color */
diff --git a/converter/ppm/ilbmtoppm.c b/converter/ppm/ilbmtoppm.c
index 209f53c0..280ca939 100644
--- a/converter/ppm/ilbmtoppm.c
+++ b/converter/ppm/ilbmtoppm.c
@@ -9,7 +9,7 @@
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.
 **
-** Modified by Mark Thompson on 10/4/90 to accomodate 24-bit IFF files
+** Modified by Mark Thompson on 10/4/90 to accommodate 24-bit IFF files
 ** as used by ASDG, NewTek, etc.
 **
 ** Modified by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
@@ -89,11 +89,10 @@ static unsigned char *ilbmrow;
 static pixel *pixelrow;
 static FILE *maskfile = NULL;
 static bit *maskrow = NULL;
-static short wrotemask = 0;
+static bool wrotemask;
 static IFF_ID typeid;       /* ID_ILBM, ID_RGBN, ID_RGB8 */
 
-static pixel *transpColor = NULL;       /* transparent color */
-static char *transpName = NULL;
+static char *transpName = NULL;  /* -transparent option value */
 
 static bool debug = FALSE;
 
@@ -192,8 +191,8 @@ read_bytes(FILE *          const ifP,
 
 
 static unsigned char
-get_byte(ifp, iffid, counter)
-    FILE* ifp;
+get_byte(ifP, iffid, counter)
+    FILE* ifP;
     IFF_ID iffid;
     long *counter;
 {
@@ -204,9 +203,9 @@ get_byte(ifp, iffid, counter)
             pm_error("insufficient data in %s chunk", ID2string(iffid));
         --(*counter);
     }
-    i = getc(ifp);
+    i = getc(ifP);
     if( i == EOF )
-        readerr(ifp, iffid);
+        readerr(ifP, iffid);
 
     return (unsigned char) i;
 }
@@ -311,7 +310,7 @@ display_chunk(FILE *        const ifP,
 
 
 static void
-read_cmap(FILE *     const ifp,
+read_cmap(FILE *     const ifP,
           IFF_ID     const iffid,
           long       const chunksize,
           ColorMap * const cmap) {
@@ -321,7 +320,7 @@ read_cmap(FILE *     const ifp,
     colors = chunksize / 3;
     if( colors == 0 ) {
         pm_error("warning - empty %s colormap", ID2string(iffid));
-        skip_chunk(ifp, iffid, chunksize);
+        skip_chunk(ifP, iffid, chunksize);
     } else {
         unsigned int i;
         if( cmap->color )               /* prefer CMAP-chunk over CMYK-chunk */
@@ -331,30 +330,30 @@ read_cmap(FILE *     const ifp,
         
         for( i = 0; i < colors; ++i ) {
             int r, g, b;
-            r = get_byte(ifp, iffid, &chunksize);
-            g = get_byte(ifp, iffid, &chunksize);
-            b = get_byte(ifp, iffid, &chunksize);
+            r = get_byte(ifP, iffid, &chunksize);
+            g = get_byte(ifP, iffid, &chunksize);
+            b = get_byte(ifP, iffid, &chunksize);
             PPM_ASSIGN(cmap->color[i], r, g, b);
         }
-        chunk_end(ifp, iffid, chunksize);
+        chunk_end(ifP, iffid, chunksize);
     }
 }
 
 
 
 static void
-read_cmyk(FILE *     const ifp,
+read_cmyk(FILE *     const ifP,
           IFF_ID     const iffid,
           long       const chunksize,
           ColorMap * const cmap) {
 
     if( HAS_COLORMAP(cmap) ) {      /* prefer RGB color map */
-        skip_chunk(ifp, iffid, chunksize);
+        skip_chunk(ifP, iffid, chunksize);
     } else {
         long const colors = chunksize/4;
         if( colors == 0 ) {
             pm_error("warning - empty %s colormap", ID2string(iffid));
-            skip_chunk(ifp, iffid, chunksize);
+            skip_chunk(ifP, iffid, chunksize);
         } else {
             unsigned int i;
             cmap->color = ppm_allocrow(colors);
@@ -362,10 +361,10 @@ read_cmyk(FILE *     const ifp,
             
             for( i = 0; i < colors; ++i ) {
                 int c, m, y, k;
-                c = get_byte(ifp, iffid, &chunksize);
-                m = get_byte(ifp, iffid, &chunksize);
-                y = get_byte(ifp, iffid, &chunksize);
-                k = get_byte(ifp, iffid, &chunksize);
+                c = get_byte(ifP, iffid, &chunksize);
+                m = get_byte(ifP, iffid, &chunksize);
+                y = get_byte(ifP, iffid, &chunksize);
+                k = get_byte(ifP, iffid, &chunksize);
 
                 {
                     pixval const red = 
@@ -381,7 +380,7 @@ read_cmyk(FILE *     const ifp,
                     PPM_ASSIGN(cmap->color[i], red, green, blue);
                 }
             }
-            chunk_end(ifp, iffid, chunksize);
+            chunk_end(ifP, iffid, chunksize);
         }
     }
 }
@@ -389,7 +388,7 @@ read_cmyk(FILE *     const ifp,
 
 
 static void
-read_clut(FILE *        const ifp,
+read_clut(FILE *        const ifP,
           IFF_ID        const iffid,
           unsigned long const chunksize,
           ColorMap *    const cmap) {
@@ -397,7 +396,7 @@ read_clut(FILE *        const ifp,
     if (chunksize != CLUTSize) {
         pm_message("invalid size for %s chunk - skipping it", 
                    ID2string(iffid));
-        skip_chunk(ifp, iffid, chunksize);
+        skip_chunk(ifP, iffid, chunksize);
     } else {
         long type;
         unsigned char * lut;
@@ -406,12 +405,12 @@ read_clut(FILE *        const ifp,
 
         remainingChunksize = chunksize;  /* initial value */
 
-        type = get_big_long(ifp, iffid, &remainingChunksize);
-        get_big_long(ifp, iffid, &remainingChunksize); /* skip reserved fld */
+        type = get_big_long(ifP, iffid, &remainingChunksize);
+        get_big_long(ifP, iffid, &remainingChunksize); /* skip reserved fld */
 
         MALLOCARRAY_NOFAIL(lut, 256);
         for( i = 0; i < 256; ++i )
-            lut[i] = get_byte(ifp, iffid, &remainingChunksize);
+            lut[i] = get_byte(ifP, iffid, &remainingChunksize);
 
         switch( type ) {
         case CLUT_MONO:
@@ -436,6 +435,27 @@ read_clut(FILE *        const ifp,
 
 
 
+static void
+warnNonsquarePixels(uint8_t const xAspect,
+                    uint8_t const yAspect) {
+
+    if (xAspect != yAspect) {
+        const char * const baseMsg = "warning - non-square pixels";
+
+        if (pm_have_float_format())
+            pm_message("%s; to fix do a 'pamscale -%cscale %g'",
+                       baseMsg,
+                       xAspect > yAspect ? 'x' : 'y',
+                       xAspect > yAspect ? 
+                       (float)xAspect/yAspect : 
+                       (float)yAspect/xAspect);
+        else
+            pm_message("%s", baseMsg);
+    }
+}
+
+
+
 static BitMapHeader *
 read_bmhd(FILE *        const ifP,
           IFF_ID        const iffid,
@@ -443,7 +463,7 @@ read_bmhd(FILE *        const ifP,
 
     BitMapHeader * bmhdP;
 
-    if( chunksize != BitMapHeaderSize ) {
+    if (chunksize != BitMapHeaderSize) {
         pm_message("invalid size for %s chunk - skipping it", 
                    ID2string(iffid));
         skip_chunk(ifP, iffid, chunksize);
@@ -470,24 +490,24 @@ read_bmhd(FILE *        const ifP,
         bmhdP->pageWidth  = get_big_short(ifP, iffid, &remainingChunksize);
         bmhdP->pageHeight = get_big_short(ifP, iffid, &remainingChunksize);
 
-        if( verbose ) {
-            if( typeid == ID_ILBM )
+        if (verbose) {
+            if (typeid == ID_ILBM)
                 pm_message("dimensions: %dx%d, %d planes", 
                            bmhdP->w, bmhdP->h, bmhdP->nPlanes);
             else
                 pm_message("dimensions: %dx%d", bmhdP->w, bmhdP->h);
 
-            if( typeid == ID_ILBM || typeid == ID_PBM ) {
+            if (typeid == ID_ILBM || typeid == ID_PBM) {
                 pm_message("compression: %s",
                            bmhdP->compression <= cmpMAXKNOWN ?
                            cmpNAME[bmhdP->compression] : "unknown");
 
-                switch( bmhdP->masking ) {
+                switch(bmhdP->masking) {
                 case mskNone:
                     break;
                 case mskHasMask:
                 case mskHasTransparentColor:
-                    if( !maskfile )
+                    if (!maskfile)
                         pm_message("use '-maskfile <filename>' "
                                    "to generate a PBM mask file from %s", 
                                    mskNAME[bmhdP->masking]);
@@ -501,27 +521,20 @@ read_bmhd(FILE *        const ifP,
                 }
             }
             else    /* RGBN/RGB8 */
-                if( !maskfile )
+                if (!maskfile)
                     pm_message("use '-maskfile <filename>' "
                                "to generate a PBM mask file "
                                "from genlock bits");
         }
 
         /* fix aspect ratio */
-        if( bmhdP->xAspect == 0 || bmhdP->yAspect == 0 ) {
+        if (bmhdP->xAspect == 0 || bmhdP->yAspect == 0) {
             pm_message("warning - illegal aspect ratio %d:%d, using 1:1", 
                        bmhdP->xAspect, bmhdP->yAspect);
             bmhdP->xAspect = bmhdP->yAspect = 1;
         }
 
-        if( bmhdP->xAspect != bmhdP->yAspect ) {
-            pm_message("warning - non-square pixels; "
-                       "to fix do a 'pnmscale -%cscale %g'",
-                       bmhdP->xAspect > bmhdP->yAspect ? 'x' : 'y',
-                       bmhdP->xAspect > bmhdP->yAspect ? 
-                       (float)(bmhdP->xAspect)/bmhdP->yAspect : 
-                       (float)(bmhdP->yAspect)/bmhdP->xAspect);
-        }
+        warnNonsquarePixels(bmhdP->xAspect, bmhdP->yAspect);
     }
     return bmhdP;
 }
@@ -630,42 +643,42 @@ decode_mask(FILE *          const ifP,
     unsigned char *ilp;
 
     cols = bmhdP->w;
-    switch( bmhdP->masking ) {
+    switch (bmhdP->masking) {
     case mskNone:
         break;
     case mskHasMask:        /* mask plane */
         read_ilbm_plane(ifP, remainingChunksizeP, RowBytes(cols), 
                         bmhdP->compression);
-        if( maskfile ) {
+        if (maskfile) {
             ilp = ilbmrow;
             cbit = 7;
-            for( col = 0; col < cols; col++, cbit-- ) {
-                if( cbit < 0 ) {
+            for (col = 0; col < cols; ++col, --cbit) {
+                if (cbit < 0) {
                     cbit = 7;
-                    ilp++;
+                    ++ilp;
                 }
-                if( *ilp & bit_mask[cbit] )
+                if (*ilp & bit_mask[cbit])
                     maskrow[col] = PBM_BLACK;
                 else
                     maskrow[col] = PBM_WHITE;
             }
             pbm_writepbmrow(maskfile, maskrow, cols, 0);
-            wrotemask = 1;
+            wrotemask = true;
         }
         break;
     case mskHasTransparentColor:
-        if( !chunkyrow )
+        if (!chunkyrow)
             pm_error("decode_mask(): chunkyrow == NULL - can't happen");
         
-        if( maskfile ) {
-            for( col = 0; col < cols; col++ ) {
-                if( chunkyrow[col] == bmhdP->transparentColor )
+        if (maskfile) {
+            for (col = 0; col < cols; ++col) {
+                if (chunkyrow[col] == bmhdP->transparentColor)
                     maskrow[col] = PBM_WHITE;
                 else
                     maskrow[col] = PBM_BLACK;
             }
             pbm_writepbmrow(maskfile, maskrow, cols, 0);
-                wrotemask = 1;
+                wrotemask = true;
         }
         break;
     case mskLasso:
@@ -760,83 +773,66 @@ multi_free(cmap)
 }
 
 
+
 /****************************************************************************
  Colormap handling
  ****************************************************************************/
 
+
+
 static void
-prepareCmap(const BitMapHeader * const bmhd,
-            ColorMap *           const cmap) {
-/*----------------------------------------------------------------------------
-   This is a really ugly subroutine that 1) analyzes a colormap and its
-   context (returning the analysis in global variables); and 2) modifies that
-   color map, because it's really one type of data structure as input and
-   another as output.
+analyzeCmapSamples(const ColorMap * const cmapP,
+                   pixval *         const maxSampleP,
+                   bool *           const shiftedP) {
 
------------------------------------------------------------------------------*/
-    pixval colmaxval = 0;
-    int shifted = 1;
-    int i, r, g, b;
-
-    if( bmhd ) {
-        if( bmhd->masking == mskHasTransparentColor || 
-            bmhd->masking == mskLasso ) {
-            unsigned short const transpIdx = bmhd->transparentColor;
-            if( !transpName ) {
-                MALLOCVAR_NOFAIL(transpColor);
-                if (HAS_COLORMAP(cmap)) {
-                    if( transpIdx >= cmap->ncolors ) {
-                        pm_message("using default transparent color (black)");
-                        PPM_ASSIGN(*transpColor, 0, 0, 0);
-                    } else
-                        *transpColor = cmap->color[transpIdx];
-                } else {
-                    /* The color index is just a direct gray level */
-                    PPM_ASSIGN(*transpColor, transpIdx, transpIdx, transpIdx);
-                }
-            }
-        }
+    pixval       maxSample;
+    bool         shifted;
+    unsigned int i;
+        
+    for (i = 0, maxSample = 0, shifted = true; i < cmapP->ncolors; ++i) {
+        pixval const r = PPM_GETR(cmapP->color[i]);
+        pixval const g = PPM_GETG(cmapP->color[i]);
+        pixval const b = PPM_GETB(cmapP->color[i]);
 
-        if( bmhd->flags & BMHD_FLAGS_CMAPOK )
-            return;
+        maxSample = MAX(maxSample, MAX(r, MAX(g, b)));
+
+        if (r & 0x0f || g & 0x0f || b & 0x0f)
+            shifted = false;
     }
+    *shiftedP   = shifted;
+    *maxSampleP = maxSample;
+}
 
-    if( !HAS_COLORMAP(cmap) )
-        return;
 
-    for( i = 0; i < cmap->ncolors; i++ ) {
-        r = PPM_GETR(cmap->color[i]);
-        if( r > colmaxval ) colmaxval = r;
-        if( r & 0x0f ) shifted = 0;
 
-        g = PPM_GETG(cmap->color[i]);
-        if( g > colmaxval ) colmaxval = g;
-        if( g & 0x0f ) shifted = 0;
+static void
+transformCmap(ColorMap * const cmapP) {
 
-        b = PPM_GETB(cmap->color[i]);
-        if( b > colmaxval ) colmaxval = b;
-        if( b & 0x0f ) shifted = 0;
-    }
+    pixval maxSample;
+        /* The maximum sample value in *cmapP input */
+    bool shifted;
+        /* Samples in the *cmapP input appear to be 4 bit (maxval 15) original
+           values shifted left 4 places to make 8 bit (maxval 255) samples.
+        */
 
-#ifdef DEBUG
-    pm_message("colormap maxval is %d", colmaxval);
-#endif
-    if( colmaxval == 0 )
+    analyzeCmapSamples(cmapP, &maxSample, &shifted);
+
+    if (maxSample == 0)
         pm_message("warning - black colormap");
-    else
-    if( shifted || colmaxval <= 15 ) {
-        if( !adjustcolors ) {
+    else if (shifted || maxSample <= 15) {
+        if (!adjustcolors) {
             pm_message("warning - probably %s4-bit colormap",
-                        shifted ? "shifted " : "");
-            pm_message("    use '-adjustcolors' to scale colormap to 8 bits");
-        }
-        else {
+                       shifted ? "shifted " : "");
+            pm_message("Use '-adjustcolors' to scale colormap to 8 bits");
+        } else {
+            unsigned int i;
             pm_message("scaling colormap to 8 bits");
-            for( i = 0; i < cmap->ncolors; i++ ) {
-                r = PPM_GETR(cmap->color[i]);
-                g = PPM_GETG(cmap->color[i]);
-                b = PPM_GETB(cmap->color[i]);
-                if( shifted ) {
+            for (i = 0; i < cmapP->ncolors; ++i) {
+                pixval r, g, b;
+                r = PPM_GETR(cmapP->color[i]);
+                g = PPM_GETG(cmapP->color[i]);
+                b = PPM_GETB(cmapP->color[i]);
+                if (shifted) {
                     r >>= 4;
                     g >>= 4;
                     b >>= 4;
@@ -844,13 +840,74 @@ prepareCmap(const BitMapHeader * const bmhd,
                 r *= FACTOR_4BIT;
                 g *= FACTOR_4BIT;
                 b *= FACTOR_4BIT;
-                PPM_ASSIGN(cmap->color[i], r, g, b);
+                PPM_ASSIGN(cmapP->color[i], r, g, b);
             }
         }
     }
 }
 
 
+
+static pixel *
+transpColor(const BitMapHeader * const bmhdP,
+            ColorMap *           const cmapP,
+            const char *         const transpName,
+            pixval               const maxval) {
+
+    pixel * transpColorP;
+
+    if (bmhdP) {
+        if (bmhdP->masking == mskHasTransparentColor || 
+            bmhdP->masking == mskLasso) {
+            MALLOCVAR_NOFAIL(transpColorP);
+
+            if (transpName)
+                *transpColorP = ppm_parsecolor(transpName, maxval);
+            else {
+                unsigned short const transpIdx = bmhdP->transparentColor;
+                if (HAS_COLORMAP(cmapP)) {
+                    if (transpIdx >= cmapP->ncolors) {
+                        pm_message("using default transparent color (black)");
+                        PPM_ASSIGN(*transpColorP, 0, 0, 0);
+                    } else
+                        *transpColorP = cmapP->color[transpIdx];
+                } else {
+                    /* The color index is just a direct gray level */
+                    PPM_ASSIGN(*transpColorP, transpIdx, transpIdx, transpIdx);
+                }
+            }
+        } else
+            transpColorP = NULL;
+    } else
+        transpColorP = NULL;
+
+    return transpColorP;
+}
+
+
+
+static void
+prepareCmap(const BitMapHeader * const bmhdP,
+            ColorMap *           const cmapP) {
+/*----------------------------------------------------------------------------
+   This is a really ugly subroutine that 1) analyzes a colormap and its
+   context (returning the analysis in global variables); and 2) modifies that
+   color map, because it's really one type of data structure as input and
+   another as output.
+-----------------------------------------------------------------------------*/
+    bool bmhdCmapOk;
+
+    if (bmhdP)
+        bmhdCmapOk = (bmhdP->flags & BMHD_FLAGS_CMAPOK);
+    else
+        bmhdCmapOk = false;
+
+    if (HAS_COLORMAP(cmapP) && !bmhdCmapOk)
+        transformCmap(cmapP);
+}
+
+
+
 static pixval
 lookup_red(cmap, oldval)
     ColorMap *cmap;
@@ -990,18 +1047,18 @@ get_color(cmap, idx, red, green, blue)
  ****************************************************************************/
 
 static void
-std_to_ppm(FILE *         const ifp, 
+std_to_ppm(FILE *         const ifP, 
            long           const chunksize, 
-           BitMapHeader * const bmhd, 
+           BitMapHeader * const bmhdP, 
            ColorMap *     const cmap, 
            long           const viewportmodes);
 
 
 
 static void
-ham_to_ppm(FILE *         const ifp, 
+ham_to_ppm(FILE *         const ifP, 
            long           const chunksize, 
-           BitMapHeader * const bmhd, 
+           BitMapHeader * const bmhdP, 
            ColorMap *     const cmap, 
            long           const viewportmodes) {
 
@@ -1010,9 +1067,9 @@ ham_to_ppm(FILE *         const ifp,
     rawtype *rawrow;
     unsigned char hamlut[256];
 
-    cols = bmhd->w;
-    rows = bmhd->h;
-    hambits = bmhd->nPlanes - 2;
+    cols = bmhdP->w;
+    rows = bmhdP->h;
+    hambits = bmhdP->nPlanes - 2;
     hammask = (1 << hambits) - 1;
     hamshift = 8 - hambits;
     hammask2 = (1 << hamshift) - 1;
@@ -1021,15 +1078,16 @@ ham_to_ppm(FILE *         const ifp,
         int const assumed_viewportmodes = viewportmodes & ~(vmHAM);
 
         pm_message("%d-plane HAM?? - interpreting image as a normal ILBM", 
-                   bmhd->nPlanes);
-        std_to_ppm(ifp, chunksize, bmhd, cmap, assumed_viewportmodes);
+                   bmhdP->nPlanes);
+        std_to_ppm(ifP, chunksize, bmhdP, cmap, assumed_viewportmodes);
         return;
     } else {
         unsigned long remainingChunksize;
+        pixel * transpColorP;
 
         pm_message("input is a %sHAM%d file", 
                    HAS_MULTIPALETTE(cmap) ? "multipalette " : "", 
-                   bmhd->nPlanes);
+                   bmhdP->nPlanes);
 
         if( HAS_COLORLUT(cmap) || HAS_MONOLUT(cmap) ) {
             pm_message("warning - color lookup tables ignored in HAM");
@@ -1048,10 +1106,7 @@ ham_to_ppm(FILE *         const ifp,
             cmap->monolut = hamlut;
         }
 
-        if( transpName ) {
-            MALLOCVAR_NOFAIL(transpColor);
-            *transpColor = ppm_parsecolor(transpName, MAXCOLVAL);
-        }
+        transpColorP = transpColor(bmhdP, cmap, transpName, MAXCOLVAL);
 
         if( HAS_MULTIPALETTE(cmap) )
             multi_init(cmap, viewportmodes);
@@ -1068,15 +1123,15 @@ ham_to_ppm(FILE *         const ifp,
             if( HAS_MULTIPALETTE(cmap) )
                 multi_update(cmap, row);
 
-            decode_row(ifp, &remainingChunksize, rawrow, bmhd->nPlanes, bmhd);
-            decode_mask(ifp, &remainingChunksize, rawrow, bmhd);
+            decode_row(ifP, &remainingChunksize, rawrow, bmhdP->nPlanes, bmhdP);
+            decode_mask(ifP, &remainingChunksize, rawrow, bmhdP);
 
             r = g = b = 0;
             for( col = 0; col < cols; col++ ) {
                 int idx = rawrow[col] & hammask;
 
-                if( transpColor && maskrow && maskrow[col] == PBM_WHITE )
-                    pixelrow[col] = *transpColor;
+                if( transpColorP && maskrow && maskrow[col] == PBM_WHITE )
+                    pixelrow[col] = *transpColorP;
                 else {
                     switch((rawrow[col] >> hambits) & 0x03) {
                     case HAMCODE_CMAP:
@@ -1103,7 +1158,7 @@ ham_to_ppm(FILE *         const ifp,
             }
             ppm_writeppmrow(stdout, pixelrow, cols, MAXCOLVAL, 0);
         }
-        chunk_end(ifp, ID_BODY, remainingChunksize);
+        chunk_end(ifP, ID_BODY, remainingChunksize);
     }
 }
 
@@ -1112,27 +1167,28 @@ ham_to_ppm(FILE *         const ifp,
 static void
 std_to_ppm(FILE *         const ifP, 
            long           const chunksize, 
-           BitMapHeader * const bmhd, 
+           BitMapHeader * const bmhdP, 
            ColorMap *     const cmap, 
            long           const viewportmodes) {
 
     if (viewportmodes & vmHAM) {
-        ham_to_ppm(ifP, chunksize, bmhd, cmap, viewportmodes);
+        ham_to_ppm(ifP, chunksize, bmhdP, cmap, viewportmodes);
     } else {
-        unsigned int const cols = bmhd->w;
-        unsigned int const rows = bmhd->h;
+        unsigned int const cols = bmhdP->w;
+        unsigned int const rows = bmhdP->h;
 
         rawtype *rawrow;
         unsigned int row, col;
         pixval maxval;
         unsigned long remainingChunksize;
+        pixel * transpColorP;
 
-        pm_message("input is a %d-plane %s%sILBM", bmhd->nPlanes,
+        pm_message("input is a %d-plane %s%sILBM", bmhdP->nPlanes,
                    HAS_MULTIPALETTE(cmap) ? "multipalette " : "",
                    viewportmodes & vmEXTRA_HALFBRITE ? "EHB " : ""
             );
 
-        if( bmhd->nPlanes > MAXPLANES )
+        if( bmhdP->nPlanes > MAXPLANES )
             pm_error("too many planes (max %d)", MAXPLANES);
 
         if( HAS_COLORMAP(cmap) ) {
@@ -1142,15 +1198,12 @@ std_to_ppm(FILE *         const ifP,
         }
         else {
             pm_message("no colormap - interpreting values as grayscale");
-            maxval = lut_maxval(cmap, pm_bitstomaxval(bmhd->nPlanes));
+            maxval = lut_maxval(cmap, pm_bitstomaxval(bmhdP->nPlanes));
             if( maxval > PPM_OVERALLMAXVAL )
                 pm_error("nPlanes is too large");
         }
 
-        if( transpName ) {
-            MALLOCVAR_NOFAIL(transpColor);
-            *transpColor = ppm_parsecolor(transpName, maxval);
-        }
+        transpColorP = transpColor(bmhdP, cmap, transpName, maxval);
 
         rawrow = alloc_rawrow(cols);
 
@@ -1166,13 +1219,13 @@ std_to_ppm(FILE *         const ifP,
             if( HAS_MULTIPALETTE(cmap) )
                 multi_update(cmap, row);
 
-            decode_row(ifP, &remainingChunksize, rawrow, bmhd->nPlanes, bmhd);
-            decode_mask(ifP, &remainingChunksize, rawrow, bmhd);
+            decode_row(ifP, &remainingChunksize, rawrow, bmhdP->nPlanes, bmhdP);
+            decode_mask(ifP, &remainingChunksize, rawrow, bmhdP);
 
             for( col = 0; col < cols; col++ ) {
                 pixval r, g, b;
-                if( transpColor && maskrow && maskrow[col] == PBM_WHITE )
-                    pixelrow[col] = *transpColor;
+                if( transpColorP && maskrow && maskrow[col] == PBM_WHITE )
+                    pixelrow[col] = *transpColorP;
                 else {
                     get_color(cmap, rawrow[col], &r, &g, &b);
                     PPM_ASSIGN(pixelrow[col], r, g, b);
@@ -1200,6 +1253,7 @@ deep_to_ppm(FILE *         const ifP,
     rawtype *Rrow, *Grow, *Brow;
     pixval maxval;
     unsigned long remainingChunksize;
+    pixel * transpColorP;
 
     pm_message("input is a deep (%d-bit) ILBM", bmhdP->nPlanes);
     if( planespercolor > MAXPLANES )
@@ -1216,11 +1270,8 @@ deep_to_ppm(FILE *         const ifP,
     if( maxval > PPM_OVERALLMAXVAL )
         pm_error("nPlanes is too large");
 
-    if( transpName ) {
-        MALLOCVAR_NOFAIL(transpColor);
-        *transpColor = ppm_parsecolor(transpName, maxval);
-    }
-
+    transpColorP = transpColor(bmhdP, cmap, transpName, maxval);
+        
     Rrow = alloc_rawrow(cols);
     Grow = alloc_rawrow(cols);
     Brow = alloc_rawrow(cols);
@@ -1236,8 +1287,8 @@ deep_to_ppm(FILE *         const ifP,
         decode_mask(ifP, &remainingChunksize, NULL, bmhdP);
 
         for( col = 0; col < cols; col++ ) {
-            if( transpColor && maskrow && maskrow[col] == PBM_WHITE )
-                pixelrow[col] = *transpColor;
+            if( transpColorP && maskrow && maskrow[col] == PBM_WHITE )
+                pixelrow[col] = *transpColorP;
             else
                 PPM_ASSIGN(pixelrow[col],   lookup_red(cmap, Rrow[col]),
                                             lookup_green(cmap, Grow[col]),
@@ -1268,6 +1319,7 @@ dcol_to_ppm(FILE *         const ifP,
     pixval maxval, redmaxval, greenmaxval, bluemaxval;
     pixval *redtable, *greentable, *bluetable;
     unsigned long remainingChunksize;
+    pixel * transpColorP;
 
     pm_message("input is a %d:%d:%d direct color ILBM",
                 redplanes, greenplanes, blueplanes);
@@ -1318,10 +1370,7 @@ dcol_to_ppm(FILE *         const ifP,
         for (i = 0; i <= bluemaxval; ++i)
             bluetable[i] = ROUNDDIV(i * maxval, bluemaxval);
     }
-    if( transpName ) {
-        MALLOCVAR_NOFAIL(transpColor);
-        *transpColor = ppm_parsecolor(transpName, maxval);
-    }
+    transpColorP = transpColor(bmhdP, cmap, transpName, maxval);
 
     Rrow = alloc_rawrow(cols);
     Grow = alloc_rawrow(cols);
@@ -1338,8 +1387,8 @@ dcol_to_ppm(FILE *         const ifP,
         decode_mask(ifP, &remainingChunksize, NULL, bmhdP);
 
         for( col = 0; col < cols; col++ ) {
-            if( transpColor && maskrow && maskrow[col] == PBM_WHITE )
-                pixelrow[col] = *transpColor;
+            if( transpColorP && maskrow && maskrow[col] == PBM_WHITE )
+                pixelrow[col] = *transpColorP;
             else
                 PPM_ASSIGN( pixelrow[col],  redtable[Rrow[col]],
                                             greentable[Grow[col]],
@@ -1374,6 +1423,7 @@ ipbm_to_ppm(FILE *         const ifP,
     int col, row;
     pixval maxval;
     unsigned long remainingChunksize;
+    pixel * transpColorP;
 
     pm_message("input is a %sPBM ", 
                HAS_MULTIPALETTE(cmap) ? "multipalette " : "");
@@ -1392,10 +1442,7 @@ ipbm_to_ppm(FILE *         const ifP,
         maxval = lut_maxval(cmap, pm_bitstomaxval(bmhdP->nPlanes));
     }
 
-    if( transpName ) {
-        MALLOCVAR_NOFAIL(transpColor);
-        *transpColor = ppm_parsecolor(transpName, maxval);
-    }
+    transpColorP = transpColor(bmhdP, cmap, transpName, maxval);
 
     if( HAS_MULTIPALETTE(cmap) )
         multi_init(cmap, viewportmodes);
@@ -1414,8 +1461,8 @@ ipbm_to_ppm(FILE *         const ifP,
 
         for( col = 0; col < cols; col++ ) {
             pixval r, g, b;
-            if( transpColor && maskrow && maskrow[col] == PBM_WHITE )
-                pixelrow[col] = *transpColor;
+            if( transpColorP && maskrow && maskrow[col] == PBM_WHITE )
+                pixelrow[col] = *transpColorP;
             else {
                 get_color(cmap, ilbmrow[col], &r, &g, &b);
                 PPM_ASSIGN(pixelrow[col], r, g, b);
@@ -1438,48 +1485,54 @@ rgbn_to_ppm(FILE *         const ifP,
     unsigned int const rows = bmhdP->h;
     unsigned int const cols = bmhdP->w;
 
-    int row, col, count, genlock, tries;
-    pixval r, g, b, maxval;
+    unsigned int row;
+    unsigned int count;
+    pixval maxval;
     unsigned long remainingChunksize;
+    pixel * transpColorP;
 
     pm_message("input is a %d-bit RGB image", (typeid == ID_RGB8 ? 8 : 4));
 
-    if( bmhdP->compression != 4 )
+    if (bmhdP->compression != 4)
         pm_error("invalid compression mode for %s: %d (must be 4)", 
                  ID2string(typeid), bmhdP->compression);
-
-    switch( typeid ) {
-        case ID_RGBN:
-            if( bmhdP->nPlanes != 13 )
-                pm_error("invalid number of planes for %s: %d (must be 13)", 
-                         ID2string(typeid), bmhdP->nPlanes);
-            maxval = lut_maxval(cmap, 15);
-            break;
-        case ID_RGB8:
-            if( bmhdP->nPlanes != 25 )
-                pm_error("invalid number of planes for %s: %d (must be 25)", 
-                         ID2string(typeid), bmhdP->nPlanes);
-            maxval = 255;
-            break;
-        default:
-            pm_error("rgbn_to_ppm(): invalid IFF ID %s - can't happen", 
-                     ID2string(typeid));
+    
+    switch (typeid) {
+    case ID_RGBN:
+        if (bmhdP->nPlanes != 13)
+            pm_error("invalid number of planes for %s: %d (must be 13)", 
+                     ID2string(typeid), bmhdP->nPlanes);
+        maxval = lut_maxval(cmap, 15);
+        break;
+    case ID_RGB8:
+        if (bmhdP->nPlanes != 25)
+            pm_error("invalid number of planes for %s: %d (must be 25)", 
+                     ID2string(typeid), bmhdP->nPlanes);
+        maxval = 255;
+        break;
+    default:
+        pm_error("rgbn_to_ppm(): invalid IFF ID %s - can't happen", 
+                 ID2string(typeid));
     }
 
-    if( transpName ) {
-        MALLOCVAR_NOFAIL(transpColor);
-        *transpColor = ppm_parsecolor(transpName, maxval);
-    }
+    transpColorP = transpColor(bmhdP, cmap, transpName, maxval);
 
     ppm_writeppminit(stdout, cols, rows, maxval, 0);
 
-    remainingChunksize = chunksize;  /* initial value */
-    count = 0;
-    for( row = 0; row < rows; row++ ) {
-        for( col = 0; col < cols; col++ ) {
+    for (row = 0, count = 0, remainingChunksize = chunksize;
+         row < rows;
+         ++row) {
+
+        unsigned int col;
+
+        for (col = 0; col < cols; ++col) {
+            unsigned int tries;
+            unsigned int genlock;
+            pixval r, g, b;
+
             tries = 0;
-            while( !count ) {
-                if( typeid == ID_RGB8 ) {
+            while (count == 0) {
+                if (typeid == ID_RGB8) {
                     r = lookup_red(cmap,   get_byte(ifP, ID_BODY, 
                                                     &remainingChunksize));
                     g = lookup_green(cmap, get_byte(ifP, ID_BODY,
@@ -1489,47 +1542,46 @@ rgbn_to_ppm(FILE *         const ifP,
                     count = get_byte(ifP, ID_BODY, &remainingChunksize);
                     genlock = count & 0x80;
                     count &= 0x7f;
-                }
-                else {
-                    int word;
-                    word = get_big_short(ifP, ID_BODY, &remainingChunksize);
+                } else {
+                    unsigned int const word =
+                        get_big_short(ifP, ID_BODY, &remainingChunksize);
                     r = lookup_red(cmap, (word & 0xf000) >> 12);
                     g = lookup_green(cmap, (word & 0x0f00) >> 8);
                     b = lookup_blue(cmap, (word & 0x00f0) >> 4);
                     genlock = word & 0x0008;
                     count = word & 0x0007;
                 }
-                if( !count ) {
+                if (!count) {
                     count = get_byte(ifP, ID_BODY, &remainingChunksize);
-                    if( !count )
+                    if (count == 0)
                         count =
                             get_big_short(ifP, ID_BODY, &remainingChunksize);
-                        if( !count )
-                            ++tries;
+                    if (count == 0)
+                        ++tries;
                 }
             }
-            if( tries ) {
-                pm_message("warning - repeat count 0 at col %d row %d: "
-                           "skipped %d RGB entr%s",
+            if (tries > 0) {
+                pm_message("warning - repeat count 0 at col %u row %u: "
+                           "skipped %u RGB entr%s",
                             col, row, tries, (tries == 1 ? "y" : "ies"));
             }
-            if( maskfile ) {
+            if (maskfile) {
                 /* genlock bit set -> transparent */
-                if( genlock )
+                if (genlock)
                     maskrow[col] = PBM_WHITE;
                 else
                     maskrow[col] = PBM_BLACK;
             }
-            if( transpColor && maskrow && maskrow[col] == PBM_WHITE )
-                pixelrow[col] = *transpColor;
+            if (transpColorP && maskrow && maskrow[col] == PBM_WHITE)
+                pixelrow[col] = *transpColorP;
             else
                 PPM_ASSIGN(pixelrow[col], r, g, b);
             --count;
         }
         ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
-        if( maskfile ) {
+        if (maskfile) {
             pbm_writepbmrow(maskfile, maskrow, cols, 0);
-            wrotemask = 1;
+            wrotemask = true;
         }
     }
     chunk_end(ifP, ID_BODY, remainingChunksize);
@@ -1918,13 +1970,13 @@ PCHG_ConvertBig(PCHGHeader *    const PCHG,
 
 
 static void
-read_pchg(FILE *     const ifp,
+read_pchg(FILE *     const ifP,
           IFF_ID     const iffid,
           long       const chunksize,
           ColorMap * const cmap) {
 
     if( cmap->mp_type >= MP_TYPE_PCHG ) {
-        skip_chunk(ifp, iffid, chunksize);
+        skip_chunk(ifP, iffid, chunksize);
     } else {
         PCHGHeader      PCHG;
         unsigned char   *data;
@@ -1938,15 +1990,15 @@ read_pchg(FILE *     const ifp,
 
         remainingChunksize = chunksize;  /* initial value */
 
-        PCHG.Compression = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.Flags       = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.StartLine   = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.LineCount   = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.ChangedLines= get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.MinReg      = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.MaxReg      = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.MaxChanges  = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.TotalChanges= get_big_long(ifp, iffid, &remainingChunksize);
+        PCHG.Compression = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.Flags       = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.StartLine   = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.LineCount   = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.ChangedLines= get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.MinReg      = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.MaxReg      = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.MaxChanges  = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.TotalChanges= get_big_long(ifP, iffid, &remainingChunksize);
 
 #ifdef DEBUG
         pm_message("PCHG StartLine   : %d", PCHG.StartLine);
@@ -1961,17 +2013,17 @@ read_pchg(FILE *     const ifp,
             long treesize, compsize;
 
             CompHdr.CompInfoSize     =
-                get_big_long(ifp, iffid, &remainingChunksize);
+                get_big_long(ifP, iffid, &remainingChunksize);
             CompHdr.OriginalDataSize =
-                get_big_long(ifp, iffid, &remainingChunksize);
+                get_big_long(ifP, iffid, &remainingChunksize);
 
             treesize = CompHdr.CompInfoSize;
             MALLOCARRAY_NOFAIL(comptree, treesize);
-            read_bytes(ifp, treesize, comptree, iffid, &remainingChunksize);
+            read_bytes(ifP, treesize, comptree, iffid, &remainingChunksize);
 
             compsize = remainingChunksize;
             MALLOCARRAY_NOFAIL(compdata, compsize);
-            read_bytes(ifp, compsize, compdata, iffid, &remainingChunksize);
+            read_bytes(ifP, compsize, compdata, iffid, &remainingChunksize);
 
             datasize = CompHdr.OriginalDataSize;
             MALLOCARRAY_NOFAIL(data, datasize);
@@ -1986,7 +2038,7 @@ read_pchg(FILE *     const ifp,
 #endif
             datasize = remainingChunksize;
             MALLOCARRAY_NOFAIL(data, datasize);
-            read_bytes(ifp, datasize, data, iffid, &remainingChunksize);
+            read_bytes(ifP, datasize, data, iffid, &remainingChunksize);
         }
 
         if( PCHG.Flags & PCHGF_USE_ALPHA )
@@ -2025,7 +2077,7 @@ read_pchg(FILE *     const ifp,
                          ID2string(iffid));
         }
         free(data);
-        chunk_end(ifp, iffid, remainingChunksize);
+        chunk_end(ifP, iffid, remainingChunksize);
     }
 }
 
@@ -2050,7 +2102,7 @@ ignored_iffid(IFF_ID       const iffid,
 
 
 static void 
-process_body( FILE *          const ifp,
+process_body( FILE *          const ifP,
               long            const chunksize,
               BitMapHeader *  const bmhdP,
               ColorMap *      const cmap,
@@ -2060,19 +2112,19 @@ process_body( FILE *          const ifp,
               DirectColor *   const dcol,
               int *           const viewportmodesP) {
     
-    if( bmhdP == NULL )
+    if (bmhdP == NULL)
         pm_error("%s chunk without %s chunk", 
                  ID2string(ID_BODY), ID2string(ID_BMHD));
 
     prepareCmap(bmhdP, cmap);
 
     pixelrow = ppm_allocrow(bmhdP->w);
-    if( maskfile ) {
+    if (maskfile) {
         maskrow = pbm_allocrow(bmhdP->w);
         pbm_writepbminit(maskfile, bmhdP->w, bmhdP->h, 0);
     }
 
-    if( typeid == ID_ILBM ) {
+    if (typeid == ID_ILBM) {
         int isdeep;
 
         MALLOCARRAY_NOFAIL(ilbmrow, RowBytes(bmhdP->w));
@@ -2086,27 +2138,27 @@ process_body( FILE *          const ifp,
         } else
             isdeep = isdeepopt;
         
-        if( isdeep > 0 )
-            deep_to_ppm(ifp, chunksize, bmhdP, cmap);
-        else if( dcol )
-            dcol_to_ppm(ifp, chunksize, bmhdP, cmap, dcol);
-        else if( bmhdP->nPlanes > 8 ) {
-            if( bmhdP->nPlanes <= 16 && HAS_COLORMAP(cmap) )
-                std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP);
-            else if( isdeep >= 0 && (bmhdP->nPlanes % 3 == 0) )
-                deep_to_ppm(ifp, chunksize, bmhdP, cmap);
-            else if( bmhdP->nPlanes <= 16 )   
+        if (isdeep > 0)
+            deep_to_ppm(ifP, chunksize, bmhdP, cmap);
+        else if (dcol)
+            dcol_to_ppm(ifP, chunksize, bmhdP, cmap, dcol);
+        else if (bmhdP->nPlanes > 8) {
+            if (bmhdP->nPlanes <= 16 && HAS_COLORMAP(cmap))
+                std_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
+            else if (isdeep >= 0 && (bmhdP->nPlanes % 3 == 0))
+                deep_to_ppm(ifP, chunksize, bmhdP, cmap);
+            else if (bmhdP->nPlanes <= 16)
                 /* will be interpreted as grayscale */
-                std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP);
+                std_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
             else
                 pm_error("don't know how to interpret %d-plane image", 
                          bmhdP->nPlanes);
         } else
-            std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP);
+            std_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
     } else if( typeid == ID_PBM )
-        ipbm_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP);
+        ipbm_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
     else   /* RGBN or RGB8 */
-        rgbn_to_ppm(ifp, chunksize, bmhdP, cmap);
+        rgbn_to_ppm(ifP, chunksize, bmhdP, cmap);
 }
 
 
@@ -2378,6 +2430,8 @@ main(int argc, char *argv[]) {
     if( argn != argc )
         pm_usage(usage);
 
+    wrotemask = false;  /* initial value */
+
     /* Read in the ILBM file. */
 
     firstIffid = get_big_long(ifP, ID_FORM, NULL);
diff --git a/converter/ppm/pc1toppm.c b/converter/ppm/pc1toppm.c
index 5ba247e9..ec6678a4 100644
--- a/converter/ppm/pc1toppm.c
+++ b/converter/ppm/pc1toppm.c
@@ -170,7 +170,7 @@ writePpm(FILE *         const ofP,
            is represented by 4 shorts (16 bit integers).  The
            first is for Plane 0, the second for Plane 1, etc.  Each short
            contains the bits for that plane for each of the 16 columns,
-           arranged from most signficant bit to least in increasing column
+           arranged from most significant bit to least in increasing column
            number.
         */
         unsigned int col0ScreenIndex = cols/16*planes * row;
diff --git a/converter/ppm/pcxtoppm.c b/converter/ppm/pcxtoppm.c
index 25a81531..e252ba22 100644
--- a/converter/ppm/pcxtoppm.c
+++ b/converter/ppm/pcxtoppm.c
@@ -84,7 +84,7 @@ parseCommandLine ( int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -100,7 +100,7 @@ parseCommandLine ( int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (argc-1 < 1)
@@ -129,9 +129,9 @@ struct pcxHeader {
     short Planes;
     short BitsPerPixel;
     short BytesPerLine;
-        /* Number of decompressed bytes each plane of each row of the image 
-           takes.  Due to padding (this is always an even number), there may
-           be garbage on the right end that isn't part of the image.
+        /* Number of decompressed bytes each plane of each row of the image
+           takes.  Because of padding (this is always an even number), there
+           may be garbage on the right end that isn't part of the image.
         */
     short PaletteInfo;
     short HorizontalResolution;
@@ -532,14 +532,14 @@ pcx_256col_to_ppm(FILE *       const ifP,
     } else
         cols = headerCols;
 
-    image = (unsigned char **)pm_allocarray(BytesPerLine, rows, 
-                                            sizeof(unsigned char));
+    MALLOCARRAY2(image, rows, BytesPerLine);
+
     for (row = 0; row < rows; ++row)
         GetPCXRow(ifP, image[row], BytesPerLine);
 
     /*
      * 256 color images have their color map at the end of the file
-     * preceeded by a magic byte
+     * preceded by a magic byte
      */
     colormapSignature = GetByte(ifP);
     if (colormapSignature != PCX_256_COLORS)
diff --git a/converter/ppm/picttoppm.c b/converter/ppm/picttoppm.c
index 77efc6f2..b14675c7 100644
--- a/converter/ppm/picttoppm.c
+++ b/converter/ppm/picttoppm.c
@@ -40,10 +40,10 @@
 /*
  * Typical byte, 2 byte and 4 byte integers.
  */
-typedef unsigned char byte;
-typedef char signed_byte;
-typedef unsigned short word;
-typedef unsigned long longword;
+typedef unsigned char Byte;
+typedef char SignedByte;
+typedef unsigned short Word;
+typedef unsigned long Longword;
 
 
 /*
@@ -51,47 +51,47 @@ typedef unsigned long longword;
  */
 
 struct Rect {
-    word top;
-    word left;
-    word bottom;
-    word right;
+    Word top;
+    Word left;
+    Word bottom;
+    Word right;
 };
 
 struct pixMap {
     struct Rect Bounds;
-    word version;
-    word packType;
-    longword packSize;
-    longword hRes;
-    longword vRes;
-    word pixelType;
-    word pixelSize;
-    word cmpCount;
-    word cmpSize;
-    longword planeBytes;
-    longword pmTable;
-    longword pmReserved;
+    Word version;
+    Word packType;
+    Longword packSize;
+    Longword hRes;
+    Longword vRes;
+    Word pixelType;
+    Word pixelSize;
+    Word cmpCount;
+    Word cmpSize;
+    Longword planeBytes;
+    Longword pmTable;
+    Longword pmReserved;
 };
 
 struct RGBColor {
-    word red;
-    word grn;
-    word blu;
+    Word red;
+    Word grn;
+    Word blu;
 };
 
 struct Point {
-    word x;
-    word y;
+    Word x;
+    Word y;
 };
 
 struct Pattern {
-    byte pix[64];
+    Byte pix[64];
 };
 
 struct rgbPlanes {
-    word * red;
-    word * grn;
-    word * blu;
+    Word * red;
+    Word * grn;
+    Word * blu;
 };
 
 struct canvas {
@@ -102,8 +102,8 @@ typedef void (*transfer_func) (struct RGBColor* src, struct RGBColor* dst);
 
 static const char * stage;
 static struct Rect picFrame;
-static word rowlen;
-static word collen;
+static Word rowlen;
+static Word collen;
 static int verbose;
 static int fullres;
 static int recognize_comment;
@@ -121,23 +121,23 @@ static struct Rect clip_rect;
 static struct Rect cur_rect;
 static struct Point current;
 static struct Pattern pen_pat;
-static word pen_width;
-static word pen_height;
-static word pen_mode;
+static Word pen_width;
+static Word pen_height;
+static Word pen_mode;
 static transfer_func pen_trf;
-static word text_font;
-static byte text_face;
-static word text_mode;
+static Word text_font;
+static Byte text_face;
+static Word text_mode;
 static transfer_func text_trf;
-static word text_size;
+static Word text_size;
 static struct font* tfont;
 
 /* state for magic printer comments */
 static int ps_text;
-static byte ps_just;
-static byte ps_flip;
-static word ps_rotation;
-static byte ps_linespace;
+static Byte ps_just;
+static Byte ps_flip;
+static Word ps_rotation;
+static Byte ps_linespace;
 static int ps_cent_x;
 static int ps_cent_y;
 static int ps_cent_set;
@@ -277,6 +277,10 @@ typedef void (drawFn)(struct canvas *, blitList *, int);
 struct opdef {
     const char* name;
     int len;
+        /* If non-negative, this is the length of the argument of the
+           instruction.  If negative, it has special meaning; WORD_LEN
+           is the only value negative value.
+        */
     drawFn * impl;
     const char* description;
 };
@@ -291,11 +295,11 @@ struct opdef {
  */
 
 /* for reserved opcodes of known length */
-#define res(length) \
+#define RESERVED_OP(length)                                   \
 { "reserved", (length), NULL, "reserved for Apple use" }
 
 /* for reserved opcodes of length determined by a function */
-#define resf(skipfunction) \
+#define RESERVED_OP_F(skipfunction) \
 { "reserved", NA, (skipfunction), "reserved for Apple use" }
 
 /* seems like RGB colors are 6 bytes, but Apple says they're variable */
@@ -308,8 +312,8 @@ static int align = 0;
 
 
 
-static byte
-read_byte(void) {
+static Byte
+readByte(void) {
     int c;
 
     if ((c = fgetc(ifp)) == EOF)
@@ -321,52 +325,52 @@ read_byte(void) {
 
 
 
-static word
-read_word(void) {
-    byte b;
+static Word
+readWord(void) {
 
-    b = read_byte();
+    Byte const hi = readByte();
+    Byte const lo = readByte();
 
-    return (b << 8) | read_byte();
+    return (hi << 8) | (lo << 0);
 }
 
 
 
-static void read_point(struct Point * const p) {
-    p->y = read_word();
-    p->x = read_word();
+static void readPoint(struct Point * const p) {
+    p->y = readWord();
+    p->x = readWord();
 }
 
 
 
-static longword
-read_long(void) {
-    word i;
+static Longword
+readLong(void) {
+    Word const hi = readWord();
+    Word const lo = readWord();
 
-    i = read_word();
-    return (i << 16) | read_word();
+    return (hi << 16) | (lo << 0);
 }
 
 
 
-static signed_byte
-read_signed_byte(void) {
-    return (signed_byte)read_byte();
+static SignedByte
+readSignedByte(void) {
+    return (SignedByte)readByte();
 }
 
 
 
 static void 
-read_short_point(struct Point * const p) {
-    p->x = read_signed_byte();
-    p->y = read_signed_byte();
+readShortPoint(struct Point * const p) {
+    p->x = readSignedByte();
+    p->y = readSignedByte();
 }
 
 
 
 static void
 skip(int const byteCount) {
-    static byte buf[1024];
+    static Byte buf[1024];
     int n;
 
     align += byteCount;
@@ -461,7 +465,7 @@ const_name(const struct const_name * const table,
 
 
 static void 
-picComment(word const type, 
+picComment(Word const type, 
            int const length) {
 
     unsigned int remainingLength;
@@ -470,10 +474,10 @@ picComment(word const type,
     case 150:
         if (verbose) pm_message("TextBegin");
         if (length >= 6) {
-            ps_just = read_byte();
-            ps_flip = read_byte();
-            ps_rotation = read_word();
-            ps_linespace = read_byte();
+            ps_just = readByte();
+            ps_flip = readByte();
+            ps_rotation = readWord();
+            ps_linespace = readByte();
             remainingLength = length - 5;
             if (recognize_comment)
                 ps_text = 1;
@@ -506,11 +510,11 @@ picComment(word const type,
         if (length < 8)
             remainingLength = length;
         else {
-            ps_cent_y = read_word();
+            ps_cent_y = readWord();
             if (ps_cent_y > 32767)
                 ps_cent_y -= 65536;
             skip(2); /* ignore fractional part */
-            ps_cent_x = read_word();
+            ps_cent_x = readWord();
             if (ps_cent_x > 32767)
                 ps_cent_x -= 65536;
             skip(2); /* ignore fractional part */
@@ -621,7 +625,7 @@ ShortComment(struct canvas * const canvasP,
              blitList *      const blitListP,
              int             const version) {
 
-    picComment(read_word(), 0);
+    picComment(readWord(), 0);
 }
 
 
@@ -633,10 +637,10 @@ LongComment(struct canvas * const canvasP,
             blitList *      const blitListP,
             int             const version) {
 
-    word type;
+    Word type;
 
-    type = read_word();
-    picComment(type, read_word());
+    type = readWord();
+    picComment(type, readWord());
 }
 
 
@@ -649,7 +653,7 @@ skip_poly_or_region(struct canvas * const canvasP,
                     int             const version) {
 
     stage = "skipping polygon or region";
-    skip(read_word() - 2);
+    skip(readWord() - 2);
 }
 
 
@@ -788,7 +792,7 @@ dumpRect(const char * const label,
 
 
 static void
-read_rect(struct Rect * const r) {
+readRect(struct Rect * const r) {
 
     /* We don't have a formal specification for the Pict format, but we have
        seen samples that have the rectangle corners either in top left, bottom
@@ -800,10 +804,10 @@ read_rect(struct Rect * const r) {
        So now we accept all 4 possibilities.
     */
 
-    word const y1 = read_word();
-    word const x1 = read_word();
-    word const y2 = read_word();
-    word const x2 = read_word();
+    Word const y1 = readWord();
+    Word const x1 = readWord();
+    Word const y2 = readWord();
+    Word const x2 = readWord();
 
     r->top    = MIN(y1, y2);
     r->left   = MIN(x1, x2);
@@ -1202,7 +1206,7 @@ doDiffSize(struct Rect       const clipsrc,
 
     unsigned int const dstadd = dstwid - xsize;
 
-    FILE * pnmscalePipeP;
+    FILE * pamscalePipeP;
     const char * command;
     FILE * scaled;
     int cols, rows, format;
@@ -1211,9 +1215,9 @@ doDiffSize(struct Rect       const clipsrc,
     pixel * rowp;
     FILE * tempFileP;
     const char * tempFilename;
-    word * reddst;
-    word * grndst;
-    word * bludst;
+    Word * reddst;
+    Word * grndst;
+    Word * bludst;
 
     reddst = dst.red;  /* initial value */
     grndst = dst.grn;  /* initial value */
@@ -1223,19 +1227,19 @@ doDiffSize(struct Rect       const clipsrc,
 
     pm_close(tempFileP);
 
-    asprintfN(&command, "pnmscale -xsize %d -ysize %d > %s",
-              rectwidth(&clipdst), rectheight(&clipdst), tempFilename);
+    pm_asprintf(&command, "pamscale -xsize %d -ysize %d > %s",
+                rectwidth(&clipdst), rectheight(&clipdst), tempFilename);
 
     pm_message("running command '%s'", command);
 
-    pnmscalePipeP = popen(command, "w");
-    if (pnmscalePipeP == NULL)
+    pamscalePipeP = popen(command, "w");
+    if (pamscalePipeP == NULL)
         pm_error("cannot execute command '%s'  popen() errno = %s (%d)",
                  command, strerror(errno), errno);
 
-    strfree(command);
+    pm_strfree(command);
 
-    fprintf(pnmscalePipeP, "P6\n%d %d\n%d\n",
+    fprintf(pamscalePipeP, "P6\n%d %d\n%d\n",
             rectwidth(&clipsrc), rectheight(&clipsrc), PPM_MAXMAXVAL);
 
     switch (pixSize) {
@@ -1247,9 +1251,9 @@ doDiffSize(struct Rect       const clipsrc,
             for (colNumber = 0; colNumber < xsize; ++colNumber) {
                 unsigned int const colorIndex = row[colNumber];
                 struct RGBColor * const ct = &color_map[colorIndex];
-                fputc(redepth(ct->red, 65535L), pnmscalePipeP);
-                fputc(redepth(ct->grn, 65535L), pnmscalePipeP);
-                fputc(redepth(ct->blu, 65535L), pnmscalePipeP);
+                fputc(redepth(ct->red, 65535L), pamscalePipeP);
+                fputc(redepth(ct->grn, 65535L), pamscalePipeP);
+                fputc(redepth(ct->blu, 65535L), pamscalePipeP);
             }
         }
     }
@@ -1261,9 +1265,9 @@ doDiffSize(struct Rect       const clipsrc,
             unsigned int colNumber;
             for (colNumber = 0; colNumber < xsize; ++colNumber) {
                 struct RGBColor const color = decode16(&row[colNumber * 2]);
-                fputc(redepth(color.red, 32), pnmscalePipeP);
-                fputc(redepth(color.grn, 32), pnmscalePipeP);
-                fputc(redepth(color.blu, 32), pnmscalePipeP);
+                fputc(redepth(color.red, 32), pamscalePipeP);
+                fputc(redepth(color.grn, 32), pamscalePipeP);
+                fputc(redepth(color.blu, 32), pamscalePipeP);
             }
         }
     }
@@ -1280,17 +1284,17 @@ doDiffSize(struct Rect       const clipsrc,
 
             unsigned int colNumber;
             for (colNumber = 0; colNumber < xsize; ++colNumber) {
-                fputc(redepth(redPlane[colNumber], 256), pnmscalePipeP);
-                fputc(redepth(grnPlane[colNumber], 256), pnmscalePipeP);
-                fputc(redepth(bluPlane[colNumber], 256), pnmscalePipeP);
+                fputc(redepth(redPlane[colNumber], 256), pamscalePipeP);
+                fputc(redepth(grnPlane[colNumber], 256), pamscalePipeP);
+                fputc(redepth(bluPlane[colNumber], 256), pamscalePipeP);
             }
         }
     }
     break;
     }
 
-    if (pclose(pnmscalePipeP))
-        pm_error("pnmscale failed.  pclose() returned Errno %s (%d)",
+    if (pclose(pamscalePipeP))
+        pm_error("pamscale failed.  pclose() returned Errno %s (%d)",
                  strerror(errno), errno);
 
     ppm_readppminit(scaled = pm_openr(tempFilename), &cols, &rows,
@@ -1337,7 +1341,7 @@ doDiffSize(struct Rect       const clipsrc,
 
     pm_close(scaled);
     ppm_freerow(row);
-    strfree(tempFilename);
+    pm_strfree(tempFilename);
     unlink(tempFilename);
 }
 
@@ -1713,9 +1717,9 @@ allocPlanes(unsigned int       const width,
         pm_error("not enough memory to hold picture");
 
     /* initialize background to white */
-    memset(planes.red, 255, planelen * sizeof(word));
-    memset(planes.grn, 255, planelen * sizeof(word));
-    memset(planes.blu, 255, planelen * sizeof(word));
+    memset(planes.red, 255, planelen * sizeof(Word));
+    memset(planes.grn, 255, planelen * sizeof(Word));
+    memset(planes.blu, 255, planelen * sizeof(Word));
 
     *planesP = planes;
 }
@@ -1733,7 +1737,7 @@ freePlanes(struct rgbPlanes const planes) {
 
 
 static unsigned char
-compact(word const input) {
+compact(Word const input) {
 
     return (input >> 8) & 0xff;
 }
@@ -1919,41 +1923,49 @@ outputPpm(FILE *           const ofP,
  * All data in version 2 is 2-byte word aligned.  Odd size data
  * is padded with a null.
  */
-static word
+static Word
 get_op(int const version) {
     if ((align & 1) && version == 2) {
         stage = "aligning for opcode";
-        read_byte();
+        readByte();
     }
 
     stage = "reading opcode";
 
     if (version == 1)
-        return read_byte();
+        return readByte();
     else
-        return read_word();
+        return readWord();
 }
 
 
 
-static drawFn Clip;
+static drawFn ClipRgn;
 
 static void
-Clip(struct canvas * const canvasP,
-     blitList *      const blitListP,
-     int             const version) {
+ClipRgn(struct canvas * const canvasP,
+        blitList *      const blitListP,
+        int             const version) {
+
+    Word const len = readWord();
+        /* Length in bytes of the parameter (including this word) */
 
-    word len;
+    if (len == 10) {    /* null rgn */
+        /* Parameter is 2 bytes of length, 8 bytes of rectangle corners */
 
-    len = read_word();
+        /* In March 2011, I saw a supposed PICT file (reported to work with
+           Apple pictureViewer) with what looked like signed numbers for the
+           rectangle: (-32767,-32767), (32767, 32767).  This code has always
+           assumed all words in a PICT are unsigned.  But even when I changed
+           it to accept this clip rectangle, this program found the image to
+           have an invalid raster.
+        */
 
-    if (len == 0x000a) {    /* null rgn */
-        read_rect(&clip_rect);
+        readRect(&clip_rect);
         /* XXX should clip this by picFrame */
         if (verbose)
             dumpRect("clipping to", clip_rect);
-    }
-    else
+    } else
         skip(len - 2);
 }
 
@@ -1966,31 +1978,31 @@ OpColor(struct canvas * const canvasP,
         blitList *      const blitListP,
         int             const version) {
 
-    op_color.red = read_word();
-    op_color.grn = read_word();
-    op_color.blu = read_word();
+    op_color.red = readWord();
+    op_color.grn = readWord();
+    op_color.blu = readWord();
 }
 
 
 
 static void
-read_pixmap(struct pixMap * const p) {
+readPixmap(struct pixMap * const p) {
 
     stage = "getting pixMap header";
 
-    read_rect(&p->Bounds);
-    p->version    = read_word();
-    p->packType   = read_word();
-    p->packSize   = read_long();
-    p->hRes       = read_long();
-    p->vRes       = read_long();
-    p->pixelType  = read_word();
-    p->pixelSize  = read_word();
-    p->cmpCount   = read_word();
-    p->cmpSize    = read_word();
-    p->planeBytes = read_long();
-    p->pmTable    = read_long();
-    p->pmReserved = read_long();
+    readRect(&p->Bounds);
+    p->version    = readWord();
+    p->packType   = readWord();
+    p->packSize   = readLong();
+    p->hRes       = readLong();
+    p->vRes       = readLong();
+    p->pixelType  = readWord();
+    p->pixelSize  = readWord();
+    p->cmpCount   = readWord();
+    p->cmpSize    = readWord();
+    p->planeBytes = readLong();
+    p->pmTable    = readLong();
+    p->pmReserved = readLong();
 
     if (verbose) {
         pm_message("pixelType: %d", p->pixelType);
@@ -2013,19 +2025,19 @@ read_pixmap(struct pixMap * const p) {
 
 
 static struct RGBColor*
-read_color_table(void) {
-    longword ctSeed;
-    word ctFlags;
-    word ctSize;
-    word val;
+readColorTable(void) {
+    Longword ctSeed;
+    Word ctFlags;
+    Word ctSize;
+    Word val;
     int i;
     struct RGBColor* color_table;
 
     stage = "getting color table info";
 
-    ctSeed = read_long();
-    ctFlags = read_word();
-    ctSize = read_word();
+    ctSeed = readLong();
+    ctFlags = readWord();
+    ctSize = readWord();
 
     if (verbose) {
         pm_message("ctSeed:  %ld", ctSeed);
@@ -2040,7 +2052,7 @@ read_color_table(void) {
         pm_error("no memory for color table");
 
     for (i = 0; i <= ctSize; i++) {
-        val = read_word();
+        val = readWord();
         /* The indices in a device color table are bogus and usually == 0.
          * so I assume we allocate up the list of colors in order.
          */
@@ -2048,9 +2060,9 @@ read_color_table(void) {
             val = i;
         if (val > ctSize)
             pm_error("pixel value greater than color table size");
-        color_table[val].red = read_word();
-        color_table[val].grn = read_word();
-        color_table[val].blu = read_word();
+        color_table[val].red = readWord();
+        color_table[val].grn = readWord();
+        color_table[val].blu = readWord();
 
         if (verbose > 1)
             pm_message("Color %3u: [%u,%u,%u]", val,
@@ -2537,7 +2549,7 @@ interpretCompressedLine(unsigned char * const linebuf,
   So with 200 being the cutoff, it's actually impossible to represent some 
   16 bpp images with 200 pixels per row.
 
-  We have not been able to find an offical spec for PICT.
+  We have not been able to find an official spec for PICT.
 
   Some day, we may have to make a user option for this.
 */
@@ -2579,9 +2591,9 @@ unpackCompressedBits(FILE *          const ifP,
         unsigned int linelen;
 
         if (llsize == 2)
-            linelen = read_word();
+            linelen = readWord();
         else
-            linelen = read_byte();
+            linelen = readByte();
 
         reportValidateCompressedLineLen(row, linelen, raster.rowSize);
 
@@ -2604,7 +2616,7 @@ unpackCompressedBits(FILE *          const ifP,
 static void
 unpackbits(FILE *           const ifP,
            struct Rect *    const boundsP,
-           word             const rowBytesArg, 
+           Word             const rowBytesArg, 
            int              const bitsPerPixel,
            struct raster *  const rasterP) {
 
@@ -2639,7 +2651,7 @@ unpackbits(FILE *           const ifP,
 
 
 static void
-interpretRowBytesWord(word           const rowBytesWord,
+interpretRowBytesWord(Word           const rowBytesWord,
                       bool *         const pixMapP,
                       unsigned int * const rowBytesP) {
 
@@ -2657,13 +2669,13 @@ interpretRowBytesWord(word           const rowBytesWord,
  * a pattern in the fabled complete version.
  */
 static void
-read_pattern(void) {
+readPattern(void) {
 
-    word PatType;
+    Word PatType;
 
     stage = "Reading a pattern";
 
-    PatType = read_word();
+    PatType = readWord();
 
     switch (PatType) {
     case 2:
@@ -2671,7 +2683,7 @@ read_pattern(void) {
         skip(5); /* RGB for pattern */
         break;
     case 1: {
-        word rowBytesWord;
+        Word rowBytesWord;
         bool pixMap;
         unsigned int rowBytes;
         struct pixMap p;
@@ -2679,16 +2691,16 @@ read_pattern(void) {
         struct RGBColor * ct;
 
         skip(8); /* old pattern data */
-        rowBytesWord = read_word();
+        rowBytesWord = readWord();
         interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
-        read_pixmap(&p);
-        ct = read_color_table();
+        readPixmap(&p);
+        ct = readColorTable();
         unpackbits(ifp, &p.Bounds, rowBytes, p.pixelSize, &raster);
         freeRaster(raster);
         free(ct);
     } break;
     default:
-        pm_error("unknown pattern type in read_pattern");
+        pm_error("unknown pattern type in readPattern");
     }
 }
 
@@ -2703,7 +2715,7 @@ BkPixPat(struct canvas * const canvasP,
          blitList *      const blitListP,
          int             const version) {
 
-    read_pattern();
+    readPattern();
 }
 
 
@@ -2715,7 +2727,7 @@ PnPixPat(struct canvas * const canvasP,
          blitList *      const blitListP,
          int             const version) {
 
-    read_pattern();
+    readPattern();
 }
 
 
@@ -2727,13 +2739,13 @@ FillPixPat(struct canvas * const canvasP,
            blitList *      const blitListP,
            int             const version) {
 
-    read_pattern();
+    readPattern();
 }
 
 
 
 static void
-read_8x8_pattern(struct Pattern * const pat) {
+read8x8Pattern(struct Pattern * const pat) {
     unsigned char buf[8];
     unsigned char * exp;
     unsigned int len;
@@ -2762,7 +2774,7 @@ BkPat(struct canvas * const canvasP,
       blitList *      const blitListP,
       int             const version) {
 
-    read_8x8_pattern(&bkpat);
+    read8x8Pattern(&bkpat);
 }
 
 
@@ -2774,7 +2786,7 @@ PnPat(struct canvas * const canvasP,
       blitList *      const blitListP,
       int             const version) {
 
-    read_8x8_pattern(&pen_pat);
+    read8x8Pattern(&pen_pat);
 }
 
 
@@ -2786,7 +2798,7 @@ FillPat(struct canvas * const canvasP,
         blitList *      const blitListP,
         int             const version) {
 
-    read_8x8_pattern(&fillpat);
+    read8x8Pattern(&fillpat);
 }
 
 
@@ -2798,8 +2810,8 @@ PnSize(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
 
-    pen_height = read_word();
-    pen_width = read_word();
+    pen_height = readWord();
+    pen_width = readWord();
     if (verbose)
         pm_message("pen size %d x %d", pen_width, pen_height);
 }
@@ -2813,7 +2825,7 @@ PnMode(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
 
-    pen_mode = read_word();
+    pen_mode = readWord();
 
     if (pen_mode >= 8 && pen_mode < 15)
         pen_mode -= 8;
@@ -2827,10 +2839,10 @@ PnMode(struct canvas * const canvasP,
 
 
 static void 
-read_rgb(struct RGBColor * const rgb) {
-    rgb->red = read_word();
-    rgb->grn = read_word();
-    rgb->blu = read_word();
+readRgb(struct RGBColor * const rgb) {
+    rgb->red = readWord();
+    rgb->grn = readWord();
+    rgb->blu = readWord();
 }
 
 
@@ -2842,7 +2854,7 @@ RGBFgCol(struct canvas * const canvasP,
          blitList *      const blitListP,
          int             const version) {
 
-    read_rgb(&foreground);
+    readRgb(&foreground);
     if (verbose)
         pm_message("foreground now [%d,%d,%d]", 
             foreground.red, foreground.grn, foreground.blu);
@@ -2857,7 +2869,7 @@ RGBBkCol(struct canvas * const canvasP,
          blitList *      const blitListP,
          int             const version) {
 
-    read_rgb(&background);
+    readRgb(&background);
     if (verbose)
         pm_message("background now [%d,%d,%d]", 
             background.red, background.grn, background.blu);
@@ -2875,7 +2887,7 @@ draw_pixel(struct canvas *   const canvasP,
            transfer_func           trf) {
 
     if (x < clip_rect.left || x >= clip_rect.right ||
-        y < clip_rect.top || y >= clip_rect.bottom) {
+        y < clip_rect.top  || y >= clip_rect.bottom) {
     } else {
         unsigned int const i = PIXEL_INDEX(x, y);
 
@@ -3017,8 +3029,8 @@ Line(struct canvas * const canvasP,
      int             const version) {
 
   struct Point p1;
-  read_point(&p1);
-  read_point(&current);
+  readPoint(&p1);
+  readPoint(&current);
   if (verbose)
     pm_message("(%d,%d) to (%d, %d)",
            p1.x,p1.y,current.x,current.y);
@@ -3035,7 +3047,7 @@ LineFrom(struct canvas * const canvasP,
          int             const version) {
 
     struct Point p1;
-    read_point(&p1);
+    readPoint(&p1);
     if (verbose)
         pm_message("(%d,%d) to (%d, %d)", current.x, current.y, p1.x, p1.y);
 
@@ -3056,8 +3068,8 @@ ShortLine(struct canvas * const canvasP,
           int             const version) {
 
     struct Point p1;
-    read_point(&p1);
-    read_short_point(&current);
+    readPoint(&p1);
+    readShortPoint(&current);
     if (verbose)
         pm_message("(%d,%d) delta (%d, %d)", p1.x, p1.y, current.x, current.y);
     current.x += p1.x;
@@ -3077,7 +3089,7 @@ ShortLineFrom(struct canvas * const canvasP,
               int             const version) {
 
     struct Point p1;
-    read_short_point(&p1);
+    readShortPoint(&p1);
     if (verbose)
         pm_message("(%d,%d) delta (%d, %d)",
                    current.x,current.y,p1.x,p1.y);
@@ -3114,7 +3126,7 @@ paintRect(struct canvas * const canvasP,
           blitList *      const blitListP,
           int             const version) {
 
-    read_rect(&cur_rect);
+    readRect(&cur_rect);
     if (!blitListP)
         do_paintRect(canvasP, cur_rect);
 }
@@ -3165,7 +3177,7 @@ frameRect(struct canvas * const canvasP,
           blitList *      const blitListP,
           int             const version) {
 
-    read_rect(&cur_rect);
+    readRect(&cur_rect);
     if (!blitListP)
         do_frameRect(canvasP, cur_rect);
 }
@@ -3185,7 +3197,7 @@ frameSameRect(struct canvas * const canvasP,
 
 
 
-/* a stupid shell sort - I'm so embarassed  */
+/* a stupid shell sort - I'm so embarrassed  */
 
 static void 
 poly_sort(int const sort_index, struct Point points[]) {
@@ -3332,11 +3344,11 @@ paintPoly(struct canvas * const canvasP,
 
   struct Rect bb;
   struct Point pts[100];
-  int i, np = (read_word() - 10) >> 2;
+  int i, np = (readWord() - 10) >> 2;
 
-  read_rect(&bb);
+  readRect(&bb);
   for (i=0; i<np; ++i)
-    read_point(&pts[i]);
+    readPoint(&pts[i]);
 
   /* scan convert poly ... */
   if (!blitListP)
@@ -3352,7 +3364,7 @@ PnLocHFrac(struct canvas * const canvasP,
            blitList *      const blitListP,
            int             const version) {
 
-    word frac = read_word();
+    Word frac = readWord();
 
     if (verbose)
         pm_message("PnLocHFrac = %d", frac);
@@ -3367,7 +3379,7 @@ TxMode(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
 
-    text_mode = read_word();
+    text_mode = readWord();
 
     if (text_mode >= 8 && text_mode < 15)
         text_mode -= 8;
@@ -3388,7 +3400,7 @@ TxFont(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
 
-    text_font = read_word();
+    text_font = readWord();
     if (verbose)
         pm_message("text font %s", const_name(font_name, text_font));
 }
@@ -3402,7 +3414,7 @@ TxFace(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
 
-    text_face = read_byte();
+    text_face = readByte();
     if (verbose)
         pm_message("text face %d", text_face);
 }
@@ -3416,7 +3428,7 @@ TxSize(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
 
-    text_size = read_word();
+    text_size = readWord();
     if (verbose)
         pm_message("text size %d", text_size);
 }
@@ -3426,7 +3438,7 @@ TxSize(struct canvas * const canvasP,
 static void
 skip_text(blitList * const blitListP) {
 
-    skip(read_byte());
+    skip(readByte());
 
     blitListP->unblittableText = true;
 }
@@ -3510,11 +3522,11 @@ rotate(int * const x,
 
 static void
 do_ps_text(struct canvas * const canvasP,
-           word            const tx, 
-           word            const ty) {
+           Word            const tx, 
+           Word            const ty) {
 
     int len, width, i, w, h, x, y, rx, ry, o;
-    byte str[256], ch;
+    Byte str[256], ch;
     struct glyph* glyph;
 
     current.x = tx;
@@ -3526,12 +3538,12 @@ do_ps_text(struct canvas * const canvasP,
         ps_cent_set = 1;
     }
 
-    len = read_byte();
+    len = readByte();
 
     /* XXX this width calculation is not completely correct */
     width = 0;
     for (i = 0; i < len; i++) {
-        ch = str[i] = read_byte();
+        ch = str[i] = readByte();
         if (tfont->glyph[ch])
             width += tfont->glyph[ch]->xadd;
     }
@@ -3580,8 +3592,8 @@ do_ps_text(struct canvas * const canvasP,
 static void
 do_text(struct canvas *  const canvasP,
         blitList *       const blitListP,
-        word             const startx, 
-        word             const starty) {
+        Word             const startx, 
+        Word             const starty) {
 
     if (blitListP)
         skip_text(blitListP);
@@ -3593,12 +3605,12 @@ do_text(struct canvas *  const canvasP,
             do_ps_text(canvasP, startx, starty);
         else {
             int len;
-            word x, y;
+            Word x, y;
 
             x = startx;
             y = starty;
-            for (len = read_byte(); len > 0; --len) {
-                struct glyph* const glyph = tfont->glyph[read_byte()];
+            for (len = readByte(); len > 0; --len) {
+                struct glyph* const glyph = tfont->glyph[readByte()];
                 if (glyph) {
                     int dy;
                     int h;
@@ -3634,7 +3646,7 @@ LongText(struct canvas * const canvasP,
 
     struct Point p;
 
-    read_point(&p);
+    readPoint(&p);
 
     do_text(canvasP, blitListP, p.x, p.y);
 }
@@ -3648,7 +3660,7 @@ DHText(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
 
-    current.x += read_byte();
+    current.x += readByte();
 
     do_text(canvasP, blitListP, current.x, current.y);
 }
@@ -3662,7 +3674,7 @@ DVText(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
 
-    current.y += read_byte();
+    current.y += readByte();
 
     do_text(canvasP, blitListP, current.x, current.y);
 }
@@ -3675,10 +3687,10 @@ static void
 DHDVText(struct canvas * const canvasP,
          blitList *      const blitListP,
          int             const version) {
-    byte dh, dv;
+    Byte dh, dv;
 
-    dh = read_byte();
-    dv = read_byte();
+    dh = readByte();
+    dv = readByte();
 
     if (verbose)
         pm_message("dh, dv = %d, %d", dh, dv);
@@ -3692,7 +3704,7 @@ DHDVText(struct canvas * const canvasP,
 
 
 /*
- * This could use read_pixmap, but I'm too lazy to hack read_pixmap.
+ * This could use readPixmap, but I'm too lazy to hack readPixmap.
  */
 
 static void
@@ -3705,36 +3717,36 @@ directBits(struct canvas * const canvasP,
     struct Rect     srcRect;
     struct Rect     dstRect;
     struct raster   raster;
-    word            mode;
+    Word            mode;
     unsigned int    rectWidth;
 
     /* skip fake len, and fake EOF */
     skip(4);    /* Ptr baseAddr == 0x000000ff */
-    read_word();    /* version */
-    read_rect(&p.Bounds);
+    readWord();    /* version */
+    readRect(&p.Bounds);
     rectWidth = p.Bounds.right - p.Bounds.left;
-    p.packType = read_word();
-    p.packSize = read_long();
-    p.hRes = read_long();
-    p.vRes = read_long();
-    p.pixelType = read_word();
-    p.pixelSize = read_word();
-    p.pixelSize = read_word();    /* XXX twice??? */
-    p.cmpCount = read_word();
-    p.cmpSize = read_word();
-    p.planeBytes = read_long();
-    p.pmTable = read_long();
-    p.pmReserved = read_long();
-
-    read_rect(&srcRect);
+    p.packType = readWord();
+    p.packSize = readLong();
+    p.hRes = readLong();
+    p.vRes = readLong();
+    p.pixelType = readWord();
+    p.pixelSize = readWord();
+    p.pixelSize = readWord();    /* XXX twice??? */
+    p.cmpCount = readWord();
+    p.cmpSize = readWord();
+    p.planeBytes = readLong();
+    p.pmTable = readLong();
+    p.pmReserved = readLong();
+
+    readRect(&srcRect);
     if (verbose)
         dumpRect("source rectangle:", srcRect);
 
-    read_rect(&dstRect);
+    readRect(&dstRect);
     if (verbose)
         dumpRect("destination rectangle:", dstRect);
 
-    mode = read_word();
+    mode = readWord();
     if (verbose)
         pm_message("transfer mode = %s", const_name(transfer_name, mode));
 
@@ -3782,38 +3794,38 @@ static void
 do_pixmap(struct canvas * const canvasP,
           blitList *      const blitListP,
           int             const version, 
-          word            const rowBytes, 
+          Word            const rowBytes, 
           int             const is_region) {
 /*----------------------------------------------------------------------------
    Do a paletted image.
 -----------------------------------------------------------------------------*/
-    word mode;
+    Word mode;
     struct pixMap p;
     struct raster raster;
     struct RGBColor * color_table;
     struct Rect srcRect;
     struct Rect dstRect;
 
-    read_pixmap(&p);
+    readPixmap(&p);
 
     if (verbose)
         pm_message("%u x %u paletted image",
                    p.Bounds.right - p.Bounds.left,
                    p.Bounds.bottom - p.Bounds.top);
 
-    color_table = read_color_table();
+    color_table = readColorTable();
 
-    read_rect(&srcRect);
+    readRect(&srcRect);
 
     if (verbose)
         dumpRect("source rectangle:", srcRect);
 
-    read_rect(&dstRect);
+    readRect(&dstRect);
 
     if (verbose)
         dumpRect("destination rectangle:", dstRect);
 
-    mode = read_word();
+    mode = readWord();
 
     if (verbose)
         pm_message("transfer mode = %s", const_name(transfer_name, mode));
@@ -3849,7 +3861,7 @@ do_bitmap(FILE *          const ifP,
     struct Rect Bounds;
     struct Rect srcRect;
     struct Rect dstRect;
-    word mode;
+    Word mode;
     struct raster raster;
         /* This raster contains padding on the right to make a multiple
            of 16 pixels per row.
@@ -3857,10 +3869,10 @@ do_bitmap(FILE *          const ifP,
     static struct RGBColor color_table[] = { 
         {65535L, 65535L, 65535L}, {0, 0, 0} };
 
-    read_rect(&Bounds);
-    read_rect(&srcRect);
-    read_rect(&dstRect);
-    mode = read_word();
+    readRect(&Bounds);
+    readRect(&srcRect);
+    readRect(&dstRect);
+    mode = readWord();
     if (verbose)
         pm_message("transfer mode = %s", const_name(transfer_name, mode));
 
@@ -3886,12 +3898,12 @@ BitsRect(struct canvas * const canvasP,
          blitList *      const blitListP,
          int             const version) {
 
-    word rowBytesWord;
+    Word rowBytesWord;
     bool pixMap;
     unsigned int rowBytes;
 
     stage = "Reading rowBytes word for bitsrect";
-    rowBytesWord = read_word();
+    rowBytesWord = readWord();
 
     interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
 
@@ -3910,12 +3922,12 @@ BitsRegion(struct canvas * const canvasP,
            blitList *      const blitListP,
            int             const version) {
     
-    word rowBytesWord;
+    Word rowBytesWord;
     bool pixMap;
     unsigned int rowBytes;
 
     stage = "Reading rowBytes for bitsregion";
-    rowBytesWord = read_word();
+    rowBytesWord = readWord();
 
     interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
 
@@ -3933,7 +3945,7 @@ BitsRegion(struct canvas * const canvasP,
   */
 static struct opdef const optable[] = {
 /* 0x00 */  { "NOP", 0, NULL, "nop" },
-/* 0x01 */  { "Clip", NA, Clip, "clip" },
+/* 0x01 */  { "ClipRgn", NA, ClipRgn, "clip region" },
 /* 0x02 */  { "BkPat", 8, BkPat, "background pattern" },
 /* 0x03 */  { "TxFont", 2, TxFont, "text font (word)" },
 /* 0x04 */  { "TxFace", 1, TxFace, "text face (byte)" },
@@ -3955,9 +3967,9 @@ static struct opdef const optable[] = {
 /* 0x14 */  { "FillPixPat", NA, FillPixPat, "color fill pattern" },
 /* 0x15 */  { "PnLocHFrac", 2, PnLocHFrac, "fractional pen position" },
 /* 0x16 */  { "ChExtra", 2, NULL, "extra for each character" },
-/* 0x17 */  res(0),
-/* 0x18 */  res(0),
-/* 0x19 */  res(0),
+/* 0x17 */  RESERVED_OP(0),
+/* 0x18 */  RESERVED_OP(0),
+/* 0x19 */  RESERVED_OP(0),
 /* 0x1a */  { "RGBFgCol", RGB_LEN, RGBFgCol, "RGB foreColor" },
 /* 0x1b */  { "RGBBkCol", RGB_LEN, RGBBkCol, "RGB backColor" },
 /* 0x1c */  { "HiliteMode", 0, NULL, "hilite mode flag" },
@@ -3969,134 +3981,134 @@ static struct opdef const optable[] = {
 /* 0x22 */  { "ShortLine", 6, ShortLine, 
               "pnLoc (point, dh, dv (-128 .. 127))" },
 /* 0x23 */  { "ShortLineFrom", 2, ShortLineFrom, "dh, dv (-128 .. 127)" },
-/* 0x24 */  res(WORD_LEN),
-/* 0x25 */  res(WORD_LEN),
-/* 0x26 */  res(WORD_LEN),
-/* 0x27 */  res(WORD_LEN),
+/* 0x24 */  RESERVED_OP(WORD_LEN),
+/* 0x25 */  RESERVED_OP(WORD_LEN),
+/* 0x26 */  RESERVED_OP(WORD_LEN),
+/* 0x27 */  RESERVED_OP(WORD_LEN),
 /* 0x28 */  { "LongText", NA, LongText, 
               "txLoc (point), count (0..255), text" },
 /* 0x29 */  { "DHText", NA, DHText, "dh (0..255), count (0..255), text" },
 /* 0x2a */  { "DVText", NA, DVText, "dv (0..255), count (0..255), text" },
 /* 0x2b */  { "DHDVText", NA, DHDVText, 
               "dh, dv (0..255), count (0..255), text" },
-/* 0x2c */  res(WORD_LEN),
-/* 0x2d */  res(WORD_LEN),
-/* 0x2e */  res(WORD_LEN),
-/* 0x2f */  res(WORD_LEN),
+/* 0x2c */  RESERVED_OP(WORD_LEN),
+/* 0x2d */  RESERVED_OP(WORD_LEN),
+/* 0x2e */  RESERVED_OP(WORD_LEN),
+/* 0x2f */  RESERVED_OP(WORD_LEN),
 /* 0x30 */  { "frameRect", 8, frameRect, "rect" },
 /* 0x31 */  { "paintRect", 8, paintRect, "rect" },
 /* 0x32 */  { "eraseRect", 8, NULL, "rect" },
 /* 0x33 */  { "invertRect", 8, NULL, "rect" },
 /* 0x34 */  { "fillRect", 8, NULL, "rect" },
-/* 0x35 */  res(8),
-/* 0x36 */  res(8),
-/* 0x37 */  res(8),
+/* 0x35 */  RESERVED_OP(8),
+/* 0x36 */  RESERVED_OP(8),
+/* 0x37 */  RESERVED_OP(8),
 /* 0x38 */  { "frameSameRect", 0, frameSameRect, "rect" },
 /* 0x39 */  { "paintSameRect", 0, paintSameRect, "rect" },
 /* 0x3a */  { "eraseSameRect", 0, NULL, "rect" },
 /* 0x3b */  { "invertSameRect", 0, NULL, "rect" },
 /* 0x3c */  { "fillSameRect", 0, NULL, "rect" },
-/* 0x3d */  res(0),
-/* 0x3e */  res(0),
-/* 0x3f */  res(0),
+/* 0x3d */  RESERVED_OP(0),
+/* 0x3e */  RESERVED_OP(0),
+/* 0x3f */  RESERVED_OP(0),
 /* 0x40 */  { "frameRRect", 8, NULL, "rect" },
 /* 0x41 */  { "paintRRect", 8, NULL, "rect" },
 /* 0x42 */  { "eraseRRect", 8, NULL, "rect" },
 /* 0x43 */  { "invertRRect", 8, NULL, "rect" },
 /* 0x44 */  { "fillRRrect", 8, NULL, "rect" },
-/* 0x45 */  res(8),
-/* 0x46 */  res(8),
-/* 0x47 */  res(8),
+/* 0x45 */  RESERVED_OP(8),
+/* 0x46 */  RESERVED_OP(8),
+/* 0x47 */  RESERVED_OP(8),
 /* 0x48 */  { "frameSameRRect", 0, NULL, "rect" },
 /* 0x49 */  { "paintSameRRect", 0, NULL, "rect" },
 /* 0x4a */  { "eraseSameRRect", 0, NULL, "rect" },
 /* 0x4b */  { "invertSameRRect", 0, NULL, "rect" },
 /* 0x4c */  { "fillSameRRect", 0, NULL, "rect" },
-/* 0x4d */  res(0),
-/* 0x4e */  res(0),
-/* 0x4f */  res(0),
+/* 0x4d */  RESERVED_OP(0),
+/* 0x4e */  RESERVED_OP(0),
+/* 0x4f */  RESERVED_OP(0),
 /* 0x50 */  { "frameOval", 8, NULL, "rect" },
 /* 0x51 */  { "paintOval", 8, NULL, "rect" },
 /* 0x52 */  { "eraseOval", 8, NULL, "rect" },
 /* 0x53 */  { "invertOval", 8, NULL, "rect" },
 /* 0x54 */  { "fillOval", 8, NULL, "rect" },
-/* 0x55 */  res(8),
-/* 0x56 */  res(8),
-/* 0x57 */  res(8),
+/* 0x55 */  RESERVED_OP(8),
+/* 0x56 */  RESERVED_OP(8),
+/* 0x57 */  RESERVED_OP(8),
 /* 0x58 */  { "frameSameOval", 0, NULL, "rect" },
 /* 0x59 */  { "paintSameOval", 0, NULL, "rect" },
 /* 0x5a */  { "eraseSameOval", 0, NULL, "rect" },
 /* 0x5b */  { "invertSameOval", 0, NULL, "rect" },
 /* 0x5c */  { "fillSameOval", 0, NULL, "rect" },
-/* 0x5d */  res(0),
-/* 0x5e */  res(0),
-/* 0x5f */  res(0),
+/* 0x5d */  RESERVED_OP(0),
+/* 0x5e */  RESERVED_OP(0),
+/* 0x5f */  RESERVED_OP(0),
 /* 0x60 */  { "frameArc", 12, NULL, "rect, startAngle, arcAngle" },
 /* 0x61 */  { "paintArc", 12, NULL, "rect, startAngle, arcAngle" },
 /* 0x62 */  { "eraseArc", 12, NULL, "rect, startAngle, arcAngle" },
 /* 0x63 */  { "invertArc", 12, NULL, "rect, startAngle, arcAngle" },
 /* 0x64 */  { "fillArc", 12, NULL, "rect, startAngle, arcAngle" },
-/* 0x65 */  res(12),
-/* 0x66 */  res(12),
-/* 0x67 */  res(12),
+/* 0x65 */  RESERVED_OP(12),
+/* 0x66 */  RESERVED_OP(12),
+/* 0x67 */  RESERVED_OP(12),
 /* 0x68 */  { "frameSameArc", 4, NULL, "rect, startAngle, arcAngle" },
 /* 0x69 */  { "paintSameArc", 4, NULL, "rect, startAngle, arcAngle" },
 /* 0x6a */  { "eraseSameArc", 4, NULL, "rect, startAngle, arcAngle" },
 /* 0x6b */  { "invertSameArc", 4, NULL, "rect, startAngle, arcAngle" },
 /* 0x6c */  { "fillSameArc", 4, NULL, "rect, startAngle, arcAngle" },
-/* 0x6d */  res(4),
-/* 0x6e */  res(4),
-/* 0x6f */  res(4),
+/* 0x6d */  RESERVED_OP(4),
+/* 0x6e */  RESERVED_OP(4),
+/* 0x6f */  RESERVED_OP(4),
 /* 0x70 */  { "framePoly", NA, skip_poly_or_region, "poly" },
 /* 0x71 */  { "paintPoly", NA, paintPoly, "poly" },
 /* 0x72 */  { "erasePoly", NA, skip_poly_or_region, "poly" },
 /* 0x73 */  { "invertPoly", NA, skip_poly_or_region, "poly" },
 /* 0x74 */  { "fillPoly", NA, skip_poly_or_region, "poly" },
-/* 0x75 */  resf(skip_poly_or_region),
-/* 0x76 */  resf(skip_poly_or_region),
-/* 0x77 */  resf(skip_poly_or_region),
+/* 0x75 */  RESERVED_OP_F(skip_poly_or_region),
+/* 0x76 */  RESERVED_OP_F(skip_poly_or_region),
+/* 0x77 */  RESERVED_OP_F(skip_poly_or_region),
 /* 0x78 */  { "frameSamePoly", 0, NULL, "poly (NYI)" },
 /* 0x79 */  { "paintSamePoly", 0, NULL, "poly (NYI)" },
 /* 0x7a */  { "eraseSamePoly", 0, NULL, "poly (NYI)" },
 /* 0x7b */  { "invertSamePoly", 0, NULL, "poly (NYI)" },
 /* 0x7c */  { "fillSamePoly", 0, NULL, "poly (NYI)" },
-/* 0x7d */  res(0),
-/* 0x7e */  res(0),
-/* 0x7f */  res(0),
+/* 0x7d */  RESERVED_OP(0),
+/* 0x7e */  RESERVED_OP(0),
+/* 0x7f */  RESERVED_OP(0),
 /* 0x80 */  { "frameRgn", NA, skip_poly_or_region, "region" },
 /* 0x81 */  { "paintRgn", NA, skip_poly_or_region, "region" },
 /* 0x82 */  { "eraseRgn", NA, skip_poly_or_region, "region" },
 /* 0x83 */  { "invertRgn", NA, skip_poly_or_region, "region" },
 /* 0x84 */  { "fillRgn", NA, skip_poly_or_region, "region" },
-/* 0x85 */  resf(skip_poly_or_region),
-/* 0x86 */  resf(skip_poly_or_region),
-/* 0x87 */  resf(skip_poly_or_region),
+/* 0x85 */  RESERVED_OP_F(skip_poly_or_region),
+/* 0x86 */  RESERVED_OP_F(skip_poly_or_region),
+/* 0x87 */  RESERVED_OP_F(skip_poly_or_region),
 /* 0x88 */  { "frameSameRgn", 0, NULL, "region (NYI)" },
 /* 0x89 */  { "paintSameRgn", 0, NULL, "region (NYI)" },
 /* 0x8a */  { "eraseSameRgn", 0, NULL, "region (NYI)" },
 /* 0x8b */  { "invertSameRgn", 0, NULL, "region (NYI)" },
 /* 0x8c */  { "fillSameRgn", 0, NULL, "region (NYI)" },
-/* 0x8d */  res(0),
-/* 0x8e */  res(0),
-/* 0x8f */  res(0),
+/* 0x8d */  RESERVED_OP(0),
+/* 0x8e */  RESERVED_OP(0),
+/* 0x8f */  RESERVED_OP(0),
 /* 0x90 */  { "BitsRect", NA, BitsRect, "copybits, rect clipped" },
 /* 0x91 */  { "BitsRgn", NA, BitsRegion, "copybits, rgn clipped" },
-/* 0x92 */  res(WORD_LEN),
-/* 0x93 */  res(WORD_LEN),
-/* 0x94 */  res(WORD_LEN),
-/* 0x95 */  res(WORD_LEN),
-/* 0x96 */  res(WORD_LEN),
-/* 0x97 */  res(WORD_LEN),
+/* 0x92 */  RESERVED_OP(WORD_LEN),
+/* 0x93 */  RESERVED_OP(WORD_LEN),
+/* 0x94 */  RESERVED_OP(WORD_LEN),
+/* 0x95 */  RESERVED_OP(WORD_LEN),
+/* 0x96 */  RESERVED_OP(WORD_LEN),
+/* 0x97 */  RESERVED_OP(WORD_LEN),
 /* 0x98 */  { "PackBitsRect", NA, BitsRect, "packed copybits, rect clipped" },
 /* 0x99 */  { "PackBitsRgn", NA, BitsRegion, "packed copybits, rgn clipped" },
 /* 0x9a */  { "DirectBitsRect", NA, DirectBitsRect, 
               "PixMap, srcRect, dstRect, int copymode, PixData" },
 /* 0x9b */  { "DirectBitsRgn", NA, DirectBitsRgn, 
               "PixMap, srcRect, dstRect, int copymode, maskRgn, PixData" },
-/* 0x9c */  res(WORD_LEN),
-/* 0x9d */  res(WORD_LEN),
-/* 0x9e */  res(WORD_LEN),
-/* 0x9f */  res(WORD_LEN),
+/* 0x9c */  RESERVED_OP(WORD_LEN),
+/* 0x9d */  RESERVED_OP(WORD_LEN),
+/* 0x9e */  RESERVED_OP(WORD_LEN),
+/* 0x9f */  RESERVED_OP(WORD_LEN),
 /* 0xa0 */  { "ShortComment", 2, ShortComment, "kind (word)" },
 /* 0xa1 */  { "LongComment", NA, LongComment, 
               "kind (word), size (word), data" }
@@ -4105,7 +4117,7 @@ static struct opdef const optable[] = {
 
 
 static void
-processOpcode(word const opcode, 
+processOpcode(Word            const opcode, 
               struct canvas * const canvasP,
               blitList *      const blitListP,
               unsigned int    const version) {
@@ -4124,13 +4136,14 @@ processOpcode(word const opcode,
         else if (optable[opcode].len >= 0)
             skip(optable[opcode].len);
         else {
+            /* It's a special length code */
             switch (optable[opcode].len) {
             case WORD_LEN: {
-                word const len = read_word();
+                Word const len = readWord();
                 skip(len);
-                } break;
+            } break;
             default:
-                pm_error("can't do length %u", optable[opcode].len);
+                pm_error("can't do length %d", optable[opcode].len);
             }
         }
     } else if (opcode == 0xc00) {
@@ -4142,7 +4155,7 @@ processOpcode(word const opcode,
         stage = "skipping reserved";
         if (verbose)
             pm_message("%s 0x%x", stage, opcode);
-        skip(read_word());
+        skip(readWord());
     } else if (opcode >= 0xb0 && opcode <= 0xcf) {
         /* just a reserved opcode, no data */
         if (verbose)
@@ -4151,21 +4164,21 @@ processOpcode(word const opcode,
         stage = "skipping reserved";
         if (verbose)
             pm_message("%s 0x%x", stage, opcode);
-        skip(read_long());
+        skip(readLong());
     } else if (opcode >= 0x100 && opcode <= 0x7fff) {
         stage = "skipping reserved";
         if (verbose)
             pm_message("%s 0x%x", stage, opcode);
         skip((opcode >> 7) & 255);
     } else if (opcode >= 0x8000 && opcode <= 0x80ff) {
-        /* just a reserved opcode */
+        /* just a reserved opcode, no data */
         if (verbose)
             pm_message("reserved 0x%x", opcode);
     } else if (opcode >= 0x8100) {
         stage = "skipping reserved";
         if (verbose)
             pm_message("%s 0x%x", stage, opcode);
-        skip(read_long());
+        skip(readLong());
     } else
         pm_error("This program does not understand opcode 0x%04x", opcode);
 }
@@ -4173,13 +4186,13 @@ processOpcode(word const opcode,
 
 
 static void
-interpret_pict(FILE * const ofP) {
+interpretPict(FILE * const ofP) {
 
-    byte ch;
-    word picSize;
-    word opcode;
+    Byte ch;
+    Word picSize;
+    Word opcode;
     unsigned int version;
-    int i;
+    unsigned int i;
     struct canvas canvas;
     blitList blitList;
 
@@ -4194,13 +4207,13 @@ interpret_pict(FILE * const ofP) {
     text_trf = transfer(text_mode);
 
     stage = "Reading picture size";
-    picSize = read_word();
+    picSize = readWord();
 
     if (verbose)
         pm_message("picture size = %u (0x%x)", picSize, picSize);
 
     stage = "reading picture frame";
-    read_rect(&picFrame);
+    readRect(&picFrame);
 
     if (verbose) {
         dumpRect("Picture frame:", picFrame);
@@ -4218,18 +4231,18 @@ interpret_pict(FILE * const ofP) {
         clip_rect = picFrame;
     }
 
-    while ((ch = read_byte()) == 0)
+    while ((ch = readByte()) == 0)
         ;
     if (ch != 0x11)
         pm_error("No version number");
 
-    version = read_byte();
+    version = readByte();
 
     switch (version) {
     case 1:
         break;
     case 2: {
-        unsigned char const subcode = read_byte();
+        unsigned char const subcode = readByte();
         if (subcode != 0xff)
             pm_error("The only Version 2 PICT images this program "
                      "undertands are subcode 0xff.  This image has "
@@ -4328,7 +4341,7 @@ main(int argc, char * argv[]) {
         skip(512);
     }
 
-    interpret_pict(stdout);
+    interpretPict(stdout);
 
     pm_close(stdout);
 
diff --git a/converter/ppm/ppmtoacad.c b/converter/ppm/ppmtoacad.c
index 4f927f02..b5ee4d65 100644
--- a/converter/ppm/ppmtoacad.c
+++ b/converter/ppm/ppmtoacad.c
@@ -20,10 +20,9 @@
 */
 
 #include <stdio.h>
-#include "ppm.h"
 
-#define TRUE     1
-#define FALSE    0
+#include "pm_c_util.h"
+#include "ppm.h"
 
 #define EOS     '\0'
 
diff --git a/converter/ppm/ppmtoapplevol.c b/converter/ppm/ppmtoapplevol.c
new file mode 100644
index 00000000..e1c7f2dc
--- /dev/null
+++ b/converter/ppm/ppmtoapplevol.c
@@ -0,0 +1,95 @@
+/* ppmtoapplevol.c - read a portable pixmap and produce an Apple volume label
+ *
+ * Copyright 2011 Red Hat <mjg@redhat.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.
+ */
+
+#include "pm.h"
+#include "ppm.h"
+
+
+
+static unsigned char const map[] = {
+    0x00, 0xf6, 0xf7, 0x2a, 0xf8, 0xf9, 0x55, 0xfa,
+    0xfb, 0x80, 0xfc, 0xfd, 0xab, 0xfe, 0xff, 0xd6
+};
+
+
+
+static void
+writeHeader(unsigned int const cols,
+            FILE *       const ofP) {
+
+    unsigned char header[5];
+
+    header[0] = 0x01;
+    header[1] = 0x00;
+    header[2] = cols;
+    header[3] = 0x00;
+    header[4] = 0x0c;
+
+    fwrite(header, sizeof(header), 1, ofP);
+}
+
+
+
+int
+main (int argc, const char * argv[]) {
+
+    const char * inputFilename;
+    FILE * ifP;
+    int rows, cols;
+    pixval maxval;
+    int format;
+    pixel * pixelrow;
+    unsigned int row;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments: %u.  There is at most one argument: "
+                 "the input file name", argc-1);
+
+    if (argc-1 >= 1)
+        inputFilename = argv[1];
+    else
+        inputFilename = "-";
+
+    ifP = pm_openr(inputFilename);
+
+    ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
+
+    if (rows != 12)
+        pm_error("Input image must be 12 rows tall.  Yours is %u", rows);
+
+    writeHeader(cols, stdout);
+
+    pixelrow = ppm_allocrow(cols);
+
+    for (row = 0; row < rows; row++) {
+        unsigned int col;
+        
+        ppm_readppmrow(stdin, pixelrow, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col) {
+            unsigned int const maxval15Value =
+                ((unsigned int)PPM_GETR(pixelrow[col]) * 15 + maxval/2) /
+                maxval;
+            unsigned char const appleValue =
+                (unsigned char)map[15 - maxval15Value];
+            fwrite(&appleValue, sizeof(appleValue), 1, stdout);
+        }
+    }
+
+    ppm_freerow(pixelrow);
+
+    pm_close(ifP);
+
+    return 0;
+}
diff --git a/converter/ppm/ppmtoarbtxt.c b/converter/ppm/ppmtoarbtxt.c
index df859c84..fc8927ce 100644
--- a/converter/ppm/ppmtoarbtxt.c
+++ b/converter/ppm/ppmtoarbtxt.c
@@ -1,4 +1,4 @@
-/* ppmtoarbtxt.c - convert portable pixmap to cleartext
+/* ppmtoarbtxt.c - convert PPM to a custom text-based format
 **
 ** Renamed from ppmtotxt.c by Bryan Henderson in January 2003.
 **
@@ -12,51 +12,151 @@
 ** implied warranty.
 */
 
+#include <assert.h>
 #include <string.h>
+#ifdef __GLIBC__
+  #include <printf.h>  /* Necessary for parse_printf_format() */
+#endif
 
-#include "ppm.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
+#include "shhopt.h"
+#include "ppm.h"
+
+/* HAVE_PARSE_PRINTF_FORMAT means the system library has
+   parse_printf_format(), declared in <printf.h>.  This essentially means
+   systems with GNU libc.
+*/
+
+#ifndef HAVE_PARSE_PRINTF_FORMAT
+  #ifdef PA_FLAG_MASK                   /* Defined in printf.h */
+    #define HAVE_PARSE_PRINTF_FORMAT 1
+  #else
+    #define HAVE_PARSE_PRINTF_FORMAT 0
+  #endif
+#endif
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+    const char * bodySklFileName;
+    const char * hd;
+    const char * tl;
+    unsigned int debug;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that many of the strings that this function returns in the
+   *cmdline_p structure are actually in the supplied argv array.  And
+   sometimes, one of these strings is actually just a suffix of an entry
+   in argv!
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int hdSpec, tlSpec;
+
+    unsigned int option_def_index;
+    
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "hd",   OPT_STRING, &cmdlineP->hd, 
+            &hdSpec,             0);
+    OPTENT3(0,   "tl",   OPT_STRING, &cmdlineP->tl,
+            &tlSpec,             0);
+    OPTENT3(0,   "debug", OPT_FLAG, NULL,
+            &cmdlineP->debug,      0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    free(option_def);
+
+    if (!hdSpec)
+        cmdlineP->hd = NULL;
+
+    if (!tlSpec)
+        cmdlineP->tl = NULL;
+
+    if (argc-1 < 1)
+        pm_error("You must specify the body skeleton file name as an "
+                 "argument");
+    else {
+        cmdlineP->bodySklFileName = strdup(argv[1]);
+
+        if (argc-1 < 2)
+            cmdlineP->inputFileName = strdup("-");  /* he wants stdin */
+        else {
+            cmdlineP->inputFileName = strdup(argv[2]);
+            if (argc-1 > 2)
+                pm_error("Too many arguments.  The only possible arguments "
+                         "are the body skeleton file name and input image "
+                         "file name");
+        }
+    }
+}
+
+
+
 
 typedef enum {
 /* The types of object we handle */
     BDATA, IRED, IGREEN, IBLUE, ILUM, FRED, FGREEN, FBLUE, FLUM,
     WIDTH, HEIGHT, POSX, POSY
-} SKL_OBJ_TYP;
+} SkeletonObjectType;
 
 typedef enum {
     OBJTYP_ICOLOR, OBJTYP_FCOLOR, OBJTYP_INT, OBJTYP_BDATA
-} SKL_OBJ_CLASS;
+} SkeletonObjectClass;
 
 /* Maximum size for a format string ("%d" etc.) */
+/* Add one to this size for the terminating '\0'. */
 #define MAXFORMAT 16
 
+typedef union {
 /* The data we keep for each object */
-typedef union
- {
-  struct BNDAT { char *bdat;   /* Binary data (text with newlines etc.) */
-                 int ndat;
-               } bin_data;
-
-  struct ICDAT { char icformat[MAXFORMAT];  /* Integer colors */
-                 int icolmin, icolmax;
-               } icol_data;
-
-  struct FCDAT { char fcformat[MAXFORMAT];  /* Float colors */
-                 double fcolmin, fcolmax;
-               } fcol_data;
-
-  struct IDAT  { char iformat[MAXFORMAT];   /* Integer data */
-               } i_data;
- } SKL_OBJ_DATA;
+    struct Bndat {
+        char * bdat;   /* Binary data (text with newlines etc.) */
+        unsigned int ndat;
+    } binData;
+    
+    struct Icdat {
+        char icformat[MAXFORMAT+1];  /* Integer colors */
+        unsigned int icolmin, icolmax;
+    } icolData;
+
+    struct Fcdat {
+        char fcformat[MAXFORMAT+1];  /* Float colors */
+        double fcolmin, fcolmax;
+    } fcolData;
+    
+    struct Idat {
+        char iformat[MAXFORMAT+1];   /* Integer data */
+    } iData;
+} SkeletonObjectData;
 
 
 /* Each object has a type and some data */
-typedef struct
- { 
-   SKL_OBJ_TYP otyp;
-   SKL_OBJ_DATA odata;
- } SKL_OBJ;
+typedef struct { 
+    SkeletonObjectType objType;
+    SkeletonObjectData odata;
+} SkeletonObject;
+
 
 
 #define MAX_SKL_HEAD_OBJ 64
@@ -66,199 +166,168 @@ typedef struct
 #define MAX_OBJ_BUF 80
 
 
-static void write_txt (fout, nobj, obj, width, height, x, y, red, green, blue)
-FILE *fout;
-int nobj;
-SKL_OBJ *obj[];
-int width, height, x, y;
-double red, green, blue;
-
-{register int count;
-
-#define WRITE_BNDAT(fd,theobj) \
- {struct BNDAT *bdata = &((theobj)->odata.bin_data); \
-       fwrite (bdata->bdat,bdata->ndat,1,fd); }
-
-#define WRITE_ICOL(fd,theobj,thecol) \
- {struct ICDAT *icdata = &((theobj)->odata.icol_data); \
-  fprintf (fd,icdata->icformat,(int)(icdata->icolmin \
-                + (icdata->icolmax - icdata->icolmin)*(thecol))); }
-
-#define WRITE_FCOL(fd,theobj,thecol) \
- {struct FCDAT *fcdata = &((theobj)->odata.fcol_data); \
-  fprintf (fd,fcdata->fcformat,(double)(fcdata->fcolmin \
-                + (fcdata->fcolmax - fcdata->fcolmin)*(thecol))); }
-
-#define WRITE_IDAT(fd,theobj,thedat) \
- {struct IDAT *idata = &((theobj)->odata.i_data); \
-  fprintf (fd,idata->iformat,thedat); }
-
- for (count = 0; count < nobj; count++)
- {
-   switch (obj[count]->otyp)
-   {
-     case BDATA:
-       WRITE_BNDAT (fout,obj[count]);
-       break;
-     case IRED:
-       WRITE_ICOL (fout,obj[count],red);
-       break;
-     case IGREEN:
-       WRITE_ICOL (fout,obj[count],green);
-       break;
-     case IBLUE:
-       WRITE_ICOL (fout,obj[count],blue);
-       break;
-     case ILUM:
-       WRITE_ICOL (fout,obj[count],0.299*red+0.587*green+0.114*blue);
-       break;
-     case FRED:
-       WRITE_FCOL (fout,obj[count],red);
-       break;
-     case FGREEN:
-       WRITE_FCOL (fout,obj[count],green);
-       break;
-     case FBLUE:
-       WRITE_FCOL (fout,obj[count],blue);
-       break;
-     case FLUM:
-       WRITE_FCOL (fout,obj[count],0.299*red+0.587*green+0.114*blue);
-       break;
-     case WIDTH:
-       WRITE_IDAT (fout,obj[count],width);
-       break;
-     case HEIGHT:
-       WRITE_IDAT (fout,obj[count],height);
-       break;
-     case POSX:
-       WRITE_IDAT (fout,obj[count],x);
-       break;
-     case POSY:
-       WRITE_IDAT (fout,obj[count],y);
-       break;
-   }
- }
-}
 
+static void
+dumpSkeleton(SkeletonObject ** const skeletonPList,
+             unsigned int      const nSkeleton) {
 
-static SKL_OBJ *
-save_bin_data(int    const ndat, 
-              char * const bdat) {
+    unsigned int i;
 
-    /* Save binary data in Object */
+    pm_message("%u objects", nSkeleton);
 
-    SKL_OBJ *obj;
+    for (i = 0; i < nSkeleton; ++i) {
+        SkeletonObject * const skeletonP = skeletonPList[i];
 
-    obj = (SKL_OBJ *)malloc (sizeof (SKL_OBJ) + ndat);
-    if (obj != NULL)
-    {
-        obj->otyp = BDATA;
-        obj->odata.bin_data.ndat = ndat;
-        obj->odata.bin_data.bdat = ((char *)(obj))+sizeof (SKL_OBJ);
-        memcpy (obj->odata.bin_data.bdat,bdat,ndat);
+        pm_message("  Object: Type %u", skeletonP->objType);
     }
-    return (obj);
 }
 
 
 
-static SKL_OBJ *
-save_icol_data(SKL_OBJ_TYP  const ctyp,
-               const char * const format,
-               int          const icolmin,
-               int          const icolmax) {
-/* Save integer color data in Object */
-
-    SKL_OBJ * objP;
+static void
+dumpAllSkeleton(SkeletonObject ** const bodySkeletonPList,
+                unsigned int      const bodyNskl,
+                SkeletonObject ** const headSkeletonPList, 
+                unsigned int      const headNskl,
+                SkeletonObject ** const tailSkeletonPList,
+                unsigned int      const tailNskl) {
+    
+    pm_message("Body skeleton:");
+    dumpSkeleton(bodySkeletonPList, bodyNskl);
+
+    pm_message("Head skeleton:");
+    dumpSkeleton(headSkeletonPList, headNskl);
+
+    pm_message("Tail skeleton:");
+    dumpSkeleton(tailSkeletonPList, tailNskl);
+}
 
-    MALLOCVAR(objP);
 
-    if (objP) {
-        objP->otyp = ctyp;
-        strcpy(objP->odata.icol_data.icformat, format);
-        objP->odata.icol_data.icolmin = icolmin;
-        objP->odata.icol_data.icolmax = icolmax;
-    }
-    return objP;
-}
 
+static void
+writeBndat(FILE *           const ofP,
+           SkeletonObject * const objectP) {
 
+    struct Bndat * const bdataP = &objectP->odata.binData;
 
-static SKL_OBJ *
-save_fcol_data(SKL_OBJ_TYP  const ctyp,
-               const char * const format,
-               double       const fcolmin,
-               double       const fcolmax) {
-/* Save float color data in Object */
+    fwrite(bdataP->bdat, bdataP->ndat, 1, ofP);
+}
 
-    SKL_OBJ * objP;
 
-    MALLOCVAR(objP);
 
-    if (objP) {
-        objP->otyp = ctyp;
-        strcpy(objP->odata.fcol_data.fcformat, format);
-        objP->odata.fcol_data.fcolmin = fcolmin;
-        objP->odata.fcol_data.fcolmax = fcolmax;
-    }
-    return objP;
-}
+static void
+writeIcol(FILE *           const ofP,
+          SkeletonObject * const objectP,
+          double           const value) {
 
+    /* Unlike Netpbm, the output format does not have an upper limit for
+       maxval.  Here we allow all values representable by unsigned int.
+    */
 
+    struct Icdat * const icdataP = &objectP->odata.icolData;
+    unsigned int const outValue =
+        ROUNDU( icdataP->icolmin +
+                ((double)icdataP->icolmax - icdataP->icolmin) * value);
 
-static SKL_OBJ *
-save_i_data(SKL_OBJ_TYP  const ctyp,
-            const char * const format) {
+    fprintf(ofP, icdataP->icformat, outValue);
+}
 
-/* Save universal data in Object */
 
-    SKL_OBJ * objP;
 
-    MALLOCVAR(objP);
-    if (objP) {
-        objP->otyp = ctyp;
-        strcpy(objP->odata.i_data.iformat, format);
-    }
-    return objP;
+static void
+writeFcol(FILE *           const ofP,
+          SkeletonObject * const objectP,
+          double           const value) {
+
+    struct Fcdat * const fcdataP = &objectP->odata.fcolData;
+    
+    fprintf(ofP, fcdataP->fcformat,
+            (double)
+            (fcdataP->fcolmin
+             + (fcdataP->fcolmax - fcdataP->fcolmin) * value));
 }
 
 
 
-static char const escape = '#';
+static void
+writeIdat(FILE *           const ofP,
+          SkeletonObject * const objectP,
+          unsigned int     const value) {
+
+    struct Idat * const idataP = &objectP->odata.iData;
+    
+    fprintf(ofP, idataP->iformat, value);
+}
 
 
 
-static SKL_OBJ_TYP
-interpretObjType(const char * const typstr) {
+static void
+writeText(FILE *            const ofP,
+          unsigned int      const nObj,
+          SkeletonObject ** const obj,
+          unsigned int      const width,
+          unsigned int      const height,
+          unsigned int      const x,
+          unsigned int      const y,
+          double            const red,
+          double            const green,
+          double            const blue) {
+    
+    unsigned int i;
 
-    SKL_OBJ_TYP otyp;
-
-    /* Check for integer colors */
-    if      (streq(typstr, "ired")  ) otyp = IRED;
-    else if (streq(typstr, "igreen")) otyp = IGREEN;
-    else if (streq(typstr, "iblue") ) otyp = IBLUE;
-    else if (streq(typstr, "ilum")  ) otyp = ILUM;
-    /* Check for real colors */
-    else if (streq(typstr, "fred")  ) otyp = FRED;
-    else if (streq(typstr, "fgreen")) otyp = FGREEN;
-    else if (streq(typstr, "fblue") ) otyp = FBLUE;
-    else if (streq(typstr, "flum")  ) otyp = FLUM;
-    /* Check for integer data */
-    else if (streq(typstr, "width") ) otyp = WIDTH;
-    else if (streq(typstr, "height")) otyp = HEIGHT;
-    else if (streq(typstr, "posx")  ) otyp = POSX;
-    else if (streq(typstr, "posy")  ) otyp = POSY;
-    else                              otyp = BDATA;
-
-    return otyp;
+    for (i = 0; i < nObj; ++i) {
+        switch (obj[i]->objType) {
+        case BDATA:
+            writeBndat(ofP, obj[i]);
+            break;
+        case IRED:
+            writeIcol(ofP, obj[i], red);
+            break;
+        case IGREEN:
+            writeIcol(ofP, obj[i], green);
+            break;
+        case IBLUE:
+            writeIcol(ofP, obj[i], blue);
+            break;
+        case ILUM:
+            writeIcol(ofP, obj[i],
+                      PPM_LUMINR*red + PPM_LUMING*green + PPM_LUMINB*blue);
+            break;
+        case FRED:
+            writeFcol(ofP, obj[i], red);
+            break;
+        case FGREEN:
+            writeFcol(ofP, obj[i], green);
+            break;
+        case FBLUE:
+            writeFcol(ofP, obj[i], blue);
+            break;
+        case FLUM:
+            writeFcol(ofP, obj[i],
+                      PPM_LUMINR*red + PPM_LUMING*green + PPM_LUMINB*blue);
+            break;
+        case WIDTH:
+            writeIdat(ofP, obj[i], width);
+            break;
+        case HEIGHT:
+            writeIdat(ofP, obj[i], height);
+            break;
+        case POSX:
+            writeIdat(ofP, obj[i], x);
+            break;
+        case POSY:
+            writeIdat(ofP, obj[i], y);
+            break;
+        }
+    }
 }
 
 
 
-static SKL_OBJ_CLASS
-objClass(SKL_OBJ_TYP const otyp) {
+static SkeletonObjectClass
+objClass(SkeletonObjectType const objType) {
 
-    switch (otyp) {
+    switch (objType) {
     case IRED:
     case IGREEN:
     case IBLUE:
@@ -283,298 +352,873 @@ objClass(SKL_OBJ_TYP const otyp) {
 }
 
 
+/*----------------------------------------------------------------------------
+  Format string validation
+
+  We validate format strings (such as "%f" "%03d") found in the skeleton files
+  for convenience, even though behavior is documented as undefined when the
+  user supplies a bogus format string.  Certain strings, most notably those
+  with "%n", are especially risky; they pose a security threat.
+
+  On systems with Glibc, we check with parse_printf_format().  On other
+  systems we conduct a cursory scan of the characters in the format string,
+  looking for characters that trigger non-numeric conversions, etc.
+
+  Documentation for parse_printf_format() is usually available in texinfo
+  format on GNU/Linux systems.  As of Dec. 2014 there is no official man page.
+  
+  Online documentation is available from:
+  https://
+  www.gnu.org/software/libc/manual/html_node/Parsing-a-Template-String.html
+-----------------------------------------------------------------------------*/
 
+#if HAVE_PARSE_PRINTF_FORMAT
 static void
-addImpostorReplacementSeq(char *         const line,
-                          unsigned int   const startCursor,
-                          const char *   const seqContents,
-                          unsigned int   const seqContentsLen,
-                          unsigned int * const newCursorP) {
+validateParsePrintfFlag(int                const printfConversion,
+                        SkeletonObjectType const ctyp,
+                        const char **      const errorP) {
 /*----------------------------------------------------------------------------
-   Add to line line[], at position 'startCursor', text that looks like a
-   replacement sequence but doesn't have the proper contents (the
-   stuff between the parentheses) to be one.  For example,
+  Assuming 'printfConversion' is the value reported by parse_printf_format()
+  as the type of argument a format string requires, 
+  return an explanation of how it is incompatible with 'ctyp' as
+  *errorP -- return null string if it is compatible.
+-----------------------------------------------------------------------------*/
+    /* We first check for "%n", then the type modifiers, and finally the
+       actual conversion type (char, int, float, double, string or pointer.)
+    */
+    switch (printfConversion & PA_FLAG_MASK) {
+    case PA_FLAG_PTR:  /* This means %n */
+        pm_asprintf(errorP, "Contains a %%n conversion specifier");
+        break;
+
+    case PA_FLAG_SHORT:
+    case PA_FLAG_LONG:
+    case PA_FLAG_LONG_LONG:
+        /* We omit PA_FLAG_LONG_DOUBLE because it is a synonym for
+           PA_FLAG_LONG_LONG: listing both causes compilation errors.
+        */
+        pm_asprintf(errorP, "Invalid type modifier");
+        break;
+
+    default:
+        switch (printfConversion & ~PA_FLAG_MASK) {
+        case PA_CHAR:
+            pm_message("Warning: char type conversion."); 
+        case PA_INT:
+            if(objClass(ctyp) == OBJTYP_ICOLOR ||
+               objClass(ctyp) == OBJTYP_INT )
+                *errorP = NULL;
+            else
+                pm_asprintf(errorP, "Conversion specifier requires a "
+                            "character or integer argument, but it is in "
+                            "a replacement sequence for a different type");
+            break;
+        case PA_DOUBLE:
+            if(objClass(ctyp) == OBJTYP_FCOLOR)
+                *errorP = NULL;
+            else
+                pm_asprintf(errorP, "Conversion specifier requires a "
+                            "double precision floating point argument, "
+                            "but it is in "
+                            "a replacement sequence for a different type");
+            break;
+        case PA_FLOAT:
+        case PA_STRING:    /* %s */
+        case PA_POINTER:   /* %p */
+        default:
+            pm_asprintf(errorP, "Conversion specifier requires an argument of "
+                        "a type that this program never provides for "
+                        "any replacement sequence");
+        }
+    }
+}
+#endif
 
-     "#(fread x)"
 
-   seqContents[] is the contents; 'seqContentsLen' its length.
 
-   Return as *newCursorP where the line[] cursor ends up after adding
-   the sequence.
+#if HAVE_PARSE_PRINTF_FORMAT
+static void
+validateFormatWithPpf(const char *       const format,
+                      SkeletonObjectType const ctyp,
+                      const char **      const errorP) {
+/*----------------------------------------------------------------------------
+  Validate format string 'format' for use with a skeleton of type 'ctyp',
+  using the system parse_printf_format() function.
+
+  Return as *errorP an explanation of how it is invalid, or a null string
+  if it is valid.
 -----------------------------------------------------------------------------*/
-    unsigned int cursor;
-    unsigned int i;
+    /* We request parse_printf_format() to report the details of the first
+       8 conversions.  8 because the maximum length of format is 16 means it
+       can have up to 8 conversions: "%d%d%d%d%d%d%d%d".
+
+       Actually this is more than necessary: we are concerned with only the
+       first conversion and whether there it is the only one.
+    */
 
-    cursor = startCursor;
+    int printfConversion[MAXFORMAT/2] = {0, 0, 0, 0, 0, 0, 0, 0};
 
-    line[cursor++] = escape;
-    line[cursor++] = '(';
+    size_t const n =
+        parse_printf_format(format, MAXFORMAT/2, printfConversion);
+
+    switch (n) {
+    case 0:
+        pm_asprintf(errorP, "No transformation found");
+        break;
+
+    case 1:
+        validateParsePrintfFlag(printfConversion[0], ctyp, errorP);
+        break;
+
+    default:
+        pm_asprintf(errorP, "Has %lu extra transformation%s ",
+                    (unsigned long)n-1, n-1 > 1 ? "s" : "");
+        break;
+    }
+}
+#endif
 
-    for (i = 0; i < seqContentsLen; ++i)
-        line[cursor++] = seqContents[i];
 
-    line[cursor++] = ')';
 
-    *newCursorP = cursor;
+static void
+validateFormatOne(char               const typeSpecifier,
+                  bool               const isLastInString,
+                  SkeletonObjectType const ctyp,
+                  bool *             const validatedP,
+                  const char **      const errorP) {
+
+    switch (typeSpecifier) {
+        /* Valid character encountered.  Skip. */
+        /* ' ' (space) is listed here, but should never occur for
+           we use sscanf() to parse the fields.
+        */
+    case ' ': case '-': case '+': case '\'': case '#': case '.':
+    case '0': case '1': case '2': case '3': case '4': case '5':
+    case '6': case '7': case '8': case '9':
+        break;
+        
+    case 'c': case 'C':
+        pm_message("Warning: char type conversion: %%%c.", typeSpecifier);
+    case 'i': case 'd': case 'u': case 'o': case 'x': case 'X':
+        if (!isLastInString)
+            pm_asprintf(errorP, "Extra characters at end");
+        else if(objClass(ctyp) != OBJTYP_ICOLOR &&
+                objClass(ctyp) != OBJTYP_INT )
+            pm_asprintf(errorP, "Conversion type mismatch");
+        else
+            *validatedP = true;
+        break;
+
+    case 'f': case 'F': case 'g': case 'G': case 'a': case 'A':
+        if (!isLastInString)
+            pm_asprintf(errorP, "Extra characters at end");
+        else if(objClass(ctyp) != OBJTYP_FCOLOR)
+            pm_asprintf(errorP, "Conversion type mismatch");
+        else
+            *validatedP = true;
+        break;
+
+    case '\0':
+        pm_asprintf(errorP, "No conversion specified");
+        break;
+    case '%':
+        pm_asprintf(errorP, "No more than one %% is allowed");
+        break;
+    case '$':
+    case '*':
+        pm_asprintf(errorP, "%c is not allowed", typeSpecifier);
+        break;
+    case 'h': case 'l': case 'L': case 'q': case 'j': case 'Z': case 't':
+        pm_asprintf(errorP, "Modifier %c is not allowed in format",
+                    typeSpecifier);
+        break;
+    case 's': case 'S': case 'm': case 'p': case 'n':
+        pm_asprintf(errorP, "Invalid conversion type");
+        break;
+    default:
+        pm_asprintf(errorP, "Abnormal character");
+        break;
+    }
 }
 
 
 
-static int
-read_skeleton(const char *   const filename,
-              unsigned int   const maxskl,
-              unsigned int * const nsklP,
-              SKL_OBJ **     const skl) {
+static void
+validateFormatGen(const char *       const format,
+                  SkeletonObjectType const ctyp,
+                  const char **      const errorP)  {
 /*----------------------------------------------------------------------------
-  Read skeleton file
+  Validate format string 'format' for use with a skeleton of type 'ctyp',
+  without using the system parse_printf_format() function.
+
+  The string must begin with "%" and end with the translation type character
+  ("%d", "%x", "%f", etc.)
+
+  We check only for invalid characters.  Invalid constructs, such as
+  "%00.00.00d" will pass this test.
+
+  Return as *errorP an explanation of how it is invalid, or a null string
+  if it is valid.
 -----------------------------------------------------------------------------*/
-    FILE * sklfile;
-    unsigned int slen;
-    unsigned int objlen;
-    int chr;
-    SKL_OBJ_TYP otyp;
-    char line[MAX_LINE_BUF+MAX_OBJ_BUF+16];
-    char objstr[MAX_OBJ_BUF],typstr[MAX_OBJ_BUF];
-    unsigned int nskl;
-
-#define SAVE_BIN(slen,line) \
-    { if (slen > 0 && (skl[nskl] = save_bin_data(slen,line)) != NULL) ++nskl; \
-      slen = 0; }
-
-    sklfile = pm_openr(filename);
-
-    /* Parse skeleton file */
-    nskl = 0;  /* initial value */
-
-    slen = 0;
-    while ((chr = getc (sklfile)) != EOF) {  /* Up to end of skeleton file */
-        if (nskl >= maxskl)
-            return -1;
-
-        if (slen+1 >= MAX_LINE_BUF) {
-            /* Buffer finished.  Save as binary object */
-            SAVE_BIN(slen,line);
+    if (format[0] != '%')
+        pm_asprintf(errorP, "Does not start with %%");
+    else {
+        unsigned int i;
+        bool validated;
+
+        for (i = 1, validated = false, *errorP = NULL;
+             !validated && !*errorP;
+             ++i) {
+
+            validateFormatOne(format[i], format[i+1] == '\0', ctyp,
+                              &validated, errorP);
         }
+    }
+}
 
-        if (chr != escape) {
-            /* Not a replacement sequence; just a literal character */
-            line[slen++] = chr;
-            continue;
-        }
 
-        chr = getc(sklfile);
-        if (chr == EOF) {
-            /* Not a valid replacement sequence */
-            line[slen++] = escape;
-            break;
-        }
-        if (chr != '(') {
-            /* Not a valid replacement sequence */
-            line[slen++] = escape;
-            line[slen++] = chr;
-            continue;
+
+static void
+validateFormat(const char *       const format,
+               SkeletonObjectType const ctyp) {
+
+    const char * error;
+
+    if (strlen(format) > MAXFORMAT)
+        pm_asprintf(&error, "Too long");
+    else {
+#if HAVE_PARSE_PRINTF_FORMAT
+        if (true)
+            validateFormatWithPpf(format, ctyp, &error);
+        else  /* Silence compiler warning about unused function */
+            validateFormatGen(format, ctyp, &error);
+#else
+        validateFormatGen(format, ctyp, &error);
+#endif
+    }
+
+    if (error)
+        pm_error("Invalid format string '%s'.  %s", format, error);
+}              
+               
+
+
+static SkeletonObject *
+newBinDataObj(unsigned int const nDat, 
+              const char * const bdat) {
+/*----------------------------------------------------------------------------
+  Create a binary data object.
+-----------------------------------------------------------------------------*/
+    SkeletonObject * objectP;
+
+    objectP = malloc(sizeof(*objectP) + nDat);
+
+    if (!objectP)
+        pm_error("Failed to allocate memory for binary data object "
+                 "with %u bytes", nDat);
+
+    objectP->objType = BDATA;
+    objectP->odata.binData.ndat = nDat;
+    objectP->odata.binData.bdat = ((char *)objectP) + sizeof(SkeletonObject);
+    memcpy(objectP->odata.binData.bdat, bdat, nDat);
+
+    return objectP;
+}
+
+
+
+static SkeletonObject *
+newIcolDataObj(SkeletonObjectType const ctyp,
+               const char *       const format,
+               unsigned int       const icolmin,
+               unsigned int       const icolmax) {
+/*----------------------------------------------------------------------------
+  Create integer color data object.
+-----------------------------------------------------------------------------*/
+    SkeletonObject * objectP;
+
+    MALLOCVAR(objectP);
+
+    if (!objectP)
+        pm_error("Failed to allocate memory for an integer color data "
+                 "object");
+
+    objectP->objType = ctyp;
+    validateFormat(format, ctyp);
+    strcpy(objectP->odata.icolData.icformat, format);
+    objectP->odata.icolData.icolmin = icolmin;
+    objectP->odata.icolData.icolmax = icolmax;
+
+    return objectP;
+}
+
+
+
+static SkeletonObject *
+newFcolDataObj(SkeletonObjectType  const ctyp,
+               const char *        const format,
+               double              const fcolmin,
+               double              const fcolmax) {
+/*----------------------------------------------------------------------------
+  Create float color data object.
+-----------------------------------------------------------------------------*/
+    SkeletonObject * objectP;
+
+    MALLOCVAR(objectP);
+
+    if (!objectP)
+        pm_error("Failed to allocate memory for a float color data object");
+
+    objectP->objType = ctyp;
+    validateFormat(format, ctyp);
+    strcpy(objectP->odata.fcolData.fcformat, format);
+    objectP->odata.fcolData.fcolmin = fcolmin;
+    objectP->odata.fcolData.fcolmax = fcolmax;
+
+    return objectP;
+}
+
+
+
+static SkeletonObject *
+newIdataObj(SkeletonObjectType const ctyp,
+            const char *       const format) {
+/*----------------------------------------------------------------------------
+  Create universal data object.
+-----------------------------------------------------------------------------*/
+    SkeletonObject * objectP;
+
+    MALLOCVAR(objectP);
+
+    if (!objectP)
+        pm_error("Failed to allocate memory for a universal data object");
+
+    objectP->objType = ctyp;
+    validateFormat(format, ctyp);
+    strcpy(objectP->odata.iData.iformat, format);
+
+    return objectP;
+}
+
+
+
+static char const escape = '#';
+
+
+
+static SkeletonObjectType
+interpretObjType(const char * const typstr) {
+
+    SkeletonObjectType objType;
+
+    /* handle integer colors */
+    if      (streq(typstr, "ired")  ) objType = IRED;
+    else if (streq(typstr, "igreen")) objType = IGREEN;
+    else if (streq(typstr, "iblue") ) objType = IBLUE;
+    else if (streq(typstr, "ilum")  ) objType = ILUM;
+    /* handle real colors */
+    else if (streq(typstr, "fred")  ) objType = FRED;
+    else if (streq(typstr, "fgreen")) objType = FGREEN;
+    else if (streq(typstr, "fblue") ) objType = FBLUE;
+    else if (streq(typstr, "flum")  ) objType = FLUM;
+    /* handle integer data */
+    else if (streq(typstr, "width") ) objType = WIDTH;
+    else if (streq(typstr, "height")) objType = HEIGHT;
+    else if (streq(typstr, "posx")  ) objType = POSX;
+    else if (streq(typstr, "posy")  ) objType = POSY;
+    else                              objType = BDATA;
+
+    return objType;
+}
+
+
+
+static SkeletonObject *
+newIcSkelFromReplString(const char *       const objstr,
+                        SkeletonObjectType const objType) {
+
+    SkeletonObject * retval;
+    unsigned int icolmin, icolmax;
+    char formstr[MAX_OBJ_BUF];
+    unsigned int nOdata;
+
+    nOdata = sscanf(objstr, "%*s%s%u%u", formstr, &icolmin, &icolmax);
+
+    if (nOdata == 3)
+        retval = newIcolDataObj(objType, formstr, icolmin, icolmax);
+    else if (nOdata == EOF) {
+        /* No arguments specified.  Use defaults */
+        retval = newIcolDataObj(objType, "%u", 0, 255);
+    } else
+        retval = NULL;
+
+    return retval;
+}
+
+
+
+static SkeletonObject *
+newFcSkelFromReplString(const char *       const objstr,
+                        SkeletonObjectType const objType) {
+
+    SkeletonObject * retval;
+    double fcolmin, fcolmax;
+    char formstr[MAX_OBJ_BUF];
+    unsigned int nOdata;
+
+    nOdata = sscanf(objstr, "%*s%s%lf%lf", formstr,
+                    &fcolmin, &fcolmax);
+
+    if (nOdata == 3)
+        retval = newFcolDataObj(objType, formstr, fcolmin, fcolmax);
+    else if (nOdata == EOF) {
+        /* No arguments specified.  Use defaults */
+        retval = newFcolDataObj(objType, "%f", 0.0, 1.0);
+    } else
+        retval = NULL;
+
+    return retval;
+} 
+
+
+
+static SkeletonObject *
+newISkelFromReplString(const char *       const objstr,
+                       SkeletonObjectType const objType) {
+
+    SkeletonObject * retval;
+    char formstr[MAX_OBJ_BUF];
+    unsigned int const nOdata = sscanf(objstr, "%*s%s", formstr);
+    
+    if (nOdata == 1)
+        retval = newIdataObj(objType, formstr);
+    else if (nOdata == EOF) {
+        /* No arguments specified.  Use defaults */
+        retval = newIdataObj(objType, "%u");
+    } else
+        retval = NULL;
+
+    return retval;
+} 
+
+
+
+static SkeletonObject *
+newSkeletonFromReplString(const char * const objstr) {
+/*----------------------------------------------------------------------------
+  Create a skeleton from the replacement string 'objstr' (the stuff
+  between the parentheses in #(...) ).
+
+  Return NULL if it isn't a valid replacement string.
+-----------------------------------------------------------------------------*/
+    /* We use sscanf() to parse the contents of objstr, giving it a format
+       template with the largest number of fields possible plus one extra to
+       pick up and check for the existence of invalid trailing characters.  We
+       read and discard fields beyond the first, if any.  The appropriate
+       new**SkelFromReplString() function determines their contents with a
+       separate call to sscanf().
+    */
+
+    SkeletonObject * retval;
+    char typstr[MAX_OBJ_BUF];
+    SkeletonObjectType objType;
+    int conversionCt;
+    char s1[MAX_OBJ_BUF];    /* Dry read. */
+    char s2[MAX_OBJ_BUF];    /* Extra tailing characters. */
+    float f1, f2;            /* Dry read. */ 
+
+    typstr[0] = '\0';  /* initial value */
+
+    conversionCt = sscanf(objstr, "%s%s%f%f%s", typstr, s1, &f1, &f2, s2);
+    switch (conversionCt) {
+    case 1: case 2: case 4:
+        objType = interpretObjType(typstr);
+      break;
+    default:
+        objType = BDATA;
+    }
+
+    switch (objClass(objType)) {
+    case OBJTYP_ICOLOR:
+        retval = newIcSkelFromReplString(objstr, objType);
+        break;
+    case OBJTYP_FCOLOR:
+        retval = newFcSkelFromReplString(objstr, objType);
+        break;
+    case OBJTYP_INT:
+        retval = newISkelFromReplString(objstr, objType);
+        break;
+    case OBJTYP_BDATA:
+        retval = NULL;
+    }
+    return retval;
+}
+
+
+
+static void
+readThroughCloseParen(FILE * const ifP,
+                      char * const objstr,
+                      size_t const objstrSize,
+                      bool * const unclosedP) {
+/*----------------------------------------------------------------------------
+   Read *ifP up through close parenthesis ( ')' ) into 'objstr', which
+   is of size 'objstrSize'.  Make it a NUL-terminated string.
+
+   Return *unclosedP true iff we run out of file or run out of objstr
+   before we see a close parenthesis.  In this case, return the rest of
+   the file, or as much as fits, in 'objstr', not NUL-terminated.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    bool eof;
+    bool gotEscSeq;
+
+    for (i= 0, eof = false, gotEscSeq = false;
+         i < objstrSize - 1 && !gotEscSeq && !eof;
+         ++i) {
+
+        int rc;
+
+        rc = getc(ifP);
+        if (rc == EOF)
+            eof = true;
+        else {
+            char const chr = rc;
+            if (chr == ')') {
+                gotEscSeq = true;
+                objstr[i] = '\0';
+	        } else
+                objstr[i] = chr;
         }
-        /* Read replacement string up through ')'.  Put contents of
-           parentheses in objstr[].
+    }
+    *unclosedP = !gotEscSeq;
+}
+
+
+
+typedef struct {
+    unsigned int      capacity;
+    SkeletonObject ** skeletonPList;
+    unsigned int      nSkeleton;
+} SkeletonBuffer;
+
+
+
+static void
+SkeletonBuffer_init(SkeletonBuffer *  const bufferP,
+                    unsigned int      const capacity,
+                    SkeletonObject ** const skeletonPList) {
+
+    bufferP->capacity      = capacity;
+    bufferP->skeletonPList = skeletonPList;
+    bufferP->nSkeleton     = 0;
+}
+
+
+
+static void
+SkeletonBuffer_add(SkeletonBuffer * const bufferP,
+                   SkeletonObject * const skeletonP) {
+
+    if (bufferP->nSkeleton >= bufferP->capacity)
+        pm_error("Too many skeletons.  Max = %u", bufferP->capacity);
+
+    bufferP->skeletonPList[bufferP->nSkeleton++] = skeletonP;
+}                   
+
+
+
+typedef struct {
+
+    char data[MAX_LINE_BUF + MAX_OBJ_BUF + 16];
+
+    unsigned int length;
+
+    SkeletonBuffer * skeletonBufferP;
+        /* The buffer to which we flush.  Flushing means turning all the
+           characters currently in our buffer into a binary skeleton object
+           here.
         */
-        for (objlen = 0; objlen < sizeof(objstr)-1; ++objlen) {
-            chr = getc(sklfile);
-            if (chr == EOF) break;
-            if (chr == ')') break;
-            objstr[objlen] = chr;
-        }
-        objstr[objlen] = '\0';
-
-        if (chr != ')') {
-            /* Not valid replacement sequence */
-            unsigned int i;
-            line[slen++] = escape;
-            line[slen++] = chr;
-            for (i = 0; i < objlen; ++i)
-                line[slen++] = objstr[i];
-            if (chr == EOF)
-                break;
-            continue;
-        }
 
-        typstr[0] = '\0';           /* Get typ of data */
-        sscanf(objstr, "%s", typstr);
-
-        otyp = interpretObjType(typstr);
-
-        switch (objClass(otyp)) {
-        case OBJTYP_ICOLOR: {
-            int icolmin, icolmax;
-            char formstr[MAX_OBJ_BUF];
-            int n_odata;
-
-            n_odata = sscanf(objstr, "%*s%s%d%d", formstr, &icolmin, &icolmax);
-
-            if (n_odata == 3) {
-                SAVE_BIN(slen, line);
-                skl[nskl] = save_icol_data(otyp, formstr, icolmin, icolmax);
-                if (skl[nskl] != NULL)
-                    ++nskl;
-            } else if (n_odata == EOF) {
-                /* No arguments specified.  Use defaults */
-                SAVE_BIN(slen, line);
-                skl[nskl] = save_icol_data(otyp, "%d", 0, 255);
-                if (skl[nskl] != NULL)
-                    ++nskl;
-            } else
-                addImpostorReplacementSeq(line, slen, objstr, objlen, &slen);
-        } break;
-        case OBJTYP_FCOLOR: {
-            double fcolmin, fcolmax;
-            char formstr[MAX_OBJ_BUF];
-            int n_odata;
-
-            n_odata = sscanf(objstr, "%*s%s%lf%lf", formstr,
-                             &fcolmin, &fcolmax);
-
-            if (n_odata == 3) {
-                SAVE_BIN(slen, line);
-                skl[nskl] = save_fcol_data(otyp, formstr, fcolmin, fcolmax);
-                if (skl[nskl] != NULL)
-                    ++nskl;
-            } else if (n_odata == EOF) {
-                /* No arguments specified.  Use defaults */
-                SAVE_BIN(slen, line);
-                skl[nskl] = save_fcol_data(otyp, "%f", 0.0, 1.0);
-                if (skl[nskl] != NULL)
-                    ++nskl;
-            } else
-                addImpostorReplacementSeq(line, slen, objstr, objlen, &slen);
-        } break;
-
-        case OBJTYP_INT: {
-            char formstr[MAX_OBJ_BUF];
-            int const n_odata = sscanf(objstr, "%*s%s", formstr);
-
-            if (n_odata == 1) {
-                SAVE_BIN(slen, line);
-                skl[nskl] = save_i_data(otyp, formstr);
-                if (skl[nskl] != NULL)
-                    ++nskl;
-            } else if (n_odata == EOF) {
-                /* No arguments specified.  Use defaults */
-                SAVE_BIN(slen, line);
-                skl[nskl] = save_i_data(otyp, "%d");
-                if (skl[nskl] != NULL)
-                    ++nskl;
-            } else
-                addImpostorReplacementSeq(line, slen, objstr, objlen, &slen);
-        } break;
-        case OBJTYP_BDATA:
-            addImpostorReplacementSeq(line, slen, objstr, objlen, &slen);
+} Buffer;
+
+
+
+static void
+Buffer_init(Buffer *         const bufferP,
+            SkeletonBuffer * const skeletonBufferP) {
+
+    bufferP->skeletonBufferP = skeletonBufferP;
+    bufferP->length = 0;
+}
+
+
+
+static void
+Buffer_flush(Buffer * const bufferP) {
+/*----------------------------------------------------------------------------
+   Flush the buffer out to a binary skeleton object.
+-----------------------------------------------------------------------------*/
+    SkeletonBuffer_add(bufferP->skeletonBufferP,
+                       newBinDataObj(bufferP->length, bufferP->data));
+
+    bufferP->length = 0;
+}
+
+
+
+static void
+Buffer_add(Buffer * const bufferP,
+           char     const newChar) {
+
+    if (bufferP->length >= MAX_LINE_BUF)
+        Buffer_flush(bufferP);
+
+    assert(bufferP->length < MAX_LINE_BUF);
+
+    bufferP->data[bufferP->length++] = newChar;
+}
+
+
+
+static void
+Buffer_dropFinalNewline(Buffer * const bufferP) {
+/*----------------------------------------------------------------------------
+   If the last thing in the buffer is a newline, remove it.
+-----------------------------------------------------------------------------*/
+    if (bufferP->length >= 1 && bufferP->data[bufferP->length-1] == '\n') {
+            /* Drop finishing newline character */
+            --bufferP->length;
+    }
+}
+
+
+
+static void
+addImpostorReplacementSeq(Buffer *     const bufferP,
+                          const char * const seqContents) {
+/*----------------------------------------------------------------------------
+  Add to buffer *bufferP something that looks like a replacement sequence but
+  doesn't have the proper contents (the stuff between the parentheses) to be
+  one.  For example,
+
+  "#(fread x)"
+
+  seqContents[] is the contents, NUL-terminated.
+-----------------------------------------------------------------------------*/
+    const char * p;
+
+    Buffer_add(bufferP, escape);
+    Buffer_add(bufferP, '(');
+
+    for (p = &seqContents[0]; *p; ++p)
+        Buffer_add(bufferP, *p);
+
+    Buffer_add(bufferP, ')');
+}
+
+
+
+static void
+readSkeletonFile(const char *      const filename,
+                 unsigned int      const maxskl,
+                 const char **     const errorP,
+                 unsigned int *    const nSkeletonP,
+                 SkeletonObject ** const skeletonPList) {
+/*----------------------------------------------------------------------------
+-----------------------------------------------------------------------------*/
+    FILE * sklfileP;
+    SkeletonBuffer skeletonBuffer;
+        /* A buffer for accumulating skeleton objects */
+    Buffer buffer;
+        /* A buffer for accumulating binary (literal; unsubstituted) data, on
+           its way to becoming a binary skeleton object. 
+        */
+    bool eof;
+    const char * error;
+
+    SkeletonBuffer_init(&skeletonBuffer, maxskl, skeletonPList);
+
+    Buffer_init(&buffer, &skeletonBuffer);
+
+    sklfileP = pm_openr(filename);
+
+    for (eof = false, error = NULL; !eof && !error; ) {
+
+        int rc;
+
+        rc = getc(sklfileP);
+
+        if (rc == EOF)
+            eof = true;
+        else {
+            char const chr = rc;
+
+            if (chr != escape) {
+                /* Not a replacement sequence; just a literal character */
+                Buffer_add(&buffer, chr);
+            } else {
+                int rc;
+                rc = getc(sklfileP);
+                if (rc == EOF) {
+                    /* Not a replacement sequence, just an escape caharacter
+                       at the end of the file.
+                    */
+                    Buffer_add(&buffer, escape);
+                    eof = true;
+                } else {
+                    char const chr = rc;
+
+                    if (chr != '(') {
+                        /* Not a replacement sequence, just a lone escape
+                           character
+                        */
+                        Buffer_add(&buffer, escape);
+                        Buffer_add(&buffer, chr);
+                    } else {
+                        char objstr[MAX_OBJ_BUF];
+                        bool unclosed;
+                        readThroughCloseParen(sklfileP,
+                                              objstr, sizeof(objstr),
+                                              &unclosed);
+                        if (unclosed)
+                            pm_asprintf(&error, "Unclosed parentheses "
+                                        "in #() escape sequence");
+                        else {
+                            SkeletonObject * const skeletonP =
+                                newSkeletonFromReplString(objstr);
+
+                            if (skeletonP) {
+                                Buffer_flush(&buffer);
+                                SkeletonBuffer_add(&skeletonBuffer, skeletonP);
+                            } else
+                                addImpostorReplacementSeq(&buffer, objstr);
+                        }
+                    }
+                }
+            }
         }
-    } /* EOF of skeleton file */
+    }
 
-    if (slen >= 1 && line[slen-1] == '\n')
-        /* Drop finishing newline character */
-        --slen;
+    if (!error) {
+        Buffer_dropFinalNewline(&buffer);
+        Buffer_flush(&buffer);
+    }
+    *errorP = error;
+    *nSkeletonP = skeletonBuffer.nSkeleton;
 
-    SAVE_BIN(slen, line);  /* Save whatever is left */
+    fclose(sklfileP);
+}
 
-    *nsklP = nskl;
 
-    fclose(sklfile);
-    return 0;
+
+static void
+convertIt(FILE *            const ifP,
+          FILE *            const ofP,
+          SkeletonObject ** const bodySkeletonPList,
+          unsigned int      const bodyNskl,
+          SkeletonObject ** const headSkeletonPList, 
+          unsigned int      const headNskl,
+          SkeletonObject ** const tailSkeletonPList,
+          unsigned int      const tailNskl) {
+
+    pixel * pixelrow;
+    pixval maxval;
+    double dmaxval;
+    int rows, cols;
+    int format;
+    unsigned int row;
+
+    ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
+
+    pixelrow = ppm_allocrow(cols);
+
+    dmaxval = (double)maxval;
+
+    /* Write header */
+    writeText(ofP, headNskl, headSkeletonPList, 
+              cols, rows , 0, 0, 0.0, 0.0, 0.0);
+
+    /* Write raster */
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        ppm_readppmrow(ifP, pixelrow, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col) {
+            pixel const thisPixel = pixelrow[col];
+
+            writeText(ofP, bodyNskl, bodySkeletonPList,
+                      cols, rows, col, row,
+                      PPM_GETR(thisPixel)/dmaxval,
+                      PPM_GETG(thisPixel)/dmaxval,
+                      PPM_GETB(thisPixel)/dmaxval);
+        }
+    }
+
+    /* Write trailer */
+    writeText(ofP, tailNskl, tailSkeletonPList, 
+              cols, rows, 0, 0, 0.0, 0.0, 0.0);
 }
 
 
 
-int main( argc, argv )
-int argc;
-char* argv[];
-
-{register int col;
- register pixel* xP;
- pixel* pixelrow;
- pixval maxval,red,green,blue;
- double dmaxval;
- int argn, rows, cols, format, row;
- unsigned int head_nskl,body_nskl,tail_nskl;
- SKL_OBJ *head_skl[MAX_SKL_HEAD_OBJ];
- SKL_OBJ *body_skl[MAX_SKL_BODY_OBJ];
- SKL_OBJ *tail_skl[MAX_SKL_TAIL_OBJ];
- FILE *ifp;
- const char *usage = "bodyskl [ -hd headskl ] [ -tl tailskl ] [pnmfile]";
-
- ppm_init( &argc, argv );
-
- argn = 1;
- if (argn == argc)
-   pm_usage( usage );
-                          /* Read body skeleton file */
- if (read_skeleton (argv[argn],sizeof (body_skl)/sizeof (SKL_OBJ *),
-                    &body_nskl,body_skl) < 0)
-   pm_usage ( usage );
- ++argn;
-
- head_nskl = tail_nskl = 0;
-
- while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0')
- {
-   if ( pm_keymatch ( argv[argn], "-hd", 1) && argn+1 < argc )
-   {
-     argn++;           /* Read header skeleton */
-     if (read_skeleton (argv[argn],sizeof (head_skl)/sizeof (SKL_OBJ *),
-                        &head_nskl,head_skl) < 0)
-       pm_usage ( usage );
-   }
-   else if ( pm_keymatch ( argv[argn], "-tl", 1) && argn+1 < argc )
-   {
-     argn++;           /* Read tail skeleton */
-     if (read_skeleton (argv[argn],sizeof (tail_skl)/sizeof (SKL_OBJ *),
-                        &tail_nskl,tail_skl) < 0)
-       pm_usage ( usage );
-   }
-   else
-   {
-     pm_usage ( usage );
-   }
-   argn++;
- }
-
- if ( argn != argc )
- {
-   ifp = pm_openr( argv[argn] );
-   ++argn;
- }
- else 
- {
-   ifp = stdin;
- }
-
- if ( argn != argc )
-   pm_usage( usage );
-
- ppm_readppminit( ifp, &cols, &rows, &maxval, &format );
- pixelrow = ppm_allocrow( cols );
- dmaxval = (double)maxval;
-
- if (head_nskl > 0)    /* Write header */
-   write_txt (stdout,head_nskl,head_skl,cols,rows,0,0,0.0,0.0,0.0);
-
- for ( row = 0; row < rows; ++row )
- {
-   ppm_readppmrow( ifp, pixelrow, cols, maxval, format );
-
-   for ( col = 0, xP = pixelrow; col < cols; ++col, ++xP )
-   {
-     red = PPM_GETR( *xP );
-     green = PPM_GETG( *xP );
-     blue = PPM_GETB( *xP );
-     write_txt (stdout,body_nskl,body_skl,cols,rows,col,row,
-                red/dmaxval,green/dmaxval,blue/dmaxval);
-   }
- }
-
- if (tail_nskl > 0)    /* Write trailer */
-   write_txt (stdout,tail_nskl,tail_skl,cols,rows,0,0,0.0,0.0,0.0);
-
- pm_close( ifp );
-
- exit( 0 );
+int
+main(int           argc,
+     const char ** argv) {
+    
+    struct CmdlineInfo cmdline;
+
+    unsigned int headNskl, bodyNskl, tailNskl;
+    SkeletonObject * headSkeletonPList[MAX_SKL_HEAD_OBJ];
+    SkeletonObject * bodySkeletonPList[MAX_SKL_BODY_OBJ];
+    SkeletonObject * tailSkeletonPList[MAX_SKL_TAIL_OBJ];
+    FILE * ifP;
+    const char * error;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    readSkeletonFile(cmdline.bodySklFileName, ARRAY_SIZE(bodySkeletonPList),
+                     &error, &bodyNskl, bodySkeletonPList);
+    if (error)
+        pm_error("Invalid body skeleton file '%s'.  %s",
+                 cmdline.bodySklFileName, error);
+
+    if (cmdline.hd) {
+        readSkeletonFile(cmdline.hd, ARRAY_SIZE(headSkeletonPList),
+                         &error, &headNskl, headSkeletonPList);
+        if (error)
+            pm_error("Invalid head skeleton file '%s'.  %s",
+                     cmdline.hd, error);
+    } else
+        headNskl = 0;
+
+    if (cmdline.tl) {
+        readSkeletonFile(cmdline.tl, ARRAY_SIZE(tailSkeletonPList),
+                         &error, &tailNskl, tailSkeletonPList);
+        if (error)
+            pm_error("Invalid tail skeleton file '%s'.  %s",
+                     cmdline.tl, error);
+    } else
+        tailNskl = 0;
+
+    if (cmdline.debug)
+        dumpAllSkeleton(bodySkeletonPList, bodyNskl,
+                        headSkeletonPList, headNskl,
+                        tailSkeletonPList, tailNskl);
+
+    convertIt(ifP, stdout,
+              bodySkeletonPList, bodyNskl,
+              headSkeletonPList, headNskl,
+              tailSkeletonPList, tailNskl);
+
+    pm_close(ifP);
+
+    return 0;
 }
+
+
diff --git a/converter/ppm/ppmtoascii.c b/converter/ppm/ppmtoascii.c
new file mode 100644
index 00000000..6b0b04d7
--- /dev/null
+++ b/converter/ppm/ppmtoascii.c
@@ -0,0 +1,236 @@
+/*=============================================================================
+                                  ppmtoascii
+===============================================================================
+
+  Convert a PPM image to ASCII with ANSI color graphics terminal controls
+
+  Based on pbmtoascii.
+  Copyright (C) 1988, 1992 by Jef Poskanzer.
+  Copyright (C) 2010 by Frank Ch. Eigler.
+
+  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.
+=============================================================================*/
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "ppm.h"
+#include "pm_gamma.h"
+
+/* We use the same algorithm for converting many pixels into a character
+   as Pbmtoascii.  See pbmtoascii.c for a full explanation.
+*/
+
+
+#define SQQ '\''
+#define BSQ '\\'
+
+/* Bit-map for 1x2 mode:
+**     1
+**     2
+*/
+static char const carr1x2[4] = {
+/*   0    1    2    3   */
+    ' ', '"', 'o', 'M' };
+
+/* Bit-map for 2x4 mode (hex):
+**     1  2
+**     4  8
+**     10 20
+**     40 80
+** The idea here is first to preserve geometry, then to show density.
+*/
+#define D08 'M'
+#define D07 'H'
+#define D06 '&'
+#define D05 '$'
+#define D04 '?'
+static char const carr2x4[256] = {
+/*0  1    2   3    4   5    6   7    8   9    A   B    C   D    E   F  */
+' ',SQQ, '`','"', '-',SQQ, SQQ,SQQ, '-','`', '`','`', '-','^','^','"',/*00-0F*/
+'.',':', ':',':', '|','|', '/',D04, '/','>', '/','>', '~','+','/','*',/*10-1F*/
+'.',':', ':',':', BSQ,BSQ, '<','<', '|',BSQ, '|',D04, '~',BSQ,'+','*',/*20-2F*/
+'-',':', ':',':', '~',D04, '<','<', '~','>', D04,'>', '=','b','d','#',/*30-3F*/
+'.',':', ':',':', ':','!', '/',D04, ':',':', '/',D04, ':',D04,D04,'P',/*40-4F*/
+',','i', '/',D04, '|','|', '|','T', '/',D04, '/','7', 'r','}','/','P',/*50-5F*/
+',',':', ';',D04, '>',D04, 'S','S', '/',')', '|','7', '>',D05,D05,D06,/*60-6F*/
+'v',D04, D04,D05, '+','}', D05,'F', '/',D05, '/',D06, 'p','D',D06,D07,/*70-7F*/
+'.',':', ':',':', ':',BSQ, ':',D04, ':',BSQ, '!',D04, ':',D04,D04,D05,/*80-8F*/
+BSQ,BSQ, ':',D04, BSQ,'|', '(',D05, '<','%', D04,'Z', '<',D05,D05,D06,/*90-9F*/
+',',BSQ, 'i',D04, BSQ,BSQ, D04,BSQ, '|','|', '|','T', D04,BSQ,'4','9',/*A0-AF*/
+'v',D04, D04,D05, BSQ,BSQ, D05,D06, '+',D05, '{',D06, 'q',D06,D06,D07,/*B0-BF*/
+'_',':', ':',D04, ':',D04, D04,D05, ':',D04, D04,D05, ':',D05,D05,D06,/*C0-CF*/
+BSQ,D04, D04,D05, D04,'L', D05,'[', '<','Z', '/','Z', 'c','k',D06,'R',/*D0-DF*/
+',',D04, D04,D05, '>',BSQ, 'S','S', D04,D05, 'J',']', '>',D06,'1','9',/*E0-EF*/
+'o','b', 'd',D06, 'b','b', D06,'6', 'd',D06, 'd',D07, '#',D07,D07,D08 /*F0-FF*/
+};
+
+
+
+static const char* rgb2x2x2fg[2][2][2] = {
+  {{"\x1b[30m",   /* 000 black */
+    "\x1b[34m"},  /* 001 blue */
+   {"\x1b[32m",   /* 010 green */
+    "\x1b[36m"}}, /* 011 cyan */
+  {{"\x1b[31m",   /* 100 red */
+    "\x1b[35m"},  /* 101 magenta */
+   {"\x1b[33m",   /* 110 yellow */
+    "\x1b[37m"}}, /* 111 white */
+};
+
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;  /* Name of input file */
+    unsigned int cellWidth;
+    unsigned int cellHeight;
+    const char * carr;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char **argv,
+                 struct cmdlineInfo * const cmdlineP) {
+
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+    unsigned int dim1x2Spec, dim2x4Spec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "1x2", OPT_FLAG, NULL, &dim1x2Spec, 0);
+    OPTENT3(0, "2x4", OPT_FLAG, NULL, &dim2x4Spec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = false;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = false;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (dim1x2Spec && dim2x4Spec)
+        pm_error("You cannot specify both -1x2 and -2x4");
+    else if (dim2x4Spec) {
+        cmdlineP->cellWidth  = 2;
+        cmdlineP->cellHeight = 4;
+        cmdlineP->carr       = carr2x4;
+    } else {
+        cmdlineP->cellWidth  = 1;
+        cmdlineP->cellHeight = 2;
+        cmdlineP->carr       = carr1x2;
+    }
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("Too many arguments: %u.  The only possible argument "
+                     "is the input file name", argc-1);
+    }
+}
+
+
+
+static void
+ppmtoascii(pixel * const *    const pixels,
+           unsigned int       const cols,
+           unsigned int       const rows,
+           pixval             const maxval,
+           struct cmdlineInfo const cmdline,
+           FILE *             const ofP) {
+
+    unsigned int const cellHeight = cmdline.cellHeight;
+    unsigned int const cellWidth  = cmdline.cellWidth;
+    const char * const carr       = cmdline.carr;
+    unsigned int row;
+
+    fprintf(ofP, "\x1b[0m"); /* Clear initial ansi attributes */
+    /* TODO: set background to black */
+
+    for (row = 0; row < rows; row += cellHeight) {
+        unsigned int col;
+        for (col = 0; col < cols; col += cellWidth)	{
+            unsigned int const sumthresh = cellWidth * cellHeight * 1.0 / 2;
+
+            float sumr, sumg, sumb;
+                /* sum of intensity within cell so far, by component */
+            unsigned int b;
+            const char * colorstr;
+            unsigned int subrow;
+
+            sumr = sumg = sumb = 0;  /* initial value */
+            b = 0; /* initial value */
+
+            for (subrow = 0; subrow < cellHeight; ++subrow) {
+                unsigned int subcol;
+                for (subcol = 0; subcol < cellWidth; ++subcol) {
+                    pixel color;
+                    pixval value;
+                
+                    if (row + subrow < rows && col + subcol < cols)
+                        color = pixels[row + subrow][col + subcol];
+                    else
+                        color = ppm_whitepixel (maxval);
+
+                    sumr += pm_ungamma709((float)PPM_GETR(color)/maxval);
+                    sumg += pm_ungamma709((float)PPM_GETG(color)/maxval);
+                    sumb += pm_ungamma709((float)PPM_GETB(color)/maxval);
+
+                    value = ppm_colorvalue(color);
+                    b <<= 1;
+                    if (value > maxval/2)
+                        b |= 1;
+                }
+            }
+            colorstr = rgb2x2x2fg
+                [sumr >= sumthresh]
+                [sumg >= sumthresh]
+                [sumb >= sumthresh];
+            fprintf(ofP, "%s%c", colorstr, carr[b]);
+        }
+        fprintf(ofP, "\n");
+    }
+    fprintf(ofP, "\x1b[0m"); /* Clear final ansi attributes */
+}
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+
+    pixval maxval;
+    pixel ** pixels;
+    int rows, cols;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+    
+    pixels = ppm_readppm(ifP, &cols, &rows, &maxval);
+
+    pm_close(ifP);
+
+    ppmtoascii(pixels, cols, rows, maxval, cmdline, stdout);
+
+    return 0;
+}
diff --git a/converter/ppm/ppmtobmp.c b/converter/ppm/ppmtobmp.c
index c295f70c..6d65d744 100644
--- a/converter/ppm/ppmtobmp.c
+++ b/converter/ppm/ppmtobmp.c
@@ -20,6 +20,7 @@
 #include <string.h>
 
 #include "pm_c_util.h"
+#include "nstring.h"
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "bmp.h"
@@ -63,11 +64,11 @@ freeColorMap(const colorMap * const colorMapP) {
 
 
 
-struct cmdlineInfo {
+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;
     int class;  /* C_WIN or C_OS2 */
     unsigned int bppSpec;
     unsigned int bpp;
@@ -77,7 +78,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
    *cmdline_p structure are actually in the supplied argv array.  And
@@ -107,7 +108,7 @@ parseCommandLine(int argc, const char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
 
     if (windowsSpec && os2Spec) 
         pm_error("Can't specify both -windows and -os2 options.");
@@ -131,13 +132,22 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->mapfile = NULL;
 
     if (argc - 1 == 0)
-        cmdlineP->input_filename = strdup("-");  /* he wants stdin */
+        cmdlineP->inputFilename = pm_strdup("-");  /* he wants stdin */
     else if (argc - 1 == 1)
-        cmdlineP->input_filename = strdup(argv[1]);
+        cmdlineP->inputFilename = pm_strdup(argv[1]);
     else 
         pm_error("Too many arguments.  The only argument accepted "
                  "is the input file specificaton");
 
+    free(option_def);
+}
+
+
+
+static void
+freeCommandLine(struct CmdlineInfo const cmdline) {
+
+    pm_strfree(cmdline.inputFilename);
 }
 
 
@@ -177,20 +187,18 @@ PutLong(FILE * const fp, long const v) {
  * BMP writing
  */
 
-static int
+static unsigned int
 BMPwritefileheader(FILE *        const fp, 
-                   int           const class, 
-                   unsigned long const bitcount, 
-                   unsigned long const x, 
-                   unsigned long const y) {
+                   unsigned int  const cbSize,
+                   unsigned int  const offBits) {
 /*----------------------------------------------------------------------------
-  Return the number of bytes written, or -1 on error.
+  Return the number of bytes written.
 -----------------------------------------------------------------------------*/
     PutByte(fp, 'B');
     PutByte(fp, 'M');
 
     /* cbSize */
-    PutLong(fp, BMPlenfile(class, bitcount, -1, x, y));
+    PutLong(fp, cbSize);
     
     /* xHotSpot */
     PutShort(fp, 0);
@@ -199,7 +207,7 @@ BMPwritefileheader(FILE *        const fp,
     PutShort(fp, 0);
     
     /* offBits */
-    PutLong(fp, BMPoffbits(class, bitcount, -1));
+    PutLong(fp, offBits);
     
     return 14;
 }
@@ -213,9 +221,9 @@ BMPwriteinfoheader(FILE *        const fp,
                    unsigned long const x, 
                    unsigned long const y) {
 /*----------------------------------------------------------------------------
-  Return the number of bytes written, or -1 on error.
+  Return the number of bytes written.
 ----------------------------------------------------------------------------*/
-    long cbFix;
+    unsigned int cbFix;
 
     switch (class) {
     case C_WIN: {
@@ -266,7 +274,7 @@ BMPwriteRgb(FILE * const fp,
             pixval const G, 
             pixval const B) {
 /*----------------------------------------------------------------------------
-  Return the number of bytes written, or -1 on error.
+  Return the number of bytes written.
 -----------------------------------------------------------------------------*/
     switch (class) {
     case C_WIN:
@@ -282,8 +290,8 @@ BMPwriteRgb(FILE * const fp,
         return 3;
     default:
         pm_error(er_internal, "BMPwriteRgb");
+        return -1;  /* avoid compiler warning. */
     }
-    return -1;
 }
 
 
@@ -294,7 +302,7 @@ BMPwriteColormap(FILE *           const ifP,
                  int              const bpp,
                  const colorMap * const colorMapP) {
 /*----------------------------------------------------------------------------
-  Return the number of bytes written, or -1 on error.
+  Return the number of bytes written.
 -----------------------------------------------------------------------------*/
     unsigned int const ncolors = (1 << bpp);
 
@@ -445,7 +453,7 @@ BMPwritebits(FILE *          const fp,
              pixval          const maxval,
              colorhash_table const cht) {
 /*----------------------------------------------------------------------------
-  Return the number of bytes written, or -1 on error.
+  Return the number of bytes written.
 -----------------------------------------------------------------------------*/
     unsigned int nbyte;
     int row;
@@ -490,6 +498,9 @@ bmpEncode(FILE *           const ifP,
 /*----------------------------------------------------------------------------
   Write a BMP file of the given class.
 -----------------------------------------------------------------------------*/
+    unsigned int const cbSize  = BMPlenfile(class, bpp, 0, x, y);
+    unsigned int const offbits = BMPoffbits(class, bpp, 0);
+
     unsigned long nbyte;
 
     if (colortype == PALETTE)
@@ -498,19 +509,17 @@ bmpEncode(FILE *           const ifP,
         pm_message("Writing %u bits per pixel truecolor (no palette)", bpp);
 
     nbyte = 0;  /* initial value */
-    nbyte += BMPwritefileheader(ifP, class, bpp, x, y);
+    nbyte += BMPwritefileheader(ifP, cbSize, offbits);
     nbyte += BMPwriteinfoheader(ifP, class, bpp, x, y);
     if (colortype == PALETTE)
         nbyte += BMPwriteColormap(ifP, class, bpp, colorMapP);
 
-    if (nbyte != (BMPlenfileheader(class)
-                  + BMPleninfoheader(class)
-                  + BMPlencolormap(class, bpp, 0)))
+    if (nbyte != offbits)
         pm_error(er_internal, "BmpEncode 1");
 
     nbyte += BMPwritebits(ifP, x, y, colortype, bpp, pixels, maxval,
                           colorMapP->cht);
-    if (nbyte != BMPlenfile(class, bpp, -1, x, y))
+    if (nbyte != cbSize)
         pm_error(er_internal, "BmpEncode 2");
 }
 
@@ -544,6 +553,8 @@ bmpEncodePbm(FILE *           const ifP,
        Only PBM input uses this routine.  Color images represented by 1 bpp via
        color palette use the general bmpEncode().
     */
+    unsigned int const cbSize       = BMPlenfile(class, 1, 0, cols, rows);
+    unsigned int const offbits      = BMPoffbits(class, 1, 0);
     unsigned int const adjustedCols = (cols + 31) / 32 * 32;
     unsigned int const packedBytes  = adjustedCols / 8;
 
@@ -555,18 +566,16 @@ bmpEncodePbm(FILE *           const ifP,
     pm_message("Writing 1 bit per pixel with a black-white palette");
 
     nbyte = 0;  /* initial value */
-    nbyte += BMPwritefileheader(ifP, class, 1, cols, rows);
+    nbyte += BMPwritefileheader(ifP, cbSize, offbits);
     nbyte += BMPwriteinfoheader(ifP, class, 1, cols, rows);
 
     makeBilevelColorMap(&bilevelColorMap);
 
     nbyte += BMPwriteColormap(ifP, class, 1, &bilevelColorMap);
 
-    if (nbyte != (BMPlenfileheader(class)
-                  + BMPleninfoheader(class)
-                  + BMPlencolormap(class, 1, 0)))
-        pm_error(er_internal, "bmpEncodePBM 1");
-   
+    if (nbyte != offbits)
+        pm_error(er_internal, "bmpEncodePbm 1");
+
     for (row = 0; row < rows; ++row){
         size_t bytesWritten;
 
@@ -581,7 +590,7 @@ bmpEncodePbm(FILE *           const ifP,
             nbyte += bytesWritten;
     }
 
-    if (nbyte != BMPlenfile(class, 1, -1, cols, rows))
+    if (nbyte != cbSize)
         pm_error(er_internal, "bmpEncodePbm 2");
 }
 
@@ -822,7 +831,6 @@ doPbm(FILE *       const ifP,
         32 bit borders and that in BMP the bottom row comes first in
         order.
     */
-    int const CHARBITS = (sizeof(unsigned char)*8); 
     int const colChars = pbm_packed_bytes(cols);
     int const adjustedCols = (cols+31) /32 * 32;
     int const packedBytes  =  adjustedCols /8;
@@ -854,11 +862,8 @@ doPbm(FILE *       const ifP,
            some BMP viewers may get confused with that.
         */
 
-        if (cols % 8 >0) {
-            /* adjust final partial byte */
-            thisRow[colChars-1] >>= CHARBITS - cols % CHARBITS;
-            thisRow[colChars-1] <<= CHARBITS - cols % CHARBITS;
-        }
+        /* Clean off remainder of fractional last character */
+        pbm_cleanrowend_packed(thisRow, cols);
     }
 
     bmpEncodePbm(ofP, class, cols, rows, bitrow);
@@ -908,6 +913,8 @@ doPgmPpm(FILE *       const ifP,
               cols, rows, (const pixel**)pixels, maxval, &colorMap);
     
     freeColorMap(&colorMap);
+
+    ppm_freearray(pixels, rows);
 }
 
 
@@ -916,7 +923,7 @@ int
 main(int           argc,
      const char ** argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     int rows;
     int cols;
@@ -927,7 +934,7 @@ main(int           argc,
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.input_filename);
+    ifP = pm_openr(cmdline.inputFilename);
     
     ppm_readppminit(ifP, &cols, &rows, &maxval, &ppmFormat);
     
@@ -938,6 +945,8 @@ main(int           argc,
                  cmdline.class, cmdline.bppSpec, cmdline.bpp, cmdline.mapfile,
                  stdout);
 
+    freeCommandLine(cmdline);
+
     pm_close(ifP);
     pm_close(stdout);
 
diff --git a/converter/ppm/ppmtogif.c b/converter/ppm/ppmtogif.c
index 93feaa95..fa7d1dbe 100644
--- a/converter/ppm/ppmtogif.c
+++ b/converter/ppm/ppmtogif.c
@@ -68,7 +68,7 @@ handleLatex2htmlHack(void) {
   This program used to put out a "usage" message when it saw an option
   it didn't understand.  Latex2html's configure program does a
   ppmtogif -h (-h was never a valid option) to elicit that message and
-  then parses the message to see if it included the strings
+  then parses the message to see if it includes the strings
   "-interlace" and "-transparent".  That way it knows if the
   'ppmtogif' program it found has those options or not.  I don't think
   any 'ppmtogif' you're likely to find today lacks those options, but
@@ -91,7 +91,7 @@ handleLatex2htmlHack(void) {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Parse the program arguments (given by argc and argv) into a form
@@ -146,7 +146,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (latex2htmlhack) 
@@ -183,8 +183,8 @@ openPnmremapStream(const char * const inputFileName,
     assert(inputFileName != NULL);
     assert(mapFileName != NULL);
 
-    asprintfN(&pnmremapCommand, "pnmremap -mapfile='%s' %s",
-              mapFileName, inputFileName);
+    pm_asprintf(&pnmremapCommand, "pnmremap -mapfile='%s' %s",
+                mapFileName, inputFileName);
 
     if (verbose)
         pm_message("Preprocessing Pamtogif input with shell command '%s'",
@@ -198,7 +198,7 @@ openPnmremapStream(const char * const inputFileName,
     else
         *pnmremapPipeP = pnmremapPipe;
 
-    strfree(pnmremapCommand);
+    pm_strfree(pnmremapCommand);
 }
 
 
@@ -221,39 +221,39 @@ pamtogifCommand(const char *       const arg0,
 
         struct stat statbuf;
 
-        asprintfN(&progName, "%s/%s", arg0DirName, pamtogifName);
+        pm_asprintf(&progName, "%s/%s", arg0DirName, pamtogifName);
 
         if (stat(progName, &statbuf) == 0)
             commandVerb = progName;
         else
             commandVerb = strdup(pamtogifName);
 
-        strfree(arg0DirName);
+        pm_strfree(arg0DirName);
     } else
         commandVerb = strdup(pamtogifName);
 
     if (cmdline.transparent)
-        asprintfN(&transparentOpt, "-transparent=%s", cmdline.transparent);
+        pm_asprintf(&transparentOpt, "-transparent=%s", cmdline.transparent);
     else
         transparentOpt = strdup("");
 
     if (cmdline.comment)
-        asprintfN(&commentOpt, "-comment=%s", cmdline.comment);
+        pm_asprintf(&commentOpt, "-comment=%s", cmdline.comment);
     else
         commentOpt = strdup("");
 
-    asprintfN(&retval, "%s - -alphacolor=%s %s %s %s %s %s %s",
-              commandVerb,
-              cmdline.alphacolor,
-              cmdline.interlace ? "-interlace" : "",
-              cmdline.sort ? "-sort" : "",
-              transparentOpt,
-              commentOpt,
-              cmdline.nolzw ? "-nolzw" : "",
-              cmdline.verbose ? "-verbose" : "");
+    pm_asprintf(&retval, "%s - -alphacolor=%s %s %s %s %s %s %s",
+                commandVerb,
+                cmdline.alphacolor,
+                cmdline.interlace ? "-interlace" : "",
+                cmdline.sort ? "-sort" : "",
+                transparentOpt,
+                commentOpt,
+                cmdline.nolzw ? "-nolzw" : "",
+                cmdline.verbose ? "-verbose" : "");
     
-    strfree(transparentOpt);
-    strfree(commentOpt);
+    pm_strfree(transparentOpt);
+    pm_strfree(commentOpt);
 
     return retval;
 }
@@ -374,8 +374,8 @@ feedPamtogif(struct pam * const inPamP,
 
 
 int
-main(int    argc,
-     char * argv[]) {
+main(int           argc,
+     const char ** argv) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
@@ -384,7 +384,7 @@ main(int    argc,
     FILE * pipeToPamtogif;
     int rc;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -413,7 +413,7 @@ main(int    argc,
     if (rc != 0)
         pm_error("Pamtogif process failed.  pclose() failed.");
 
-    strfree(command);
+    pm_strfree(command);
 
     pm_close(ifP);
     pm_close(stdout);
diff --git a/converter/ppm/ppmtoicr.c b/converter/ppm/ppmtoicr.c
index feca0c18..3c8be421 100644
--- a/converter/ppm/ppmtoicr.c
+++ b/converter/ppm/ppmtoicr.c
@@ -10,311 +10,255 @@
 ** implied warranty.
 */
 
+#include <stdbool.h>
+#include <assert.h>
 #include "ppm.h"
 
-#define MAXCOLORS 256
-#define CLUTCOLORS 768
+#define MAXCOLORCT 256
+#define CLUTCOLORCT 768
 
-static int colorstobpp ARGS(( int colors ));
-static int GetPixel ARGS(( int x, int y ));
-static int rleit ARGS(( char* buf, char* bufto, int len ));
 
-static pixel** pixels;
-static colorhash_table cht;
-static char* testimage;
 
-int
-main(argc, argv)
-int argc;
-char* argv[];
-{
-	FILE* ifp;
-	int argn, rows, cols, colors, i, j, BitsPerPixel, newxsize;
-	pixval maxval;
-	colorhist_vector chv;
-	char rgb[CLUTCOLORS];
-	const char* windowname;
-	char* thischar;
-	char* thisline;
-	char* space;
-	register unsigned char c;
-	register char* p;
-	int display, expand;
-	int rleflag, winflag;
-	const char* const usage = "[-windowname windowname] [-expand expand] [-display display] [-rle] [ppmfile]";
-
-
-	ppm_init( &argc, argv );
-
-	argn = 1;
-	windowname = "untitled";
-	winflag = 0;
-	expand = 1;
-	display = 0;
-	rleflag = 0;
-
-	while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-	    {
-	    if ( pm_keymatch(argv[argn],"-windowname",2) && argn + 1 < argc )
-		{
-		++argn;
-		windowname = argv[argn];
-		winflag = 1;
-		}
-	    else if ( pm_keymatch(argv[argn],"-expand",2) && argn + 1 < argc )
-		{
-		++argn;
-		if ( sscanf( argv[argn], "%d",&expand ) != 1 )
-		    pm_usage( usage );
-		}
-	    else if ( pm_keymatch(argv[argn],"-display",2) && argn + 1 < argc )
-		{
-		++argn;
-		if ( sscanf( argv[argn], "%d",&display ) != 1 )
-		    pm_usage( usage );
-		}
-	    else if ( pm_keymatch(argv[argn],"-rle",2) )
-		rleflag = 1;
-	    else if ( pm_keymatch(argv[argn],"-norle",2) )
-		rleflag = 0;
-	    else
-		pm_usage( usage );
-	    }
-
-	if ( argn < argc )
-	    {
-	    ifp = pm_openr( argv[argn] );
-	    if ( ! winflag )
-		windowname = argv[argn];
-	    ++argn;
-	    }
-	else
-	    ifp = stdin;
-
-	if ( argn != argc )
-	    pm_usage( usage );
-
-	pixels = ppm_readppm( ifp, &cols, &rows, &maxval );
-
-	pm_close( ifp );
-
-	for (i = 0; i < CLUTCOLORS; i++)
-	    rgb[i] = 0;
-
-	/* Figure out the colormap. */
-	pm_message("computing colormap..." );
-	chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
-	if (chv == (colorhist_vector) 0)
-	pm_error( "too many colors - try doing a 'pnmquant %d'", MAXCOLORS );
-	pm_message("%d colors found", colors );
-
-	/* Turn the ppm colormap into an ICR colormap. */
-	if (maxval > 255)
-	pm_message(
-		"maxval is not 255 - automatically rescaling colors" );
-	for (i = 0; i < colors; i++)
-	{
-	j = (3 * i);
-	if (maxval == 255)
-		{
-		rgb[j] = PPM_GETR(chv[i].color) ;
-		j++;
-		rgb[j] = PPM_GETG(chv[i].color) ;
-		j++;
-		rgb[j] = PPM_GETB(chv[i].color) ;
-		}
-	else
-		{
-		rgb[j] = (int) PPM_GETR(chv[i].color) * 255 / maxval;
-		j++;
-		rgb[j] = (int) PPM_GETG(chv[i].color) * 255 / maxval;
-		j++;
-		rgb[j] = (int) PPM_GETB(chv[i].color) * 255 / maxval;
-		}
-	}
-	BitsPerPixel = colorstobpp(colors);
-
-	/* And make a hash table for fast lookup. */
-	cht = ppm_colorhisttocolorhash(chv, colors);
-	ppm_freecolorhist(chv);
-
-
-	/************** Create a new window using ICR protocol *********/
-	/* Format is "ESC^W;left;top;width;height;display;windowname"  */
-
-	pm_message("creating window %s ...", windowname );
-	(void)printf("\033^W;%d;%d;%d;%d;%d;%s^",0,0,cols*expand,rows*expand,display,windowname);
-	fflush(stdout);
-
-
-	/****************** Download the colormap.  ********************/
-	pm_message("downloading colormap for %s ...", windowname );
-
-	(void)printf("\033^M;%d;%d;%d;%s^",0,MAXCOLORS,CLUTCOLORS,windowname);
-	thischar = rgb;
-	for (j=0; j<CLUTCOLORS; j++) {
-	c = *thischar++;
-		if (c > 31 && c < 123 ) {	 /* printable ASCII */
-		putchar(c);
-		}
-		else {
-		putchar((c>>6)+123);	 /* non-printable, so encode it */
-		putchar((c & 0x3f) + 32);
-		}
-	}
-	fflush(stdout);
-
-	/**************** send out picture *************************/
-	/* Protocol's RLE scheme is quicker but buggy              */
-
-	if (rleflag) {	
-		pm_message("sending run-length encoded picture data ..." );
-		testimage = (char*) malloc(rows*cols);
-		p = testimage;
-		for (i=0; i<rows; i++)
-			for (j=0; j<cols; j++) 
-			*p++ = GetPixel(j,i);
-		space = (char*) malloc(rows*3);
-		thisline = testimage;
-		for (i = 0; i < rows; i++) {
-			newxsize = rleit(thisline,space,cols);
-			thisline += cols;	/* increment to next line */
-		(void)printf("\033^R;%d;%d;%d;%d;%s^",0,i*expand,expand,newxsize,windowname);
-		thischar = space;
-		for (j=0; j< newxsize; j++) {
-			c= *thischar++;  /*get byte to send */
-			if (c>31 && c <123) {
-				putchar(c);
-				}
-			else {
-				putchar((c>>6) + 123);
-				putchar((c & 0x3f) + 32);
-				}
-			}
-			fflush(stdout);
-		}
-		free(space);
-		exit(0);
-		}
-
-	/* Otherwise, send out uncompressed pixel data via the slow method */
-
-		else {
-		pm_message("sending picture data ..." );
-		for (i = 0; i < rows; i++) {
-			(void)printf("\033^P;%d;%d;%d;%d;%s^",0,i*expand,expand,cols,windowname);
-			for (j = 0; j < cols; j++) {
-				c  = GetPixel(j,i);
-				if (c > 31 && c < 123) {
-						putchar(c);
-						}
-				else		{
-						putchar((c>>6)+123);
-						putchar((c & 0x3f) + 32);
-						}
-				}
-			}
-		fflush(stdout);
-		exit(0);
-		}
-	}
+static void
+makeIcrColormap(colorhist_vector const chv,
+                unsigned int     const colorCt,
+                pixval           const maxval,
+                char *           const rgb) {
+
+    unsigned int i;
+
+    if (maxval > 255)
+        pm_message("Maxval is not 255 - automatically rescaling colors" );
+
+    for (i = 0; i < CLUTCOLORCT; ++i)
+        rgb[i] = 0;
+
+    for (i = 0; i < colorCt; ++i) {
+        unsigned int j;
+
+        j = (3 * i);
+
+        if (maxval == 255) {
+            rgb[j++] = PPM_GETR(chv[i].color) ;
+            rgb[j++] = PPM_GETG(chv[i].color) ;
+            rgb[j++] = PPM_GETB(chv[i].color) ;
+        } else {
+            rgb[j++] = (unsigned int) PPM_GETR(chv[i].color) * 255 / maxval;
+            rgb[j++] = (unsigned int) PPM_GETG(chv[i].color) * 255 / maxval;
+            rgb[j++] = (unsigned int) PPM_GETB(chv[i].color) * 255 / maxval;
+        }
+    }
+}
+
+
 
 static int
-colorstobpp(colors)
-int colors;
-	{
-	int bpp;
-
-	if (colors <= 2)
-	bpp = 1;
-	else if (colors <= 4)
-	bpp = 2;
-	else if (colors <= 8)
-	bpp = 3;
-	else if (colors <= 16)
-	bpp = 4;
-	else if (colors <= 32)
-	bpp = 5;
-	else if (colors <= 64)
-	bpp = 6;
-	else if (colors <= 128)
-	bpp = 7;
-	else if (colors <= 256)
-	bpp = 8;
-	else
-	pm_error("can't happen" );
-	return bpp;
-	}
+bppFromColorCt(unsigned int const colorCt) {
+
+    unsigned int bpp;
+
+    if (colorCt <= 2)
+        bpp = 1;
+    else if (colorCt <= 4)
+        bpp = 2;
+    else if (colorCt <= 8)
+        bpp = 3;
+    else if (colorCt <= 16)
+        bpp = 4;
+    else if (colorCt <= 32)
+        bpp = 5;
+    else if (colorCt <= 64)
+        bpp = 6;
+    else if (colorCt <= 128)
+        bpp = 7;
+    else if (colorCt <= 256)
+        bpp = 8;
+    else
+        assert(false);
+
+    return bpp;
+}
+
+
 
 static int
-GetPixel(x, y)
-int x, y;
-	{
-	int color;
+colorIndexAtPosition(unsigned int    const x,
+                     unsigned int    const y,
+                     pixel **        const pixels,
+                     colorhash_table const cht) {
 
-	color = ppm_lookupcolor(cht, &pixels[y][x]);
-	return color;
-	}
+    int rc;
 
+    rc = ppm_lookupcolor(cht, &pixels[y][x]);
 
-/* rleit   compress with run length encoding as per NCSA's documentation */
+    /* Every color in the image is in the palette */
+    assert(rc >= 0);
 
-static int
-rleit(buf,bufto,len)
-	char* buf;
-	char* bufto;
-	int len;
-	{
-	register char* p;
-	register char* q;
-	register char* cfoll;
-	register char* clead;
-	char* begp;
-	int i;
-
-	p = buf;
-	cfoll = bufto;
-	clead = cfoll + 1;
-
-	begp = p;
-	while (len > 0 ) {		/* encode until gone */
-		
-		q = p + 1;
-		i = len-1;
-	while (*p == *q && i+120 > len && i) {
-		q++;
-		i--;
-	}
-
-	if (q > p +2) {			/* three in a row */
-		if (p > begp) {
-			*cfoll = p - begp;
-			cfoll = clead;
-		}
-		*cfoll++ = 128 | (q-p);		/*len of seq*/
-		*cfoll++ = *p;			/* char of seq */
-		len -= q-p;		/* subtract len of seq */
-		p = q;
-		clead = cfoll+1;
-		begp = p;
-	}
-	else {
-		*clead++ = *p++;	/* copy one char */
-		len--;
-		if (p>begp + 120) {
-			*cfoll = p - begp;
-			cfoll = clead++;
-			begp = p;
-		}
-	}
-	}
-
-/* fillin last bytecount */
-
-	if (p>begp)
-		*cfoll = (p - begp);
-	else
-		clead--;
-
-	return((int) (clead-bufto));	/*how many stored as encoded */
+    return rc;
 }
+
+
+
+static void
+downloadColormap(char         const rgb[CLUTCOLORCT],
+                 const char * const windowName) {
+
+    unsigned int i;
+
+    pm_message("Downloading colormap for %s ...", windowName);
+
+    printf("\033^M;%d;%d;%d;%s^",
+           0, MAXCOLORCT, CLUTCOLORCT, windowName);
+
+    for (i = 0; i < CLUTCOLORCT; ++i) {
+        unsigned char const c = rgb[i];
+
+        if (c > 31 && c < 123) {
+            /* printable ASCII */
+            putchar(c);
+        } else {
+            /* non-printable, so encode it */
+            putchar((c >> 6) + 123);
+            putchar((c & 0x3f) + 32);
+        }
+    }
+    fflush(stdout);
+}
+
+
+
+static void
+sendOutPicture(pixel **        const pixels,
+               unsigned int    const rows,
+               unsigned int    const cols,
+               colorhash_table const cht,
+               int             const expand,
+               const char *    const windowName) {
+
+    unsigned int row;
+
+    pm_message("Sending picture data ..." );
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        printf("\033^P;%d;%d;%d;%d;%s^",
+               0, row * expand, expand, cols, windowName);
+        for (col = 0; col < cols; ++col) {
+            unsigned char const c =
+                colorIndexAtPosition(col, row, pixels, cht);
+            if (c > 31 && c < 123) {
+                putchar(c);
+            } else {
+                putchar((c >> 6) + 123);
+                putchar((c & 0x3f) + 32);
+            }
+        }
+    }
+    fflush(stdout);
+}
+
+
+
+int
+main(int argc, const char ** const argv) {
+
+    FILE * ifP;
+    int rows, cols;
+    int colorCt;
+    int argn;
+    unsigned int bitsPerPixel;
+    pixval maxval;
+    colorhist_vector chv;
+    char rgb[CLUTCOLORCT];
+    const char * windowName;
+    int display, expand;
+    int winflag;
+    const char* const usage = "[-windowname windowname] [-expand expand] [-display display] [ppmfile]";
+    pixel** pixels;
+    colorhash_table cht;
+
+    pm_proginit(&argc, argv);
+
+    argn = 1;
+    windowName = "untitled";
+    winflag = 0;
+    expand = 1;
+    display = 0;
+
+    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
+    {
+        if ( pm_keymatch(argv[argn],"-windowname",2) && argn + 1 < argc )
+        {
+            ++argn;
+            windowName = argv[argn];
+            winflag = 1;
+        }
+        else if ( pm_keymatch(argv[argn],"-expand",2) && argn + 1 < argc )
+        {
+            ++argn;
+            if ( sscanf( argv[argn], "%d",&expand ) != 1 )
+                pm_usage( usage );
+        }
+        else if ( pm_keymatch(argv[argn],"-display",2) && argn + 1 < argc )
+        {
+            ++argn;
+            if ( sscanf( argv[argn], "%d",&display ) != 1 )
+                pm_usage( usage );
+        }
+        else
+            pm_usage( usage );
+    }
+
+    if ( argn < argc )
+    {
+        ifP = pm_openr( argv[argn] );
+        if ( ! winflag )
+            windowName = argv[argn];
+        ++argn;
+    }
+    else
+        ifP = stdin;
+
+    if ( argn != argc )
+        pm_usage( usage );
+
+    pixels = ppm_readppm(ifP, &cols, &rows, &maxval);
+
+    pm_close(ifP);
+
+    /* Figure out the colormap. */
+    pm_message("Computing colormap..." );
+    chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORCT, &colorCt);
+    if (!chv)
+        pm_error("Too many colors - try doing a 'pnmquant %u'", MAXCOLORCT);
+    pm_message("%u colors found", colorCt );
+
+    makeIcrColormap(chv, colorCt, maxval, rgb);
+
+    bitsPerPixel = bppFromColorCt(colorCt);
+
+    /* And make a hash table for fast lookup. */
+    cht = ppm_colorhisttocolorhash(chv, colorCt);
+
+    ppm_freecolorhist(chv);
+
+    /************** Create a new window using ICR protocol *********/
+    /* Format is "ESC^W;left;top;width;height;display;windowname"  */
+
+    pm_message("Creating window %s ...", windowName);
+
+    printf("\033^W;%d;%d;%d;%d;%d;%s^",
+           0, 0, cols * expand, rows * expand, display, windowName);
+    fflush(stdout);
+
+    /****************** Download the colormap.  ********************/
+
+    downloadColormap(rgb, windowName);
+
+    sendOutPicture(pixels, rows, cols, cht, expand, windowName);
+
+    return 0;
+}
+
+
+
diff --git a/converter/ppm/ppmtoilbm.c b/converter/ppm/ppmtoilbm.c
index d296f498..595aa3f4 100644
--- a/converter/ppm/ppmtoilbm.c
+++ b/converter/ppm/ppmtoilbm.c
@@ -38,8 +38,11 @@
 **  - added IFF text chunks
 **
 **  Feb 2010: afu
-**  Added dimension check to prevent short int from overflowing.
-**  
+**  - added dimension check to prevent short int from overflowing.
+**
+**  June 2015: afu
+**  - moved byterun1 (or Packbits) compression to lib/util/runlenth.c
+**  - fixed bug with HAM -nocompress
 **
 **  TODO:
 **  - multipalette capability (PCHG chunk) for std and HAM
@@ -64,6 +67,7 @@
 ** implied warranty.
 */
 
+#include <assert.h>
 #include <string.h>
 
 #include "pm_c_util.h"
@@ -71,6 +75,7 @@
 #include "ppm.h"
 #include "ppmfloyd.h"
 #include "pbm.h"
+#include "runlength.h"
 #include "ilbm.h"
 #include "lum.h"
 
@@ -106,31 +111,6 @@
 
 #define INT16MAX 32767
 
-static void put_big_short ARGS((short s));
-static void put_big_long ARGS((long l));
-#define put_byte(b)     (void)(putc((unsigned char)(b), stdout))
-static void write_bytes ARGS((unsigned char *buffer, int bytes));
-static void ppm_to_ham ARGS((FILE *fp, int cols, int rows, int maxval, pixel *colormap, int colors, int cmapmaxval, int hamplanes));
-static void ppm_to_deep ARGS((FILE *fp, int cols, int rows, int maxval, int bitspercolor));
-static void ppm_to_dcol ARGS((FILE *fp, int cols, int rows, int maxval, DirectColor *dcol));
-static void ppm_to_rgb8 ARGS((FILE *fp, int cols, int rows, int maxval));
-static void ppm_to_rgbn ARGS((FILE *fp, int cols, int rows, int maxval));
-static void ppm_to_std ARGS((FILE *fp, int cols, int rows, int maxval, pixel *colormap, int colors, int cmapmaxval, int maxcolors, int nPlanes));
-static void ppm_to_cmap ARGS((pixel *colormap, int colors, int maxval));
-static void write_bmhd ARGS((int cols, int rows, int nPlanes));
-static void write_cmap ARGS((pixel *colormap, int colors, int maxval));
-static long encode_row ARGS((FILE *outfile, rawtype *rawrow, int cols, int nPlanes));
-static long encode_maskrow ARGS((FILE *outfile, rawtype *rawrow, int cols));
-static int compress_row ARGS((int bytes));
-static void store_bodyrow ARGS((unsigned char *row, int len));
-static int runbyte1 ARGS((int bytes));
-static pixel * next_pixrow ARGS((FILE *fp, int row));
-static int * make_val_table ARGS((int oldmaxval, int newmaxval));
-static void init_read ARGS((FILE *fp, int *colsP, int *rowsP, pixval *maxvalP, int *formatP, int readall));
-static void write_body_rows ARGS((void));
-static void write_camg ARGS((void));
-static int  length_of_text_chunks ARGS((void));
-static void write_text_chunks ARGS((void));
 #define PAD(n)      (ODD(n) ? 1 : 0)    /* pad to a word */
 
 
@@ -183,25 +163,394 @@ static short gen_camg = 0;      /* write CAMG chunk */
 #define WORSTCOMPR(bytes)       ((bytes) + (bytes)/128 + 1)
 #define DO_COMPRESS             (compmethod != cmpNone)
 
+#define NEWDEPTH(pix, table) PPM_ASSIGN((pix), (table)[PPM_GETR(pix)], (table)[PPM_GETG(pix)], (table)[PPM_GETB(pix)])
 
-/***** parse options and figure out what kind of ILBM to write *****/
+#define putByte(b)     (void)(putc((unsigned char)(b), stdout))
 
-static int get_int_val ARGS((char *string, char *option, int bot, int top));
-static int get_compr_method ARGS((char *string));
-static int get_mask_type ARGS((char *string));
-static int get_hammap_mode ARGS((char *string));
 
 
+/************ other utility functions ************/
+
+
+
+static void
+writeBytes(unsigned char * const buffer,
+           int             const bytes) {
+
+    if( fwrite(buffer, 1, bytes, stdout) != bytes )
+        pm_error("write error");
+}
+
+
+
+static int *
+makeValTable(int const oldmaxval,
+             int const newmaxval) {
+
+    unsigned int i;
+    int * table;
+
+    MALLOCARRAY_NOFAIL(table, oldmaxval + 1);
+    for (i = 0; i <= oldmaxval; ++i)
+        table[i] = ROUNDDIV(i * newmaxval, oldmaxval);
+
+    return table;
+}
+
+
+
+static int  gFormat;
+static int  gCols;
+static int  gMaxval;
+
+static void
+initRead(FILE *   const fp,
+         int *    const colsP,
+         int *    const rowsP,
+         pixval * const maxvalP,
+         int *    const formatP,
+         int      const readall) {
+
+    ppm_readppminit(fp, colsP, rowsP, maxvalP, formatP);
+
+    if( *rowsP >INT16MAX || *colsP >INT16MAX )
+      pm_error ("Input image is too large.");
+
+    if( readall ) {
+        int row;
+
+        pixels = ppm_allocarray(*colsP, *rowsP);
+        for( row = 0; row < *rowsP; row++ )
+            ppm_readppmrow(fp, pixels[row], *colsP, *maxvalP, *formatP);
+        /* pixels = ppm_readppm(fp, colsP, rowsP, maxvalP); */
+    }
+    else {
+        pixrow = ppm_allocrow(*colsP);
+    }
+    gCols = *colsP;
+    gMaxval = *maxvalP;
+    gFormat = *formatP;
+}
+
+
+
+static pixel *
+nextPixrow(FILE * const fp,
+           int    const row) {
+
+    if( pixels )
+        pixrow = pixels[row];
+    else {
+        ppm_readppmrow(fp, pixrow, gCols, gMaxval, gFormat);
+    }
+    if( maskrow ) {
+        int col;
+
+        if( maskfile )
+            pbm_readpbmrow(maskfile, maskrow, maskcols, maskformat);
+        else {
+            for( col = 0; col < gCols; col++ )
+                maskrow[col] = PBM_BLACK;
+        }
+        if( transpColor ) {
+            for( col = 0; col < gCols; col++ )
+                if( PPM_EQUAL(pixrow[col], *transpColor) )
+                    maskrow[col] = PBM_WHITE;
+        }
+    }
+    return pixrow;
+}
+
+
+
+/************ ILBM functions ************/
+
+
+
+static int
+lengthOfTextChunks(void) {
+
+    int len, n;
+
+    len = 0;
+    if( anno_chunk ) {
+        n = strlen(anno_chunk);
+        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
+    }
+    if( auth_chunk ) {
+        n = strlen(auth_chunk);
+        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
+    }
+    if( name_chunk ) {
+        n = strlen(name_chunk);
+        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
+    }
+    if( copyr_chunk ) {
+        n = strlen(copyr_chunk);
+        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
+    }
+    if( text_chunk ) {
+        n = strlen(text_chunk);
+        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
+    }
+    return len;
+}
+
+
+
+static void
+writeTextChunks(void) {
+
+    int n;
+
+    if( anno_chunk ) {
+        n = strlen(anno_chunk);
+        pm_writebiglong(stdout, ID_ANNO);
+        pm_writebiglong(stdout, n);
+        writeBytes((unsigned char *)anno_chunk, n);
+        if( ODD(n) )
+            putByte(0);
+    }
+    if( auth_chunk ) {
+        n = strlen(auth_chunk);
+        pm_writebiglong(stdout, ID_AUTH);
+        pm_writebiglong(stdout, n);
+        writeBytes((unsigned char *)auth_chunk, n);
+        if( ODD(n) )
+            putByte(0);
+    }
+    if( copyr_chunk ) {
+        n = strlen(copyr_chunk);
+        pm_writebiglong(stdout, ID_copy);
+        pm_writebiglong(stdout, n);
+        writeBytes((unsigned char *)copyr_chunk, n);
+        if( ODD(n) )
+            putByte(0);
+    }
+    if( name_chunk ) {
+        n = strlen(name_chunk);
+        pm_writebiglong(stdout, ID_NAME);
+        pm_writebiglong(stdout, n);
+        writeBytes((unsigned char *)name_chunk, n);
+        if( ODD(n) )
+            putByte(0);
+    }
+    if( text_chunk ) {
+        n = strlen(text_chunk);
+        pm_writebiglong(stdout, ID_TEXT);
+        pm_writebiglong(stdout, n);
+        writeBytes((unsigned char *)text_chunk, n);
+        if( ODD(n) )
+            putByte(0);
+    }
+}
+
+
+static void
+writeCmap(pixel * const colormap,
+          int     const colors,
+          int     const maxval) {
+
+    int cmapsize, i;
+
+    cmapsize = 3 * colors;
+
+    /* write colormap */
+    pm_writebiglong(stdout, ID_CMAP);
+    pm_writebiglong(stdout, cmapsize);
+    if( maxval != MAXCOLVAL ) {
+        int *table;
+        pm_message("maxval is not %d - automatically rescaling colors", 
+                   MAXCOLVAL);
+        table = makeValTable(maxval, MAXCOLVAL);
+        for( i = 0; i < colors; i++ ) {
+            putByte(table[PPM_GETR(colormap[i])]);
+            putByte(table[PPM_GETG(colormap[i])]);
+            putByte(table[PPM_GETB(colormap[i])]);
+        }
+        free(table);
+    }
+    else {
+        for( i = 0; i < colors; i++ ) {
+            putByte(PPM_GETR(colormap[i]));
+            putByte(PPM_GETG(colormap[i]));
+            putByte(PPM_GETB(colormap[i]));
+        }
+    }
+    if( ODD(cmapsize) )
+        putByte(0);
+}
+
+
+
+static void
+writeBmhd(int const cols,
+          int const rows,
+          int const nPlanes) {
+
+    unsigned char xasp, yasp;
+
+    xasp = 10;  /* initial value */
+    yasp = 10;  /* initial value */
+
+    if( viewportmodes & vmLACE )
+        xasp *= 2;
+    if( viewportmodes & vmHIRES )
+        yasp *= 2;
+
+    pm_writebiglong(stdout, ID_BMHD);
+    pm_writebiglong(stdout, BitMapHeaderSize);
+
+    pm_writebigshort(stdout, cols);
+    pm_writebigshort(stdout, rows);
+    pm_writebigshort(stdout, 0);                       /* x-offset */
+    pm_writebigshort(stdout, 0);                       /* y-offset */
+    putByte(nPlanes);                      /* no of planes */
+    putByte(maskmethod);                   /* masking */
+    putByte(compmethod);                   /* compression */
+    putByte(BMHD_FLAGS_CMAPOK);            /* flags */
+    if( maskmethod == mskHasTransparentColor )
+        pm_writebigshort(stdout, transpIndex);
+    else
+        pm_writebigshort(stdout, 0);
+    putByte(xasp);                         /* x-aspect */
+    putByte(yasp);                         /* y-aspect */
+    pm_writebigshort(stdout, cols);                    /* pageWidth */
+    pm_writebigshort(stdout, rows);                    /* pageHeight */
+}
+
 
-#define NEWDEPTH(pix, table) PPM_ASSIGN((pix), (table)[PPM_GETR(pix)], (table)[PPM_GETG(pix)], (table)[PPM_GETB(pix)])
 
+/************ compression ************/
 
 static void
-report_too_many_colors(int         const ifmode,
-                       int         const maxplanes,
-                       int         const hamplanes,
-                       DirectColor const dcol,
-                       int         const deepbits) {
+storeBodyrow(unsigned char * const row,
+             int             const len) {
+
+    int idx;
+
+    idx = cur_block->used;  /* initial value */
+
+    if (idx >= ROWS_PER_BLOCK) {
+        MALLOCVAR_NOFAIL(cur_block->next);
+        cur_block = cur_block->next;
+        cur_block->used = idx = 0;
+        cur_block->next = NULL;
+    }
+    MALLOCARRAY_NOFAIL(cur_block->row[idx], len);
+    cur_block->len[idx] = len;
+    memcpy(cur_block->row[idx], row, len);
+    ++cur_block->used;
+}
+
+
+
+static unsigned int
+compressRow(unsigned int const bytes) {
+
+    size_t compressedByteCt;
+
+    switch (compmethod) {
+        case cmpByteRun1:
+            pm_rlenc_compressbyte(
+                coded_rowbuf, compr_rowbuf, PM_RLE_PACKBITS, bytes,
+                &compressedByteCt);
+            break;
+        default:
+            pm_error("compressRow(): unknown compression method %d", 
+                     compmethod);
+    }
+    storeBodyrow(compr_rowbuf, compressedByteCt);
+
+    assert((unsigned)compressedByteCt == compressedByteCt);
+
+    return (unsigned)compressedByteCt;
+}
+
+
+
+static const unsigned char bitmask[] = {1, 2, 4, 8, 16, 32, 64, 128};
+
+
+
+static long
+encodeRow(FILE *    const outfile,
+              /* if non-NULL, write uncompressed row to this file */
+          rawtype * const rawrow,
+          int       const cols,
+          int       const nPlanes) {
+
+    /* encode algorithm by Johan Widen (jw@jwdata.se) */
+
+    int plane, bytes;
+    long retbytes = 0;
+
+    bytes = RowBytes(cols);
+
+    /* Encode and write raw bytes in plane-interleaved form. */
+    for( plane = 0; plane < nPlanes; plane++ ) {
+        register int col, cbit;
+        register rawtype *rp;
+        register unsigned char *cp;
+        int mask;
+
+        mask = 1 << plane;
+        cbit = -1;
+        cp = coded_rowbuf-1;
+        rp = rawrow;
+        for( col = 0; col < cols; col++, cbit--, rp++ ) {
+            if( cbit < 0 ) {
+                cbit = 7;
+                *++cp = 0;
+            }
+            if( *rp & mask )
+                *cp |= bitmask[cbit];
+        }
+        if( outfile ) {
+            writeBytes(coded_rowbuf, bytes);
+            retbytes += bytes;
+        }
+        else
+            retbytes += compressRow(bytes);
+    }
+    return retbytes;
+}
+
+
+
+static long
+encodeMaskrow(FILE *    const ofP,
+              rawtype * const rawrow,
+              int       const cols) {
+
+    int col;
+
+    for( col = 0; col < cols; col++ ) {
+        if( maskrow[col] == PBM_BLACK )
+            rawrow[col] = 1;
+        else
+            rawrow[col] = 0;
+    }
+    return encodeRow(ofP, rawrow, cols, 1);
+}
+
+
+
+static void
+writeCamg(void) {
+    pm_writebiglong(stdout, ID_CAMG);
+    pm_writebiglong(stdout, CAMGChunkSize);
+    pm_writebiglong(stdout, viewportmodes);
+}
+
+
+
+static void
+reportTooManyColors(int         const ifmode,
+                    int         const maxplanes,
+                    int         const hamplanes,
+                    DirectColor const dcol,
+                    int         const deepbits) {
     
     int const maxcolors = 1 << maxplanes;
 
@@ -237,17 +586,19 @@ report_too_many_colors(int         const ifmode,
 }
 
 
+
 static int
-get_int_val(string, option, bot, top)
-    char *string, *option;
-    int bot, top;
-{
+getIntVal(const char * const string,
+          const char * const option,
+          int          const bot,
+          int          const top) {
+
     int val;
 
-    if( sscanf(string, "%d", &val) != 1 )
+    if (sscanf(string, "%d", &val) != 1 )
         pm_error("option \"%s\" needs integer argument", option);
 
-    if( val < bot || val > top )
+    if (val < bot || val > top)
         pm_error("option \"%s\" argument value out of range (%d..%d)", 
                  option, bot, top);
 
@@ -255,10 +606,10 @@ get_int_val(string, option, bot, top)
 }
 
 
+
 static int
-get_compr_method(string)
-    char *string;
-{
+getComprMethod(const char * const string) {
+
     int retval;
     if( pm_keymatch(string, "none", 1) || pm_keymatch(string, "0", 1) )
         retval = cmpNone;
@@ -271,10 +622,10 @@ get_compr_method(string)
 }
 
 
+
 static int
-get_mask_type(string)
-    char *string;
-{
+getMaskType(const char * const string) {
+
     int retval;
 
     if( pm_keymatch(string, "none", 1) || pm_keymatch(string, "0", 1) )
@@ -297,10 +648,10 @@ get_mask_type(string)
 }
 
 
+
 static int
-get_hammap_mode(string)
-    char *string;
-{
+getHammapMode(const char * const string) {
+
     int retval;
 
     if( pm_keymatch(string, "grey", 1) || pm_keymatch(string, "gray", 1) )
@@ -320,14 +671,16 @@ get_hammap_mode(string)
 }
 
 
+
 /************ colormap file ************/
 
+
+
 static void
-ppm_to_cmap(colorrow, colors, maxval)
-    pixel *colorrow;
-    int colors;
-    int maxval;
-{
+ppmToCmap(pixel * const colorrow,
+          int     const colors,
+          int     const maxval) {
+
     int formsize, cmapsize;
 
     cmapsize = colors * 3;
@@ -336,27 +689,21 @@ ppm_to_cmap(colorrow, colors, maxval)
         4 +                                 /* ILBM */
         4 + 4 + BitMapHeaderSize +          /* BMHD size header */
         4 + 4 + cmapsize + PAD(cmapsize) +  /* CMAP size colormap */
-        length_of_text_chunks();
+        lengthOfTextChunks();
 
-    put_big_long(ID_FORM);
-    put_big_long(formsize);
-    put_big_long(ID_ILBM);
+    pm_writebiglong(stdout, ID_FORM);
+    pm_writebiglong(stdout, formsize);
+    pm_writebiglong(stdout, ID_ILBM);
 
-    write_bmhd(0, 0, 0);
-    write_text_chunks();
-    write_cmap(colorrow, colors, maxval);
+    writeBmhd(0, 0, 0);
+    writeTextChunks();
+    writeCmap(colorrow, colors, maxval);
 }
 
-/************ HAM ************/
 
-static long 
-do_ham_body     ARGS((FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval,
-                pixval hammaxval, int nPlanes, pixel *cmap, int colors));
 
+/************ HAM ************/
 
-static int hcmp (const void *va, const void *vb);
-static pixel *compute_ham_cmap ARGS((int cols, int rows, int maxval, 
-                                     int maxcolors, int *colorsP, int hbits));
 
 
 typedef struct {
@@ -365,20 +712,33 @@ typedef struct {
 } hentry;
 
 
+
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn hcmp;
+#endif
+
 static int
-hcmp(const void *va, const void *vb)
-{
-    return(((hentry *)vb)->count - ((hentry *)va)->count);  
-        /* reverse sort, highest count first */
+hcmp(const void * const a,
+     const void * const b) {
+
+    /* reverse sort, highest count first */
+
+    const hentry * const vaP = a;
+    const hentry * const vbP = b;
+
+    return(vbP->count - vaP->count);  
 }
 
 
+
 static pixel *
-compute_ham_cmap(cols, rows, maxval, maxcolors, colorsP, hbits)
-    int cols, rows, maxval, maxcolors;
-    int *colorsP;
-    int hbits;
-{
+computeHamCmap(int   const cols,
+               int   const rows,
+               int   const maxval,
+               int   const maxcolors,
+               int * const colorsP,
+               int   const hbits) {
+
     int colors;
     hentry *hmap;
     pixel *cmap;
@@ -405,7 +765,7 @@ compute_ham_cmap(cols, rows, maxval, maxcolors, colorsP, hbits)
         }
     }
 
-    htable = make_val_table(maxval, hmaxval);
+    htable = makeValTable(maxval, hmaxval);
     for( row = 0; row < rows; row++ ) {
         unsigned int col;
         for( col = 0; col < cols; ++col) {
@@ -485,167 +845,38 @@ out:
 }
 
 
-static void
-ppm_to_ham(fp, cols, rows, maxval, colormap, colors, cmapmaxval, hamplanes)
-    FILE *fp;
-    int cols, rows, maxval;
-    pixel *colormap;
-    int colors, cmapmaxval, hamplanes;
-{
-    int hamcolors, nPlanes, i, hammaxval;
-    long oldsize, bodysize, formsize, cmapsize;
-    int *table = NULL;
-
-    if( maskmethod == mskHasTransparentColor ) {
-        pm_message("masking method '%s' not usable with HAM - "
-                   "using '%s' instead",
-                   mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]);
-        maskmethod = mskHasMask;
-    }
-
-    hamcolors = 1 << (hamplanes-2);
-    hammaxval = pm_bitstomaxval(hamplanes-2);
-
-    if( colors == 0 ) {
-        /* no colormap, make our own */
-        switch( hammapmode ) {
-            case HAMMODE_GRAY:
-                colors = hamcolors;
-                MALLOCARRAY_NOFAIL(colormap, colors);
-#ifdef DEBUG
-                pm_message("generating grayscale colormap");
-#endif
-                table = make_val_table(hammaxval, MAXCOLVAL);
-                for( i = 0; i < colors; i++ )
-                    PPM_ASSIGN(colormap[i], table[i], table[i], table[i]);
-                free(table);
-                cmapmaxval = MAXCOLVAL;
-                break;
-            case HAMMODE_FIXED: {
-                int entries, val;
-                double step;
 
-#ifdef DEBUG
-                pm_message("generating rgb colormap");
-#endif
-                /* generate a colormap of 7 "rays" in an RGB color cube:
-                        r, g, b, r+g, r+b, g+b, r+g+b
-                   we need one colormap entry for black, so the number of
-                   entries per ray is (maxcolors-1)/7 */
+static void
+writeBodyRows(void) {
 
-                entries = (hamcolors-1)/7;
-                colors = 7*entries+1;
-                MALLOCARRAY_NOFAIL(colormap, colors);
-                step = (double)MAXCOLVAL / (double)entries;
+    bodyblock *b;
+    int i;
+    long total = 0;
 
-                PPM_ASSIGN(colormap[0], 0, 0, 0);
-                for( i = 1; i <= entries; i++ ) {
-                    val = (int)((double)i * step);
-                    PPM_ASSIGN(colormap[          i], val,   0,   0); /* r */
-                    PPM_ASSIGN(colormap[  entries+i],   0, val,   0); /* g */
-                    PPM_ASSIGN(colormap[2*entries+i],   0,   0, val); /* b */
-                    PPM_ASSIGN(colormap[3*entries+i], val, val,   0); /* r+g */
-                    PPM_ASSIGN(colormap[4*entries+i], val,   0, val); /* r+b */
-                    PPM_ASSIGN(colormap[5*entries+i],   0, val, val); /* g+b */
-                    PPM_ASSIGN(colormap[6*entries+i], val, val, val); /*r+g+b*/
-                }
-                cmapmaxval = MAXCOLVAL;
-            }
-            break;
-            case HAMMODE_RGB4:
-                colormap = compute_ham_cmap(cols, rows, maxval, hamcolors, 
-                                            &colors, 4);
-                cmapmaxval = 15;
-                break;
-            case HAMMODE_RGB5:
-                colormap = compute_ham_cmap(cols, rows, maxval, 
-                                            hamcolors, &colors, 5);
-                cmapmaxval = 31;
-                break;
-            default:
-                pm_error("ppm_to_ham(): unknown hammapmode - can't happen");
-        }
-    }
-    else {
-        hammapmode = HAMMODE_MAPFILE;
-        if( colors > hamcolors ) {
-            pm_message("colormap too large - using first %d colors", 
-                       hamcolors);
-            colors = hamcolors;
+    for( b = &firstblock; b != NULL; b = b->next ) {
+        for( i = 0; i < b->used; i++ ) {
+            writeBytes(b->row[i], b->len[i]);
+            total += b->len[i];
         }
     }
-
-    if( cmapmaxval != maxval ) {
-        int i, *table;
-        pixel *newcmap;
-
-        newcmap = ppm_allocrow(colors);
-        table = make_val_table(cmapmaxval, maxval);
-        for( i = 0; i < colors; i++ )
-            PPM_ASSIGN(newcmap[i], 
-                       table[PPM_GETR(colormap[i])], 
-                       table[PPM_GETG(colormap[i])], 
-                       table[PPM_GETB(colormap[i])]);
-        free(table);
-        ppm_freerow(colormap);
-        colormap = newcmap;
-    }
-    if( sortcmap )
-        ppm_sortcolorrow(colormap, colors, PPM_STDSORT);
-
-    nPlanes = hamplanes;
-    cmapsize = colors * 3;
-
-    bodysize = oldsize = rows * TOTALPLANES(nPlanes) * RowBytes(cols);
-    if( DO_COMPRESS ) {
-        bodysize = do_ham_body(fp, NULL, cols, rows, maxval, 
-                               hammaxval, nPlanes, colormap, colors);
-        /*bodysize = do_ham_body(fp, NULL, cols, 
-          rows, maxval, hammaxval, nPlanes, colbits, nocolor);*/
-        if( bodysize > oldsize )
-            pm_message("warning - %s compression increases BODY size "
-                       "by %ld%%", 
-                       cmpNAME[compmethod], 100*(bodysize-oldsize)/oldsize);
-        else
-            pm_message("BODY compression (%s): %ld%%", 
-                       cmpNAME[compmethod], 100*(oldsize-bodysize)/oldsize);
-    }
-
-
-    formsize =
-        4 +                                 /* ILBM */
-        4 + 4 + BitMapHeaderSize +          /* BMHD size header */
-        4 + 4 + CAMGChunkSize +             /* CAMG size viewportmodes */
-        4 + 4 + cmapsize + PAD(cmapsize) +  /* CMAP size colormap */
-        4 + 4 + bodysize + PAD(bodysize) +  /* BODY size data */
-        length_of_text_chunks();
-
-    put_big_long(ID_FORM);
-    put_big_long(formsize);
-    put_big_long(ID_ILBM);
-
-    write_bmhd(cols, rows, nPlanes);
-    write_text_chunks();
-    write_camg();       /* HAM requires CAMG chunk */
-    write_cmap(colormap, colors, maxval);
-
-    /* write body */
-    put_big_long(ID_BODY);
-    put_big_long(bodysize);
-    if( DO_COMPRESS )
-        write_body_rows();
-    else
-        do_ham_body(fp, stdout, cols, rows, maxval, hammaxval, 
-                    nPlanes, colormap, colors);
+    if( ODD(total) )
+        putByte(0);
 }
 
 
+
 static long
-do_ham_body(FILE *ifP, FILE *ofp, int cols, int rows,
-            pixval maxval, pixval hammaxval, int nPlanes,
-            pixel *colormap, int colors)
-{
-    register int col, row, i;
+doHamBody(FILE *  const ifP,
+          FILE *  const ofP,
+          int     const cols,
+          int     const rows,
+          pixval  const maxval,
+          pixval  const hammaxval,
+          int     const nPlanes,
+          pixel * const colormap,
+          int     const colors) {
+
+    int col, row, i;
     rawtype *raw_rowbuf;
     ppm_fs_info *fi = NULL;
     colorhash_table cht, cht2;
@@ -665,7 +896,7 @@ do_ham_body(FILE *ifP, FILE *ofp, int cols, int rows,
     hamcode_green = HAMCODE_GREEN << colbits;
     hamcode_blue  = HAMCODE_BLUE << colbits;
 
-    itoh = make_val_table(maxval, hammaxval);
+    itoh = makeValTable(maxval, hammaxval);
 
     if( floyd )
         fi = ppm_fs_init(cols, maxval, 0);
@@ -677,7 +908,7 @@ do_ham_body(FILE *ifP, FILE *ofp, int cols, int rows,
         pixel *prow;
 
         noprev = 1;
-        prow = next_pixrow(ifP, row);
+        prow = nextPixrow(ifP, row);
         for( col = ppm_fs_startrow(fi, prow); 
              col < cols; 
              col = ppm_fs_next(fi, col) ) {
@@ -790,13 +1021,13 @@ do_ham_body(FILE *ifP, FILE *ofp, int cols, int rows,
             }
             ppm_fs_update3(fi, col, upr, upg, upb);
         }
-        bodysize += encode_row(ofp, raw_rowbuf, cols, nPlanes);
+        bodysize += encodeRow(ofP, raw_rowbuf, cols, nPlanes);
         if( maskmethod == mskHasMask )
-            bodysize += encode_maskrow(ofp, raw_rowbuf, cols);
+            bodysize += encodeMaskrow(ofP, raw_rowbuf, cols);
         ppm_fs_endrow(fi);
     }
-    if( ofp && ODD(bodysize) )
-        put_byte(0);
+    if( ofP && ODD(bodysize) )
+        putByte(0);
 
     free(itoh);
 
@@ -808,34 +1039,132 @@ do_ham_body(FILE *ifP, FILE *ofp, int cols, int rows,
 }
 
 
-/************ deep (24-bit) ************/
-
-static long do_deep_body      ARGS((FILE *ifP, FILE *ofp, 
-                                    int cols, int rows, 
-                                    pixval maxval, int bitspercolor));
 
 static void
-ppm_to_deep(fp, cols, rows, maxval, bitspercolor)
-    FILE *fp;
-    int cols, rows, maxval, bitspercolor;
-{
-    int nPlanes;
-    long bodysize, oldsize, formsize;
+ppmToHam(FILE *  const ifP,
+         int     const cols,
+         int     const rows,
+         int     const maxval,
+         pixel * const colormapArg,
+         int     const colorsArg,
+         int     const cmapmaxvalArg,
+         int     const hamplanes) {
+
+    int hamcolors, nPlanes, i, hammaxval;
+    long oldsize, bodysize, formsize, cmapsize;
+    int * table;
+    int colors;
+    pixel * colormap;
+    int cmapmaxval;
+
+    table = NULL;  /* initial value */
+    colors = colorsArg;  /* initial value*/
+    colormap = colormapArg;  /* initial value */
+    cmapmaxval = cmapmaxvalArg;  /* initial value */
 
     if( maskmethod == mskHasTransparentColor ) {
-        pm_message("masking method '%s' not usable with deep ILBM - "
+        pm_message("masking method '%s' not usable with HAM - "
                    "using '%s' instead",
-                    mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]);
+                   mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]);
         maskmethod = mskHasMask;
     }
 
-    nPlanes = 3*bitspercolor;
+    hamcolors = 1 << (hamplanes-2);
+    hammaxval = pm_bitstomaxval(hamplanes-2);
+
+    if( colors == 0 ) {
+        /* no colormap, make our own */
+        switch( hammapmode ) {
+            case HAMMODE_GRAY:
+                colors = hamcolors;
+                MALLOCARRAY_NOFAIL(colormap, colors);
+                table = makeValTable(hammaxval, MAXCOLVAL);
+                for( i = 0; i < colors; i++ )
+                    PPM_ASSIGN(colormap[i], table[i], table[i], table[i]);
+                free(table);
+                cmapmaxval = MAXCOLVAL;
+                break;
+            case HAMMODE_FIXED: {
+                int entries, val;
+                double step;
+
+                /* generate a colormap of 7 "rays" in an RGB color cube:
+                        r, g, b, r+g, r+b, g+b, r+g+b
+                   we need one colormap entry for black, so the number of
+                   entries per ray is (maxcolors-1)/7 */
+
+                entries = (hamcolors-1)/7;
+                colors = 7*entries+1;
+                MALLOCARRAY_NOFAIL(colormap, colors);
+                step = (double)MAXCOLVAL / (double)entries;
+
+                PPM_ASSIGN(colormap[0], 0, 0, 0);
+                for( i = 1; i <= entries; i++ ) {
+                    val = (int)((double)i * step);
+                    PPM_ASSIGN(colormap[          i], val,   0,   0); /* r */
+                    PPM_ASSIGN(colormap[  entries+i],   0, val,   0); /* g */
+                    PPM_ASSIGN(colormap[2*entries+i],   0,   0, val); /* b */
+                    PPM_ASSIGN(colormap[3*entries+i], val, val,   0); /* r+g */
+                    PPM_ASSIGN(colormap[4*entries+i], val,   0, val); /* r+b */
+                    PPM_ASSIGN(colormap[5*entries+i],   0, val, val); /* g+b */
+                    PPM_ASSIGN(colormap[6*entries+i], val, val, val); /*r+g+b*/
+                }
+                cmapmaxval = MAXCOLVAL;
+            }
+            break;
+            case HAMMODE_RGB4:
+                colormap = computeHamCmap(cols, rows, maxval, hamcolors, 
+                                          &colors, 4);
+                cmapmaxval = 15;
+                break;
+            case HAMMODE_RGB5:
+                colormap = computeHamCmap(cols, rows, maxval, 
+                                          hamcolors, &colors, 5);
+                cmapmaxval = 31;
+                break;
+            default:
+                pm_error("ppm_to_ham(): unknown hammapmode - can't happen");
+        }
+    }
+    else {
+        hammapmode = HAMMODE_MAPFILE;
+        if( colors > hamcolors ) {
+            pm_message("colormap too large - using first %d colors", 
+                       hamcolors);
+            colors = hamcolors;
+        }
+    }
+
+    if( cmapmaxval != maxval ) {
+        int i, *table;
+        pixel *newcmap;
+
+        newcmap = ppm_allocrow(colors);
+        table = makeValTable(cmapmaxval, maxval);
+        for( i = 0; i < colors; i++ )
+            PPM_ASSIGN(newcmap[i], 
+                       table[PPM_GETR(colormap[i])], 
+                       table[PPM_GETG(colormap[i])], 
+                       table[PPM_GETB(colormap[i])]);
+        free(table);
+        ppm_freerow(colormap);
+        colormap = newcmap;
+    }
+    if( sortcmap )
+        ppm_sortcolorrow(colormap, colors, PPM_STDSORT);
+
+    nPlanes = hamplanes;
+    cmapsize = colors * 3;
 
     bodysize = oldsize = rows * TOTALPLANES(nPlanes) * RowBytes(cols);
     if( DO_COMPRESS ) {
-        bodysize = do_deep_body(fp, NULL, cols, rows, maxval, bitspercolor);
+        bodysize = doHamBody(ifP, NULL, cols, rows, maxval, 
+                               hammaxval, nPlanes, colormap, colors);
+        /*bodysize = doHamBody(ifP, NULL, cols, 
+          rows, maxval, hammaxval, nPlanes, colbits, nocolor);*/
         if( bodysize > oldsize )
-            pm_message("warning - %s compression increases BODY size by %ld%%",
+            pm_message("warning - %s compression increases BODY size "
+                       "by %ld%%", 
                        cmpNAME[compmethod], 100*(bodysize-oldsize)/oldsize);
         else
             pm_message("BODY compression (%s): %ld%%", 
@@ -846,35 +1175,45 @@ ppm_to_deep(fp, cols, rows, maxval, bitspercolor)
     formsize =
         4 +                                 /* ILBM */
         4 + 4 + BitMapHeaderSize +          /* BMHD size header */
+        4 + 4 + CAMGChunkSize +             /* CAMG size viewportmodes */
+        4 + 4 + cmapsize + PAD(cmapsize) +  /* CMAP size colormap */
         4 + 4 + bodysize + PAD(bodysize) +  /* BODY size data */
-        length_of_text_chunks();
-    if( gen_camg )
-        formsize += 4 + 4 + CAMGChunkSize;  /* CAMG size viewportmodes */
+        lengthOfTextChunks();
 
-    put_big_long(ID_FORM);
-    put_big_long(formsize);
-    put_big_long(ID_ILBM);
+    pm_writebiglong(stdout, ID_FORM);
+    pm_writebiglong(stdout, formsize);
+    pm_writebiglong(stdout, ID_ILBM);
 
-    write_bmhd(cols, rows, nPlanes);
-    write_text_chunks();
-    if( gen_camg )
-        write_camg();
+    writeBmhd(cols, rows, nPlanes);
+    writeTextChunks();
+    writeCamg();       /* HAM requires CAMG chunk */
+    writeCmap(colormap, colors, maxval);
 
     /* write body */
-    put_big_long(ID_BODY);
-    put_big_long(bodysize);
+    pm_writebiglong(stdout, ID_BODY);
+    pm_writebiglong(stdout, bodysize);
     if( DO_COMPRESS )
-        write_body_rows();
+        writeBodyRows();
     else
-        do_deep_body(fp, stdout, cols, rows, maxval, bitspercolor);
+        doHamBody(ifP, stdout, cols, rows, maxval, hammaxval, 
+                  nPlanes, colormap, colors);
 }
 
 
+
+/************ deep (24-bit) ************/
+
+
+
 static long
-do_deep_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, 
-             int bitspercolor)
-{
-    register int row, col;
+doDeepBody(FILE * const ifP,
+           FILE * const ofP,
+           int    const cols,
+           int    const rows,
+           pixval const maxval, 
+           int    const bitspercolor) {
+
+    int row, col;
     pixel *pP;
     int *table = NULL;
     long bodysize = 0;
@@ -889,11 +1228,11 @@ do_deep_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval,
     if( maxval != newmaxval ) {
         pm_message("maxval is not %d - automatically rescaling colors", 
                    newmaxval);
-        table = make_val_table(maxval, newmaxval);
+        table = makeValTable(maxval, newmaxval);
     }
 
     for( row = 0; row < rows; row++ ) {
-        pP = next_pixrow(ifP, row);
+        pP = nextPixrow(ifP, row);
         if( table ) {
             for( col = 0; col < cols; col++, pP++ ) {
                 redbuf[col]     = table[PPM_GETR(*pP)];
@@ -908,14 +1247,14 @@ do_deep_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval,
                 bluebuf[col]    = PPM_GETB(*pP);
             }
         }
-        bodysize += encode_row(ofp, redbuf,   cols, bitspercolor);
-        bodysize += encode_row(ofp, greenbuf, cols, bitspercolor);
-        bodysize += encode_row(ofp, bluebuf,  cols, bitspercolor);
+        bodysize += encodeRow(ofP, redbuf,   cols, bitspercolor);
+        bodysize += encodeRow(ofP, greenbuf, cols, bitspercolor);
+        bodysize += encodeRow(ofP, bluebuf,  cols, bitspercolor);
         if( maskmethod == mskHasMask )
-            bodysize += encode_maskrow(ofp, redbuf, cols);
+            bodysize += encodeMaskrow(ofP, redbuf, cols);
     }
-    if( ofp && ODD(bodysize) )
-        put_byte(0);
+    if( ofP && ODD(bodysize) )
+        putByte(0);
 
     /* clean up */
     if( table )
@@ -928,83 +1267,78 @@ do_deep_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval,
 }
 
 
-/************ direct color ************/
-
-static long do_dcol_body      ARGS((FILE *ifP, FILE *ofp, int cols, int rows, 
-                                    pixval maxval, DirectColor *dcol));
 
 static void
-ppm_to_dcol(fp, cols, rows, maxval, dcol)
-    FILE *fp;
-    int cols, rows, maxval;
-    DirectColor *dcol;
-{
+ppmToDeep(FILE * const ifP,
+          int    const cols,
+          int    const rows,
+          int    const maxval,
+          int    const bitspercolor) {
+
     int nPlanes;
     long bodysize, oldsize, formsize;
 
     if( maskmethod == mskHasTransparentColor ) {
         pm_message("masking method '%s' not usable with deep ILBM - "
                    "using '%s' instead",
-                   mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]);
+                    mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]);
         maskmethod = mskHasMask;
     }
 
-    nPlanes = dcol->r + dcol->g + dcol->b;
+    nPlanes = 3*bitspercolor;
 
     bodysize = oldsize = rows * TOTALPLANES(nPlanes) * RowBytes(cols);
     if( DO_COMPRESS ) {
-        bodysize = do_dcol_body(fp, NULL, cols, rows, maxval, dcol);
+        bodysize = doDeepBody(ifP, NULL, cols, rows, maxval, bitspercolor);
         if( bodysize > oldsize )
             pm_message("warning - %s compression increases BODY size by %ld%%",
-                       cmpNAME[compmethod], 
-                       100*(bodysize-oldsize)/oldsize);
+                       cmpNAME[compmethod], 100*(bodysize-oldsize)/oldsize);
         else
-            pm_message("BODY compression (%s): %ld%%", cmpNAME[compmethod], 
-                       100*(oldsize-bodysize)/oldsize);
+            pm_message("BODY compression (%s): %ld%%", 
+                       cmpNAME[compmethod], 100*(oldsize-bodysize)/oldsize);
     }
 
-
     formsize =
         4 +                                 /* ILBM */
         4 + 4 + BitMapHeaderSize +          /* BMHD size header */
-        4 + 4 + DirectColorSize +           /* DCOL size dcol */
         4 + 4 + bodysize + PAD(bodysize) +  /* BODY size data */
-        length_of_text_chunks();
+        lengthOfTextChunks();
     if( gen_camg )
         formsize += 4 + 4 + CAMGChunkSize;  /* CAMG size viewportmodes */
 
-    put_big_long(ID_FORM);
-    put_big_long(formsize);
-    put_big_long(ID_ILBM);
-
-    write_bmhd(cols, rows, nPlanes);
-    write_text_chunks();
-
-    put_big_long(ID_DCOL);
-    put_big_long(DirectColorSize);
-    put_byte(dcol->r);
-    put_byte(dcol->g);
-    put_byte(dcol->b);
-    put_byte(0);    /* pad */
+    pm_writebiglong(stdout, ID_FORM);
+    pm_writebiglong(stdout, formsize);
+    pm_writebiglong(stdout, ID_ILBM);
 
+    writeBmhd(cols, rows, nPlanes);
+    writeTextChunks();
     if( gen_camg )
-        write_camg();
+        writeCamg();
 
     /* write body */
-    put_big_long(ID_BODY);
-    put_big_long(bodysize);
+    pm_writebiglong(stdout, ID_BODY);
+    pm_writebiglong(stdout, bodysize);
     if( DO_COMPRESS )
-        write_body_rows();
+        writeBodyRows();
     else
-        do_dcol_body(fp, stdout, cols, rows, maxval, dcol);
+        doDeepBody(ifP, stdout, cols, rows, maxval, bitspercolor);
 }
 
 
+
+/************ direct color ************/
+
+
+
 static long
-do_dcol_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, 
-             DirectColor *dcol)
-{
-    register int row, col;
+doDcolBody(FILE *        const ifP,
+           FILE *        const ofP,
+           int           const cols,
+           int           const rows,
+           pixval        const maxval, 
+           DirectColor * const dcol) {
+
+    int row, col;
     pixel *pP;
     long bodysize = 0;
     rawtype *redbuf, *greenbuf, *bluebuf;
@@ -1014,25 +1348,25 @@ do_dcol_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval,
     MALLOCARRAY_NOFAIL(greenbuf, cols);
     MALLOCARRAY_NOFAIL(bluebuf,  cols);
 
-    redtable   = make_val_table(maxval, pm_bitstomaxval(dcol->r));
-    greentable = make_val_table(maxval, pm_bitstomaxval(dcol->g));
-    bluetable  = make_val_table(maxval, pm_bitstomaxval(dcol->b));
+    redtable   = makeValTable(maxval, pm_bitstomaxval(dcol->r));
+    greentable = makeValTable(maxval, pm_bitstomaxval(dcol->g));
+    bluetable  = makeValTable(maxval, pm_bitstomaxval(dcol->b));
 
     for( row = 0; row < rows; row++ ) {
-        pP = next_pixrow(ifP, row);
+        pP = nextPixrow(ifP, row);
         for( col = 0; col < cols; col++, pP++ ) {
             redbuf[col]   = redtable[PPM_GETR(*pP)];
             greenbuf[col] = greentable[PPM_GETG(*pP)];
             bluebuf[col]  = bluetable[PPM_GETB(*pP)];
         }
-        bodysize += encode_row(ofp, redbuf,   cols, dcol->r);
-        bodysize += encode_row(ofp, greenbuf, cols, dcol->g);
-        bodysize += encode_row(ofp, bluebuf,  cols, dcol->b);
+        bodysize += encodeRow(ofP, redbuf,   cols, dcol->r);
+        bodysize += encodeRow(ofP, greenbuf, cols, dcol->g);
+        bodysize += encodeRow(ofP, bluebuf,  cols, dcol->b);
         if( maskmethod == mskHasMask )
-            bodysize += encode_maskrow(ofp, redbuf, cols);
+            bodysize += encodeMaskrow(ofP, redbuf, cols);
     }
-    if( ofp && ODD(bodysize) )
-        put_byte(0);
+    if( ofP && ODD(bodysize) )
+        putByte(0);
 
     /* clean up */
     free(redtable);
@@ -1046,104 +1380,91 @@ do_dcol_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval,
 }
 
 
-/************ normal colormapped ************/
-
-static long do_std_body     ARGS((FILE *ifP, FILE *ofp, int cols, int rows, 
-                                  pixval maxval, pixel *colormap, 
-                                  int colors, int nPlanes));
 
 static void
-ppm_to_std(fp, cols, rows, maxval, colormap, colors, cmapmaxval, 
-           maxcolors, nPlanes)
-    FILE *fp;
-    int cols, rows, maxval;
-    pixel *colormap;
-    int cmapmaxval, colors, maxcolors, nPlanes;
-{
-    long formsize, cmapsize, bodysize, oldsize;
+ppmToDcol(FILE *        const ifP,
+          int           const cols,
+          int           const rows,
+          int           const maxval,
+          DirectColor * const dcol) {
 
-    if( maskmethod == mskHasTransparentColor ) {
-        if( transpColor ) {
-            transpIndex = 
-                ppm_addtocolorrow(colormap, &colors, maxcolors, transpColor);
-        }
-        else
-        if( colors < maxcolors )
-            transpIndex = colors;
+    int nPlanes;
+    long bodysize, oldsize, formsize;
 
-        if( transpIndex < 0 ) {
-            pm_message("too many colors for masking method '%s' - "
-                       "using '%s' instead",
-                       mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]);
-            maskmethod = mskHasMask;
-        }
+    if( maskmethod == mskHasTransparentColor ) {
+        pm_message("masking method '%s' not usable with deep ILBM - "
+                   "using '%s' instead",
+                   mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]);
+        maskmethod = mskHasMask;
     }
 
-    if( cmapmaxval != maxval ) {
-        int i, *table;
-        pixel *newcmap;
-
-        newcmap = ppm_allocrow(colors);
-        table = make_val_table(cmapmaxval, maxval);
-        for (i = 0; i < colors; ++i)
-            PPM_ASSIGN(newcmap[i], 
-                       table[PPM_GETR(colormap[i])], 
-                       table[PPM_GETG(colormap[i])], 
-                       table[PPM_GETB(colormap[i])]);
-        free(table);
-        colormap = newcmap;
-    }
-    if( sortcmap )
-        ppm_sortcolorrow(colormap, colors, PPM_STDSORT);
+    nPlanes = dcol->r + dcol->g + dcol->b;
 
     bodysize = oldsize = rows * TOTALPLANES(nPlanes) * RowBytes(cols);
     if( DO_COMPRESS ) {
-        bodysize = do_std_body(fp, NULL, cols, rows, maxval, colormap, 
-                               colors, nPlanes);
+        bodysize = doDcolBody(ifP, NULL, cols, rows, maxval, dcol);
         if( bodysize > oldsize )
             pm_message("warning - %s compression increases BODY size by %ld%%",
-                       cmpNAME[compmethod], 100*(bodysize-oldsize)/oldsize);
+                       cmpNAME[compmethod], 
+                       100*(bodysize-oldsize)/oldsize);
         else
-            pm_message("BODY compression (%s): %ld%%", 
-                       cmpNAME[compmethod], 100*(oldsize-bodysize)/oldsize);
+            pm_message("BODY compression (%s): %ld%%", cmpNAME[compmethod], 
+                       100*(oldsize-bodysize)/oldsize);
     }
 
-    cmapsize = colors * 3;
 
     formsize =
         4 +                                 /* ILBM */
         4 + 4 + BitMapHeaderSize +          /* BMHD size header */
-        4 + 4 + cmapsize + PAD(cmapsize) +  /* CMAP size colormap */
+        4 + 4 + DirectColorSize +           /* DCOL size dcol */
         4 + 4 + bodysize + PAD(bodysize) +  /* BODY size data */
-        length_of_text_chunks();
+        lengthOfTextChunks();
     if( gen_camg )
         formsize += 4 + 4 + CAMGChunkSize;  /* CAMG size viewportmodes */
 
-    put_big_long(ID_FORM);
-    put_big_long(formsize);
-    put_big_long(ID_ILBM);
+    pm_writebiglong(stdout, ID_FORM);
+    pm_writebiglong(stdout, formsize);
+    pm_writebiglong(stdout, ID_ILBM);
+
+    writeBmhd(cols, rows, nPlanes);
+    writeTextChunks();
+
+    pm_writebiglong(stdout, ID_DCOL);
+    pm_writebiglong(stdout, DirectColorSize);
+    putByte(dcol->r);
+    putByte(dcol->g);
+    putByte(dcol->b);
+    putByte(0);    /* pad */
 
-    write_bmhd(cols, rows, nPlanes);
-    write_text_chunks();
     if( gen_camg )
-        write_camg();
-    write_cmap(colormap, colors, maxval);
+        writeCamg();
 
     /* write body */
-    put_big_long(ID_BODY);
-    put_big_long(bodysize);
+    pm_writebiglong(stdout, ID_BODY);
+    pm_writebiglong(stdout, bodysize);
     if( DO_COMPRESS )
-        write_body_rows();
+        writeBodyRows();
     else
-        do_std_body(fp, stdout, cols, rows, maxval, colormap, colors, nPlanes);
+        doDcolBody(ifP, stdout, cols, rows, maxval, dcol);
 }
 
 
+
+/************ normal colormapped ************/
+
+
+
 static long
-do_std_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval,
-            pixel *colormap, int colors, int nPlanes)
-{
-    register int row, col, i;
+doStdBody(FILE *  const ifP,
+          FILE *  const ofP,
+          int     const cols,
+          int     const rows,
+          pixval  const maxval,
+          pixel * const colormap,
+          int     const colors,
+          int     const nPlanes) {
+
+    int row, col, i;
     pixel *pP;
     rawtype *raw_rowbuf;
     ppm_fs_info *fi = NULL;
@@ -1158,7 +1479,7 @@ do_std_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval,
 
     for( row = 0; row < rows; row++ ) {
         pixel *prow;
-        prow = next_pixrow(ifP, row);
+        prow = nextPixrow(ifP, row);
 
         for( col = ppm_fs_startrow(fi, prow); 
              col < cols; 
@@ -1188,13 +1509,13 @@ do_std_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval,
             raw_rowbuf[col] = i;
             ppm_fs_update(fi, col, &colormap[i]);
         }
-        bodysize += encode_row(ofp, raw_rowbuf, cols, nPlanes);
+        bodysize += encodeRow(ofP, raw_rowbuf, cols, nPlanes);
         if( maskmethod == mskHasMask )
-            bodysize += encode_maskrow(ofp, raw_rowbuf, cols);
+            bodysize += encodeMaskrow(ofP, raw_rowbuf, cols);
         ppm_fs_endrow(fi);
     }
-    if( ofp && ODD(bodysize) )
-        put_byte(0);
+    if( ofP && ODD(bodysize) )
+        putByte(0);
 
     /* clean up */
     ppm_freecolorhash(cht);
@@ -1204,14 +1525,117 @@ do_std_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval,
     return bodysize;
 }
 
+
+
+static void
+ppmToStd(FILE *  const ifP,
+         int     const cols,
+         int     const rows,
+         int     const maxval,
+         pixel * const colormapArg,
+         int     const colorsArg,
+         int     const cmapmaxvalArg, 
+         int     const maxcolors,
+         int     const nPlanes) {
+
+    long formsize, cmapsize, bodysize, oldsize;
+
+    int colors;
+    pixel * colormap;
+    int cmapmaxval;
+
+    colors = colorsArg;  /* initial value */
+    colormap = colormapArg;  /* initial value */
+    cmapmaxval = cmapmaxvalArg;  /* initial value */
+
+    if( maskmethod == mskHasTransparentColor ) {
+        if( transpColor ) {
+            transpIndex = 
+                ppm_addtocolorrow(colormap, &colors, maxcolors, transpColor);
+        }
+        else
+        if( colors < maxcolors )
+            transpIndex = colors;
+
+        if( transpIndex < 0 ) {
+            pm_message("too many colors for masking method '%s' - "
+                       "using '%s' instead",
+                       mskNAME[mskHasTransparentColor], mskNAME[mskHasMask]);
+            maskmethod = mskHasMask;
+        }
+    }
+
+    if( cmapmaxval != maxval ) {
+        int i, *table;
+        pixel *newcmap;
+
+        newcmap = ppm_allocrow(colors);
+        table = makeValTable(cmapmaxval, maxval);
+        for (i = 0; i < colors; ++i)
+            PPM_ASSIGN(newcmap[i], 
+                       table[PPM_GETR(colormap[i])], 
+                       table[PPM_GETG(colormap[i])], 
+                       table[PPM_GETB(colormap[i])]);
+        free(table);
+        colormap = newcmap;
+    }
+    if( sortcmap )
+        ppm_sortcolorrow(colormap, colors, PPM_STDSORT);
+
+    bodysize = oldsize = rows * TOTALPLANES(nPlanes) * RowBytes(cols);
+    if( DO_COMPRESS ) {
+        bodysize = doStdBody(ifP, NULL, cols, rows, maxval, colormap, 
+                             colors, nPlanes);
+        if( bodysize > oldsize )
+            pm_message("warning - %s compression increases BODY size by %ld%%",
+                       cmpNAME[compmethod], 100*(bodysize-oldsize)/oldsize);
+        else
+            pm_message("BODY compression (%s): %ld%%", 
+                       cmpNAME[compmethod], 100*(oldsize-bodysize)/oldsize);
+    }
+
+    cmapsize = colors * 3;
+
+    formsize =
+        4 +                                 /* ILBM */
+        4 + 4 + BitMapHeaderSize +          /* BMHD size header */
+        4 + 4 + cmapsize + PAD(cmapsize) +  /* CMAP size colormap */
+        4 + 4 + bodysize + PAD(bodysize) +  /* BODY size data */
+        lengthOfTextChunks();
+    if( gen_camg )
+        formsize += 4 + 4 + CAMGChunkSize;  /* CAMG size viewportmodes */
+
+    pm_writebiglong(stdout, ID_FORM);
+    pm_writebiglong(stdout, formsize);
+    pm_writebiglong(stdout, ID_ILBM);
+
+    writeBmhd(cols, rows, nPlanes);
+    writeTextChunks();
+    if( gen_camg )
+        writeCamg();
+    writeCmap(colormap, colors, maxval);
+
+    /* write body */
+    pm_writebiglong(stdout, ID_BODY);
+    pm_writebiglong(stdout, bodysize);
+    if( DO_COMPRESS )
+        writeBodyRows();
+    else
+        doStdBody(ifP, stdout, cols, rows, maxval, colormap, colors, nPlanes);
+}
+
+
+
 /************ RGB8 ************/
 
+
+
 static void
-ppm_to_rgb8(ifP, cols, rows, maxval)
-    FILE *ifP;
-    int cols, rows;
-    int maxval;
-{
+ppmToRgb8(FILE * const ifP,
+          int    const cols,
+          int    const rows,
+          int    const maxval) {
+
     long bodysize, oldsize, formsize;
     pixel *pP;
     int *table = NULL;
@@ -1224,13 +1648,13 @@ ppm_to_rgb8(ifP, cols, rows, maxval)
 
     if( maxval != 255 ) {
         pm_message("maxval is not 255 - automatically rescaling colors");
-        table = make_val_table(maxval, 255);
+        table = makeValTable(maxval, 255);
     }
 
     oldsize = cols * rows * 4;
     bodysize = 0;
     for( row = 0; row < rows; row++ ) {
-        pP = next_pixrow(ifP, row);
+        pP = nextPixrow(ifP, row);
         compr_len = 0;
         for( col1 = 0; col1 < cols; col1 = col2 ) {
             col2 = col1 + 1;
@@ -1264,7 +1688,7 @@ ppm_to_rgb8(ifP, cols, rows, maxval)
                 ++compr_len;
             }
         }
-        store_bodyrow(compr_row, compr_len);
+        storeBodyrow(compr_row, compr_len);
         bodysize += compr_len;
     }
 
@@ -1275,31 +1699,34 @@ ppm_to_rgb8(ifP, cols, rows, maxval)
         4 + 4 + BitMapHeaderSize +          /* BMHD size header */
         4 + 4 + CAMGChunkSize +             /* CAMG size viewportmode */
         4 + 4 + bodysize + PAD(bodysize) +  /* BODY size data */
-        length_of_text_chunks();
+        lengthOfTextChunks();
 
     /* write header */
-    put_big_long(ID_FORM);
-    put_big_long(formsize);
-    put_big_long(ID_RGB8);
+    pm_writebiglong(stdout, ID_FORM);
+    pm_writebiglong(stdout, formsize);
+    pm_writebiglong(stdout, ID_RGB8);
 
-    write_bmhd(cols, rows, 25);
-    write_text_chunks();
-    write_camg();               /* RGB8 requires CAMG chunk */
+    writeBmhd(cols, rows, 25);
+    writeTextChunks();
+    writeCamg();               /* RGB8 requires CAMG chunk */
 
-    put_big_long(ID_BODY);
-    put_big_long(bodysize);
-    write_body_rows();
+    pm_writebiglong(stdout, ID_BODY);
+    pm_writebiglong(stdout, bodysize);
+    writeBodyRows();
 }
 
 
+
 /************ RGBN ************/
 
+
+
 static void
-ppm_to_rgbn(ifP, cols, rows, maxval)
-    FILE *ifP;
-    int cols, rows;
-    int maxval;
-{
+ppmToRgbn(FILE * const ifP,
+          int    const cols,
+          int    const rows,
+          int    const maxval) {
+
     long bodysize, oldsize, formsize;
     pixel *pP;
     int *table = NULL;
@@ -1312,13 +1739,13 @@ ppm_to_rgbn(ifP, cols, rows, maxval)
 
     if( maxval != 15 ) {
         pm_message("maxval is not 15 - automatically rescaling colors");
-        table = make_val_table(maxval, 15);
+        table = makeValTable(maxval, 15);
     }
 
     oldsize = cols * rows * 2;
     bodysize = 0;
     for( row = 0; row < rows; row++ ) {
-        pP = next_pixrow(ifP, row);
+        pP = nextPixrow(ifP, row);
         compr_len = 0;
         for( col1 = 0; col1 < cols; col1 = col2 ) {
             col2 = col1 + 1;
@@ -1368,7 +1795,7 @@ ppm_to_rgbn(ifP, cols, rows, maxval)
                 }
             }
         }
-        store_bodyrow(compr_row, compr_len);
+        storeBodyrow(compr_row, compr_len);
         bodysize += compr_len;
     }
 
@@ -1379,40 +1806,43 @@ ppm_to_rgbn(ifP, cols, rows, maxval)
         4 + 4 + BitMapHeaderSize +          /* BMHD size header */
         4 + 4 + CAMGChunkSize +             /* CAMG size viewportmode */
         4 + 4 + bodysize + PAD(bodysize) +  /* BODY size data */
-        length_of_text_chunks();
+        lengthOfTextChunks();
 
     /* write header */
-    put_big_long(ID_FORM);
-    put_big_long(formsize);
-    put_big_long(ID_RGBN);
+    pm_writebiglong(stdout, ID_FORM);
+    pm_writebiglong(stdout, formsize);
+    pm_writebiglong(stdout, ID_RGBN);
 
-    write_bmhd(cols, rows, 13);
-    write_text_chunks();
-    write_camg();               /* RGBN requires CAMG chunk */
+    writeBmhd(cols, rows, 13);
+    writeTextChunks();
+    writeCamg();               /* RGBN requires CAMG chunk */
 
-    put_big_long(ID_BODY);
-    put_big_long(bodysize);
-    write_body_rows();
+    pm_writebiglong(stdout, ID_BODY);
+    pm_writebiglong(stdout, bodysize);
+    writeBodyRows();
 }
 
 
+
 /************ multipalette ************/
 
+
+
 #ifdef ILBM_PCHG
 static pixel *ppmslice[2];  /* need 2 for laced ILBMs, else 1 */
 
-void ppm_to_pchg()
-{
+void
+ppmToPchg() {
 /*
     read first slice
     build a colormap from this slice
-    select upto <maxcolors> colors
+    select up to <maxcolors> colors
     build colormap from selected colors
     map slice to colormap
     write slice
     while( !finished ) {
         read next slice
-        compute distances for each pixel and select upto
+        compute distances for each pixel and select up to
             <maxchangesperslice> unused colors in this slice
         modify selected colors to the ones with maximum(?) distance
         map slice to colormap
@@ -1431,432 +1861,6 @@ void ppm_to_pchg()
 #endif
 
 
-/************ ILBM functions ************/
-
-static int
-length_of_text_chunks ARGS((void))
-{
-    int len, n;
-
-    len = 0;
-    if( anno_chunk ) {
-        n = strlen(anno_chunk);
-        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
-    }
-    if( auth_chunk ) {
-        n = strlen(auth_chunk);
-        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
-    }
-    if( name_chunk ) {
-        n = strlen(name_chunk);
-        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
-    }
-    if( copyr_chunk ) {
-        n = strlen(copyr_chunk);
-        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
-    }
-    if( text_chunk ) {
-        n = strlen(text_chunk);
-        len += 4 + 4 + n + PAD(n);      /* ID chunksize text */
-    }
-    return len;
-}
-
-
-static void
-write_text_chunks ARGS((void))
-{
-    int n;
-
-    if( anno_chunk ) {
-        n = strlen(anno_chunk);
-        put_big_long(ID_ANNO);
-        put_big_long(n);
-        write_bytes((unsigned char *)anno_chunk, n);
-        if( ODD(n) )
-            put_byte(0);
-    }
-    if( auth_chunk ) {
-        n = strlen(auth_chunk);
-        put_big_long(ID_AUTH);
-        put_big_long(n);
-        write_bytes((unsigned char *)auth_chunk, n);
-        if( ODD(n) )
-            put_byte(0);
-    }
-    if( copyr_chunk ) {
-        n = strlen(copyr_chunk);
-        put_big_long(ID_copy);
-        put_big_long(n);
-        write_bytes((unsigned char *)copyr_chunk, n);
-        if( ODD(n) )
-            put_byte(0);
-    }
-    if( name_chunk ) {
-        n = strlen(name_chunk);
-        put_big_long(ID_NAME);
-        put_big_long(n);
-        write_bytes((unsigned char *)name_chunk, n);
-        if( ODD(n) )
-            put_byte(0);
-    }
-    if( text_chunk ) {
-        n = strlen(text_chunk);
-        put_big_long(ID_TEXT);
-        put_big_long(n);
-        write_bytes((unsigned char *)text_chunk, n);
-        if( ODD(n) )
-            put_byte(0);
-    }
-}
-
-
-static void
-write_cmap(colormap, colors, maxval)
-    pixel *colormap;
-    int colors, maxval;
-{
-    int cmapsize, i;
-
-    cmapsize = 3 * colors;
-
-    /* write colormap */
-    put_big_long(ID_CMAP);
-    put_big_long(cmapsize);
-    if( maxval != MAXCOLVAL ) {
-        int *table;
-        pm_message("maxval is not %d - automatically rescaling colors", 
-                   MAXCOLVAL);
-        table = make_val_table(maxval, MAXCOLVAL);
-        for( i = 0; i < colors; i++ ) {
-            put_byte(table[PPM_GETR(colormap[i])]);
-            put_byte(table[PPM_GETG(colormap[i])]);
-            put_byte(table[PPM_GETB(colormap[i])]);
-        }
-        free(table);
-    }
-    else {
-        for( i = 0; i < colors; i++ ) {
-            put_byte(PPM_GETR(colormap[i]));
-            put_byte(PPM_GETG(colormap[i]));
-            put_byte(PPM_GETB(colormap[i]));
-        }
-    }
-    if( ODD(cmapsize) )
-        put_byte(0);
-}
-
-
-static void
-write_bmhd(cols, rows, nPlanes)
-    int cols, rows, nPlanes;
-{
-    unsigned char xasp = 10, yasp = 10;
-
-    if( viewportmodes & vmLACE )
-        xasp *= 2;
-    if( viewportmodes & vmHIRES )
-        yasp *= 2;
-
-    put_big_long(ID_BMHD);
-    put_big_long(BitMapHeaderSize);
-
-    put_big_short(cols);
-    put_big_short(rows);
-    put_big_short(0);                       /* x-offset */
-    put_big_short(0);                       /* y-offset */
-    put_byte(nPlanes);                      /* no of planes */
-    put_byte(maskmethod);                   /* masking */
-    put_byte(compmethod);                   /* compression */
-    put_byte(BMHD_FLAGS_CMAPOK);            /* flags */
-    if( maskmethod == mskHasTransparentColor )
-        put_big_short(transpIndex);
-    else
-        put_big_short(0);
-    put_byte(xasp);                         /* x-aspect */
-    put_byte(yasp);                         /* y-aspect */
-    put_big_short(cols);                    /* pageWidth */
-    put_big_short(rows);                    /* pageHeight */
-}
-
-
-/* encode algorithm by Johan Widen (jw@jwdata.se) */
-static const unsigned char bitmask[] = {1, 2, 4, 8, 16, 32, 64, 128};
-
-static long
-encode_row(outfile, rawrow, cols, nPlanes)
-    FILE *outfile;  /* if non-NULL, write uncompressed row to this file */
-    rawtype *rawrow;
-    int cols, nPlanes;
-{
-    int plane, bytes;
-    long retbytes = 0;
-
-    bytes = RowBytes(cols);
-
-    /* Encode and write raw bytes in plane-interleaved form. */
-    for( plane = 0; plane < nPlanes; plane++ ) {
-        register int col, cbit;
-        register rawtype *rp;
-        register unsigned char *cp;
-        int mask;
-
-        mask = 1 << plane;
-        cbit = -1;
-        cp = coded_rowbuf-1;
-        rp = rawrow;
-        for( col = 0; col < cols; col++, cbit--, rp++ ) {
-            if( cbit < 0 ) {
-                cbit = 7;
-                *++cp = 0;
-            }
-            if( *rp & mask )
-                *cp |= bitmask[cbit];
-        }
-        if( outfile ) {
-            write_bytes(coded_rowbuf, bytes);
-            retbytes += bytes;
-        }
-        else
-            retbytes += compress_row(bytes);
-    }
-    return retbytes;
-}
-
-
-static long
-encode_maskrow(ofp, rawrow, cols)
-    FILE *ofp;
-    rawtype *rawrow;
-    int cols;
-{
-    int col;
-
-    for( col = 0; col < cols; col++ ) {
-        if( maskrow[col] == PBM_BLACK )
-            rawrow[col] = 1;
-        else
-            rawrow[col] = 0;
-    }
-    return encode_row(ofp, rawrow, cols, 1);
-}
-
-
-static int
-compress_row(bytes)
-    int bytes;
-{
-    int newbytes;
-
-    switch( compmethod ) {
-        case cmpByteRun1:
-            newbytes = runbyte1(bytes);
-            break;
-        default:
-            pm_error("compress_row(): unknown compression method %d", 
-                     compmethod);
-    }
-    store_bodyrow(compr_rowbuf, newbytes);
-
-    return newbytes;
-}
-
-
-static void
-store_bodyrow(row, len)
-    unsigned char *row;
-    int len;
-{
-    int idx = cur_block->used;
-    if( idx >= ROWS_PER_BLOCK ) {
-        MALLOCVAR_NOFAIL(cur_block->next);
-        cur_block = cur_block->next;
-        cur_block->used = idx = 0;
-        cur_block->next = NULL;
-    }
-    MALLOCARRAY_NOFAIL(cur_block->row[idx], len);
-    cur_block->len[idx] = len;
-    memcpy(cur_block->row[idx], row, len);
-    cur_block->used++;
-}
-
-
-static void
-write_body_rows ARGS((void))
-{
-    bodyblock *b;
-    int i;
-    long total = 0;
-
-    for( b = &firstblock; b != NULL; b = b->next ) {
-        for( i = 0; i < b->used; i++ ) {
-            write_bytes(b->row[i], b->len[i]);
-            total += b->len[i];
-        }
-    }
-    if( ODD(total) )
-        put_byte(0);
-}
-
-
-static void
-write_camg ARGS((void))
-{
-    put_big_long(ID_CAMG);
-    put_big_long(CAMGChunkSize);
-    put_big_long(viewportmodes);
-}
-
-
-/************ compression ************/
-
-
-/* runbyte1 algorithm by Robert A. Knop (rknop@mop.caltech.edu) */
-static int
-runbyte1(size)
-   int size;
-{
-    int in,out,count,hold;
-    register unsigned char *inbuf = coded_rowbuf;
-    register unsigned char *outbuf = compr_rowbuf;
-
-
-    in=out=0;
-    while( in<size ) {
-        if( (in<size-1) && (inbuf[in]==inbuf[in+1]) ) {    
-            /*Begin replicate run*/
-            for( count=0, hold=in; 
-                 in < size && inbuf[in] == inbuf[hold] && count < 128; 
-                 in++, count++)
-                ;
-            outbuf[out++]=(unsigned char)(char)(-count+1);
-            outbuf[out++]=inbuf[hold];
-        }
-        else {  /*Do a literal run*/
-            hold=out; out++; count=0;
-            while( ((in>=size-2)&&(in<size)) || 
-                   ((in<size-2) && ((inbuf[in]!=inbuf[in+1])
-                                    ||(inbuf[in]!=inbuf[in+2]))) ) {
-                outbuf[out++]=inbuf[in++];
-                if( ++count>=128 )
-                    break;
-            }
-            outbuf[hold]=count-1;
-        }
-    }
-    return(out);
-}
-
-
-
-/************ other utility functions ************/
-
-static void
-put_big_short(short s)
-{
-    if ( pm_writebigshort( stdout, s ) == -1 )
-        pm_error( "write error" );
-}
-
-
-static void
-put_big_long(l)
-    long l;
-{
-    if ( pm_writebiglong( stdout, l ) == -1 )
-        pm_error( "write error" );
-}
-
-
-static void
-write_bytes(buffer, bytes)
-    unsigned char *buffer;
-    int bytes;
-{
-    if( fwrite(buffer, 1, bytes, stdout) != bytes )
-        pm_error("write error");
-}
-
-
-static int *
-make_val_table(oldmaxval, newmaxval)
-    int oldmaxval, newmaxval;
-{
-    unsigned int i;
-    int * table;
-
-    MALLOCARRAY_NOFAIL(table, oldmaxval + 1);
-    for (i = 0; i <= oldmaxval; ++i)
-        table[i] = ROUNDDIV(i * newmaxval, oldmaxval);
-
-    return table;
-}
-
-
-
-static int  gFormat;
-static int  gCols;
-static int  gMaxval;
-
-static void
-init_read(fp, colsP, rowsP, maxvalP, formatP, readall)
-    FILE *fp;
-    int *colsP, *rowsP;
-    pixval *maxvalP;
-    int *formatP;
-    int readall;
-{
-    ppm_readppminit(fp, colsP, rowsP, maxvalP, formatP);
-
-    if( *rowsP >INT16MAX || *colsP >INT16MAX )
-      pm_error ("Input image is too large.");
-
-    if( readall ) {
-        int row;
-
-        pixels = ppm_allocarray(*colsP, *rowsP);
-        for( row = 0; row < *rowsP; row++ )
-            ppm_readppmrow(fp, pixels[row], *colsP, *maxvalP, *formatP);
-        /* pixels = ppm_readppm(fp, colsP, rowsP, maxvalP); */
-    }
-    else {
-        pixrow = ppm_allocrow(*colsP);
-    }
-    gCols = *colsP;
-    gMaxval = *maxvalP;
-    gFormat = *formatP;
-}
-
-
-static pixel *
-next_pixrow(fp, row)
-    FILE *fp;
-    int row;
-{
-    if( pixels )
-        pixrow = pixels[row];
-    else {
-        ppm_readppmrow(fp, pixrow, gCols, gMaxval, gFormat);
-    }
-    if( maskrow ) {
-        int col;
-
-        if( maskfile )
-            pbm_readpbmrow(maskfile, maskrow, maskcols, maskformat);
-        else {
-            for( col = 0; col < gCols; col++ )
-                maskrow[col] = PBM_BLACK;
-        }
-        if( transpColor ) {
-            for( col = 0; col < gCols; col++ )
-                if( PPM_EQUAL(pixrow[col], *transpColor) )
-                    maskrow[col] = PBM_WHITE;
-        }
-    }
-    return pixrow;
-}
-
-
 
 int
 main(int argc, char ** argv) {
@@ -1902,7 +1906,7 @@ main(int argc, char ** argv) {
             pm_keymatch(argv[argn], "-mp", 3) ) {
             if( ++argn >= argc )
                 pm_error("-maxplanes requires a value");
-            maxplanes = get_int_val(argv[argn], argv[argn-1], 1, MAXPLANES);
+            maxplanes = getIntVal(argv[argn], argv[argn-1], 1, MAXPLANES);
             fixplanes = 0;
         }
         else
@@ -1910,7 +1914,7 @@ main(int argc, char ** argv) {
             pm_keymatch(argv[argn], "-fp", 3) ) {
             if( ++argn >= argc )
                 pm_error("-fixplanes requires a value");
-            fixplanes = get_int_val(argv[argn], argv[argn-1], 1, MAXPLANES);
+            fixplanes = getIntVal(argv[argn], argv[argn-1], 1, MAXPLANES);
             maxplanes = fixplanes;
         }
         else
@@ -1923,7 +1927,7 @@ main(int argc, char ** argv) {
         if( pm_keymatch(argv[argn], "-mmethod", 3) ) {
             if( ++argn >= argc )
                 pm_error("-mmethod requires a value");
-            maskmethod = get_mask_type(argv[argn]);
+            maskmethod = getMaskType(argv[argn]);
             switch( maskmethod ) {
                 case mskNone:
                 case mskHasMask:
@@ -1985,7 +1989,8 @@ main(int argc, char ** argv) {
             if( ++argn >= argc )
                 pm_error("-camg requires a value");
             value = strtol(argv[argn], &tail, 16);
-            /* TODO: should do some error checking here */
+            if(argv[argn] == tail)
+                pm_error("-camg requires a value");
             viewportmodes |= value;
             gen_camg = 1;
         }
@@ -2003,14 +2008,14 @@ main(int argc, char ** argv) {
         if( pm_keymatch(argv[argn], "-hamplanes", 5) ) {
             if( ++argn >= argc )
                 pm_error("-hamplanes requires a value");
-            hamplanes = get_int_val(argv[argn], argv[argn-1], 3, HAMMAXPLANES);
+            hamplanes = getIntVal(argv[argn], argv[argn-1], 3, HAMMAXPLANES);
         }
         else
         if( pm_keymatch(argv[argn], "-hambits", 5) ) {
             if( ++argn >= argc )
                 pm_usage("-hambits requires a value");
             hamplanes = 
-                get_int_val(argv[argn], argv[argn-1], 3, HAMMAXPLANES-2) +2;
+                getIntVal(argv[argn], argv[argn-1], 3, HAMMAXPLANES-2) +2;
         }
         else
         if( pm_keymatch(argv[argn], "-ham6", 5) ) {
@@ -2026,7 +2031,7 @@ main(int argc, char ** argv) {
         if( pm_keymatch(argv[argn], "-hammap", 5) ) {
             if( ++argn >= argc )
                 pm_error("-hammap requires a value");
-            hammapmode = get_hammap_mode(argv[argn]);
+            hammapmode = getHammapMode(argv[argn]);
         }
         else
         if( pm_keymatch(argv[argn], "-hamif", 5) )
@@ -2068,7 +2073,7 @@ main(int argc, char ** argv) {
         if( pm_keymatch(argv[argn], "-deepplanes", 6) ) {
             if( ++argn >= argc )
                 pm_error("-deepplanes requires a value");
-            deepbits = get_int_val(argv[argn], argv[argn-1], 3, 3*MAXPLANES);
+            deepbits = getIntVal(argv[argn], argv[argn-1], 3, 3*MAXPLANES);
             if( deepbits % 3 != 0 )
                 pm_error("option \"%s\" argument value must be divisible by 3",
                          argv[argn-1]);
@@ -2078,7 +2083,7 @@ main(int argc, char ** argv) {
         if( pm_keymatch(argv[argn], "-deepbits", 6) ) {
             if( ++argn >= argc )
                 pm_error("-deepbits requires a value");
-            deepbits = get_int_val(argv[argn], argv[argn-1], 1, MAXPLANES);
+            deepbits = getIntVal(argv[argn], argv[argn-1], 1, MAXPLANES);
         }
         else
         if( pm_keymatch(argv[argn], "-deepif", 6) )
@@ -2117,9 +2122,9 @@ main(int argc, char ** argv) {
             pm_keymatch(argv[argn], "-dcplanes", 4) ) {
             if( argc - argn < 4 )
                 pm_error("-dcbits requires 4 arguments");
-            dcol.r = get_int_val(argv[argn+1], argv[argn], 1, MAXPLANES);
-            dcol.g = get_int_val(argv[argn+2], argv[argn], 1, MAXPLANES);
-            dcol.b = get_int_val(argv[argn+3], argv[argn], 1, MAXPLANES);
+            dcol.r = getIntVal(argv[argn+1], argv[argn], 1, MAXPLANES);
+            dcol.g = getIntVal(argv[argn+2], argv[argn], 1, MAXPLANES);
+            dcol.b = getIntVal(argv[argn+3], argv[argn], 1, MAXPLANES);
             argn += 3;
         }
         else
@@ -2146,7 +2151,7 @@ main(int argc, char ** argv) {
         if( pm_keymatch(argv[argn], "-cmethod", 4) ) {
             if( ++argn >= argc )
                 pm_error("-cmethod requires a value");
-            compmethod = get_compr_method(argv[argn]);
+            compmethod = getComprMethod(argv[argn]);
         }
         else
         if( pm_keymatch(argv[argn], "-floyd", 3) || 
@@ -2205,22 +2210,22 @@ main(int argc, char ** argv) {
     switch(forcemode) {
         case MODE_HAM:
             if (hammapmode == HAMMODE_RGB4 || hammapmode == HAMMODE_RGB5)
-                init_read(ifP, &cols, &rows, &maxval, &format, 1);
+                initRead(ifP, &cols, &rows, &maxval, &format, 1);
             else
-                init_read(ifP, &cols, &rows, &maxval, &format, 0);
+                initRead(ifP, &cols, &rows, &maxval, &format, 0);
             break;
         case MODE_DCOL:
         case MODE_DEEP:
             mapfile = NULL;
-            init_read(ifP, &cols, &rows, &maxval, &format, 0);
+            initRead(ifP, &cols, &rows, &maxval, &format, 0);
             break;
         case MODE_RGB8:
             mapfile = NULL;
-            init_read(ifP, &cols, &rows, &maxval, &format, 0);
+            initRead(ifP, &cols, &rows, &maxval, &format, 0);
             break;
         case MODE_RGBN:
             mapfile = NULL;
-            init_read(ifP, &cols, &rows, &maxval, &format, 0);
+            initRead(ifP, &cols, &rows, &maxval, &format, 0);
             break;
         case MODE_CMAP:
             /* Figure out the colormap. */
@@ -2234,9 +2239,9 @@ main(int argc, char ** argv) {
             break;
         default:
             if (mapfile)
-                init_read(ifP, &cols, &rows, &maxval, &format, 0);
+                initRead(ifP, &cols, &rows, &maxval, &format, 0);
             else {
-                init_read(ifP, &cols, &rows, &maxval, &format, 1);  
+                initRead(ifP, &cols, &rows, &maxval, &format, 1);  
                     /* read file into memory */
                 pm_message("computing colormap...");
                 colormap = 
@@ -2250,7 +2255,7 @@ main(int argc, char ** argv) {
                         nPlanes = fixplanes;
                 } else {  /* too many colors */
                     mode = ifmode;
-                    report_too_many_colors(ifmode, maxplanes, hamplanes,
+                    reportTooManyColors(ifmode, maxplanes, hamplanes,
                                            dcol, deepbits );
                 }
             }
@@ -2294,37 +2299,40 @@ main(int argc, char ** argv) {
         for (i = 0; i < RowBytes(cols); ++i)
             coded_rowbuf[i] = 0;
         if (DO_COMPRESS)
-            MALLOCARRAY_NOFAIL(compr_rowbuf, WORSTCOMPR(RowBytes(cols)));
+            pm_rlenc_allocoutbuf(&compr_rowbuf, RowBytes(cols), PM_RLE_PACKBITS);
     }
     
     switch (mode) {
         case MODE_HAM:
             viewportmodes |= vmHAM;
-            ppm_to_ham(ifP, cols, rows, maxval, 
-                       colormap, colors, cmapmaxval, hamplanes);
+            ppmToHam(ifP, cols, rows, maxval, 
+                     colormap, colors, cmapmaxval, hamplanes);
             break;
         case MODE_DEEP:
-            ppm_to_deep(ifP, cols, rows, maxval, deepbits);
+            ppmToDeep(ifP, cols, rows, maxval, deepbits);
             break;
         case MODE_DCOL:
-            ppm_to_dcol(ifP, cols, rows, maxval, &dcol);
+            ppmToDcol(ifP, cols, rows, maxval, &dcol);
             break;
         case MODE_RGB8:
-            ppm_to_rgb8(ifP, cols, rows, maxval);
+            ppmToRgb8(ifP, cols, rows, maxval);
             break;
         case MODE_RGBN:
-            ppm_to_rgbn(ifP, cols, rows, maxval);
+            ppmToRgbn(ifP, cols, rows, maxval);
             break;
         case MODE_CMAP:
-            ppm_to_cmap(colormap, colors, cmapmaxval);
+            ppmToCmap(colormap, colors, cmapmaxval);
             break;
         default:
             if (mapfile == NULL)
                 floyd = 0;          /* would only slow down conversion */
-            ppm_to_std(ifP, cols, rows, maxval, colormap, colors, 
-                       cmapmaxval, MAXCOLORS, nPlanes);
+            ppmToStd(ifP, cols, rows, maxval, colormap, colors, 
+                     cmapmaxval, MAXCOLORS, nPlanes);
             break;
     }
     pm_close(ifP);
     return 0;
 }
+
+
+
diff --git a/converter/ppm/ppmtompeg/BUGS b/converter/ppm/ppmtompeg/BUGS
index 64269dfb..9724014e 100644
--- a/converter/ppm/ppmtompeg/BUGS
+++ b/converter/ppm/ppmtompeg/BUGS
@@ -14,7 +14,7 @@ Known BUGS:
      REFERENCE_FRAME	DECODED
    does not work
 
-5. Cannot use both STDIN and CDL_FILEs (ok, since it doesnt make sense...)
+5. Cannot use both STDIN and CDL_FILEs (ok, since it doesn't make sense...)
 
 6. <fixed>
 
diff --git a/converter/ppm/ppmtompeg/CHANGES b/converter/ppm/ppmtompeg/CHANGES
index fffa7a65..efb8fdcf 100644
--- a/converter/ppm/ppmtompeg/CHANGES
+++ b/converter/ppm/ppmtompeg/CHANGES
@@ -28,7 +28,7 @@ Changes chronology
 	- non-integer frame rates now work for all machines
 	- fixed parsing of -mv_histogram
 	- fixed numPadding bug (file name problem)
-	- fixed full pixel assertation bug
+	- fixed full pixel assertion bug
         - corrected ASPECT_RATIO bug (was forced to 1)
 	- buffer size is now set correctly
         - complains when file is too small
diff --git a/converter/ppm/ppmtompeg/Makefile b/converter/ppm/ppmtompeg/Makefile
index a1004fdd..eeab9727 100644
--- a/converter/ppm/ppmtompeg/Makefile
+++ b/converter/ppm/ppmtompeg/Makefile
@@ -31,14 +31,17 @@ endif
 #	1) long's are 32 bits and
 #	2) int's are not
 #
-# if you are using a non-ANSI compiler, then use:
-#	-DNON_ANSI_COMPILER
-#
 # one other option:
 #	-DHEINOUS_DEBUG_MODE
 #
 
-MP_BASE_OBJS = mfwddct.o postdct.o huff.o bitio.o mheaders.o
+MP_BASE_OBJS = \
+  mfwddct.o \
+  postdct.o \
+  huff.o \
+  bitio.o \
+  mheaders.o \
+
 MP_ENCODE_OBJS = \
   frames.o \
   iframe.o \
@@ -46,12 +49,25 @@ MP_ENCODE_OBJS = \
   bframe.o \
   psearch.o \
   bsearch.o \
-  block.o 
-
-MP_OTHER_OBJS = mpeg.o subsample.o param.o rgbtoycc.o \
-	readframe.o combine.o jrevdct.o frame.o fsize.o frametype.o \
-	specifics.o rate.o opts.o input.o
-ifeq ($(OMIT_NETWORK),y)
+  block.o \
+
+MP_OTHER_OBJS = \
+  mpeg.o \
+  subsample.o \
+  param.o \
+  rgbtoycc.o \
+  readframe.o \
+  combine.o \
+  jrevdct.o \
+  frame.o \
+  fsize.o \
+  frametype.o \
+  specifics.o \
+  rate.o \
+  opts.o \
+  input.o \
+
+ifeq ($(OMIT_NETWORK),Y)
   MP_OTHER_OBJS += noparallel.o
 else
   MP_OTHER_OBJS += parallel.o psocket.o
@@ -62,14 +78,19 @@ else
   MP_OTHER_OBJS += gethostname.o
 endif
 
-NONMAIN_OBJS = $(MP_BASE_OBJS) $(MP_OTHER_OBJS) $(MP_ENCODE_OBJS) \
-	      	$(JPEG_MODULE).o
-OBJECTS = ppmtompeg.o $(NONMAIN_OBJS)
-MERGE_OBJECTS = ppmtompeg.o2 $(NONMAIN_OBJS)
+ADDL_OBJECTS = \
+  $(MP_BASE_OBJS) \
+  $(MP_OTHER_OBJS) \
+  $(MP_ENCODE_OBJS) \
+  $(JPEG_MODULE).o \
+
+OBJECTS = ppmtompeg.o $(ADDL_OBJECTS)
+MERGE_OBJECTS = ppmtompeg.o2 $(ADDL_OBJECTS)
 MP_INCLUDE = mproto.h mtypes.h huff.h bitio.h
 MP_MISC = Makefile huff.table parse_huff.pl
 
-BINARIES = ppmtompeg
+PORTBINARIES = ppmtompeg
+BINARIES = $(PORTBINARIES)
 MERGEBINARIES = $(BINARIES)
 SCRIPTS = 
 
@@ -84,18 +105,14 @@ else
   LIBOPTR =
 endif
 
-ppmtompeg: $(OBJECTS) $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ \
-          $(OBJECTS) $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR) $(JPEGLIBX)) \
-	  $(NETWORKLD) $(MATHLIB) $(LDFLAGS) $(LDLIBS) \
-	  $(RPATH) $(LADD)
-
-profile: $(OBJECTS) $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ -Bstatic -pg \
-          $(OBJECTS) $(shell $(LIBOPT) $(NETPBMLIB) $(LIBOPTR) $(JPEGLIBX)) \
-	  $(NETWORKLD) $(MATHLIB) $(LDFLAGS) $(LDLIBS) \
-	  $(RPATH) $(LADD)
+ppmtompeg: $(ADDL_OBJECTS) $(LIBOPT)
+ppmtompeg: LDFLAGS_TARGET = \
+  $(shell $(LIBOPT) $(LIBOPTR) $(JPEGLIBX)) $(NETWORKLD)
 
+profile: $(ADDL_OBJECTS) $(LIBOPT)
+profile: LDFLAGS_TARGET = \
+  -Bstatic -pg \
+  $(shell $(LIBOPT) $(LIBOPTR) $(JPEGLIBX)) $(NETWORKLD)
 
 #########
 # OTHER #
diff --git a/converter/ppm/ppmtompeg/bframe.c b/converter/ppm/ppmtompeg/bframe.c
index 1dbc1846..f5009d6c 100644
--- a/converter/ppm/ppmtompeg/bframe.c
+++ b/converter/ppm/ppmtompeg/bframe.c
@@ -523,7 +523,7 @@ makeNonSkipBlock(int              const y,
                  MpegFrame *      const curr, 
                  MpegFrame *      const prev, 
                  MpegFrame *      const next,
-                 boolean          const specificsOn,
+                 bool             const specificsOn,
                  int              const mbAddress,
                  int              const QScale,
                  const LumBlock * const currentBlockP,
diff --git a/converter/ppm/ppmtompeg/bitio.c b/converter/ppm/ppmtompeg/bitio.c
index e0dc4d4e..3812bc39 100644
--- a/converter/ppm/ppmtompeg/bitio.c
+++ b/converter/ppm/ppmtompeg/bitio.c
@@ -244,8 +244,8 @@ Bitio_Write(BitBucket * const bbPtr,
     assert(nbits <= 32 && nbits >= 0);
 
     /*
-     * Clear top bits if not part of data, necessary due to down and
-     * dirty calls of Bitio_Write with unecessary top bits set.
+     * Clear top bits if not part of data, necessary because of down and
+     * dirty calls of Bitio_Write with unnecessary top bits set.
      */
 
     bits &= lower_mask[nbits];
diff --git a/converter/ppm/ppmtompeg/bsearch.c b/converter/ppm/ppmtompeg/bsearch.c
index 70edfef6..c618bbd4 100644
--- a/converter/ppm/ppmtompeg/bsearch.c
+++ b/converter/ppm/ppmtompeg/bsearch.c
@@ -43,7 +43,7 @@
  * Changed copyrights
  *
  * Revision 1.6  1994/12/07  00:40:36  smoot
- * Added seperate P and B search ranges
+ * Added separate P and B search ranges
  *
  * Revision 1.5  1994/03/15  00:27:11  keving
  * nothing
@@ -834,7 +834,6 @@ BMotionSearchCross2(const LumBlock * const currentBlockP,
     interpErrF = FindBestMatch(&forwardBlock, currentBlockP,
                                next, by, bx, &newMotion.bwd,
                                bestErr, searchRangeB);
-    bestErr = min(bestErr, interpErr);
     interpErrB = FindBestMatch(&backBlock, currentBlockP,
                                prev, by, bx, &newMotion.fwd,
                                bestErr, searchRangeB);
diff --git a/converter/ppm/ppmtompeg/combine.c b/converter/ppm/ppmtompeg/combine.c
index 8e0d3281..c00f9c71 100644
--- a/converter/ppm/ppmtompeg/combine.c
+++ b/converter/ppm/ppmtompeg/combine.c
@@ -104,7 +104,7 @@ appendSpecifiedGopFiles(struct inputSource * const inputSourceP,
         FILE * ifP;
 
         GetNthInputFileName(inputSourceP, fileSeq, &inputFileName);
-        asprintfN(&fileName, "%s/%s", currentGOPPath, inputFileName);
+        pm_asprintf(&fileName, "%s/%s", currentGOPPath, inputFileName);
 
         for (nAttempts = 0, ifP = NULL;
              nAttempts < READ_ATTEMPTS && !ifP;
@@ -122,8 +122,8 @@ appendSpecifiedGopFiles(struct inputSource * const inputSourceP,
         } else
             pm_error("Unable to read file '%s' after %u attempts.",
                      fileName, READ_ATTEMPTS);
-        strfree(fileName);
-        strfree(inputFileName);
+        pm_strfree(fileName);
+        pm_strfree(inputFileName);
     }
 } 
 
@@ -140,7 +140,7 @@ appendDefaultGopFiles(const char * const outputFileName,
         const char * fileName;
         FILE * ifP;
 
-        asprintfN(&fileName, "%s.gop.%u", outputFileName, fileSeq);
+        pm_asprintf(&fileName, "%s.gop.%u", outputFileName, fileSeq);
         
         ifP = fopen(fileName, "rb");
         if (ifP == NULL)
@@ -151,7 +151,7 @@ appendDefaultGopFiles(const char * const outputFileName,
             
             AppendFile(ofP, ifP);
         }
-        strfree(fileName);
+        pm_strfree(fileName);
     }
 }
 
diff --git a/converter/ppm/ppmtompeg/docs/template.param b/converter/ppm/ppmtompeg/docs/template.param
index 66b6dd98..78ad5300 100644
--- a/converter/ppm/ppmtompeg/docs/template.param
+++ b/converter/ppm/ppmtompeg/docs/template.param
@@ -18,7 +18,7 @@
 # files in the order in which they must appear, followed by 'END_INPUT'
 #
 # Also, if you use the `command` method of generating input file names,
-# the command will only be executed in the INPUT_DIR if INPUT_DIR preceeds
+# the command will only be executed in the INPUT_DIR if INPUT_DIR precedes
 # the INPUT parameter.
 #
 # <option> MUST be in UPPER CASE
diff --git a/converter/ppm/ppmtompeg/examples/template.param b/converter/ppm/ppmtompeg/examples/template.param
index 66b6dd98..78ad5300 100644
--- a/converter/ppm/ppmtompeg/examples/template.param
+++ b/converter/ppm/ppmtompeg/examples/template.param
@@ -18,7 +18,7 @@
 # files in the order in which they must appear, followed by 'END_INPUT'
 #
 # Also, if you use the `command` method of generating input file names,
-# the command will only be executed in the INPUT_DIR if INPUT_DIR preceeds
+# the command will only be executed in the INPUT_DIR if INPUT_DIR precedes
 # the INPUT parameter.
 #
 # <option> MUST be in UPPER CASE
diff --git a/converter/ppm/ppmtompeg/frame.c b/converter/ppm/ppmtompeg/frame.c
index 09f46f66..75b209f8 100644
--- a/converter/ppm/ppmtompeg/frame.c
+++ b/converter/ppm/ppmtompeg/frame.c
@@ -753,7 +753,7 @@ Frame_AllocHalf(MpegFrame * const frameP) {
  *===========================================================================*/
 void
 Frame_AllocDecoded(MpegFrame * const frameP,
-                   boolean     const makeReference) {
+                   bool        const makeReference) {
 
     if (frameP->decoded_y != NULL) {
         /* already allocated */
diff --git a/converter/ppm/ppmtompeg/gethostname.c b/converter/ppm/ppmtompeg/gethostname.c
index 014b42e8..d20af17c 100644
--- a/converter/ppm/ppmtompeg/gethostname.c
+++ b/converter/ppm/ppmtompeg/gethostname.c
@@ -1,3 +1,4 @@
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 
 #include <string.h>
diff --git a/converter/ppm/ppmtompeg/gethostname_win32.c b/converter/ppm/ppmtompeg/gethostname_win32.c
index 383f4e55..e37fbb37 100644
--- a/converter/ppm/ppmtompeg/gethostname_win32.c
+++ b/converter/ppm/ppmtompeg/gethostname_win32.c
@@ -94,7 +94,7 @@ get_string_version(push_string_t *str)
             return FALSE;
     }
 
-    /* Call GetNativeSystemInfo if supported or GetSystemInfo otherwise. */
+    /* Call GetNativeSystemInfo if available; GetSystemInfo otherwise. */
     pGNSI = (PGNSI)
             GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), 
             "GetNativeSystemInfo");
diff --git a/converter/ppm/ppmtompeg/headers/all.h b/converter/ppm/ppmtompeg/headers/all.h
index e350aab8..8f095d8e 100644
--- a/converter/ppm/ppmtompeg/headers/all.h
+++ b/converter/ppm/ppmtompeg/headers/all.h
@@ -33,7 +33,7 @@
  * added little_endian force for irizx
  *
  * Revision 1.8  1995/02/02  22:02:18  smoot
- * added ifdefs for compatability on stranger and stranger architectures...
+ * added ifdefs for compatibility on stranger and stranger architectures...
  *
  * Revision 1.7  1995/02/02  07:26:45  eyhung
  * added parens to all.h to remove compiler warning
@@ -80,7 +80,6 @@
 #include <time.h>
 #endif
 
-#include "ansi.h"
 #include "general.h"
 
 /* some machines have #define index strchr; get rid of this nonsense */
diff --git a/converter/ppm/ppmtompeg/headers/ansi.h b/converter/ppm/ppmtompeg/headers/ansi.h
deleted file mode 100644
index b3c3ab17..00000000
--- a/converter/ppm/ppmtompeg/headers/ansi.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*===========================================================================*
- * ansi.h								     *
- *									     *
- *	macro for non-ansi compilers					     *
- *									     *
- *===========================================================================*/
-
-/*
- * Copyright (c) 1995 The Regents of the University of California.
- * All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose, without fee, and without written agreement is
- * hereby granted, provided that the above copyright notice and the following
- * two paragraphs appear in all copies of this software.
- *
- * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
- * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
- * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- */
-
-/*  
- *  $Header: /n/picasso/project/mpeg/mpeg_dist/mpeg_encode/headers/RCS/ansi.h,v 1.6 1995/08/15 23:43:13 smoot Exp $
- *  $Log: ansi.h,v $
- *  Revision 1.6  1995/08/15 23:43:13  smoot
- *  *** empty log message ***
- *
- *  Revision 1.5  1995/01/19 23:54:35  eyhung
- *  Changed copyrights
- *
- * Revision 1.4  1994/11/12  02:12:13  keving
- * nothing
- *
- * Revision 1.3  1993/07/22  22:24:23  keving
- * nothing
- *
- * Revision 1.2  1993/07/09  00:17:23  keving
- * nothing
- *
- * Revision 1.1  1993/06/14  22:50:22  keving
- * nothing
- *
- */
-
-
-#ifndef ANSI_INCLUDED
-#define ANSI_INCLUDED
-
-
-/*  
- *  _ANSI_ARGS_ macro stolen from Tcl6.5 by John Ousterhout
- */
-#undef _ANSI_ARGS_
-#undef const
-#ifdef NON_ANSI_COMPILER
-#define _ANSI_ARGS_(x)       ()
-#define CONST
-#else
-#define _ANSI_ARGS_(x)   x
-#define CONST const
-#ifdef __cplusplus
-#define VARARGS (...)
-#else
-#define VARARGS ()
-#endif
-#endif
-
-
-#endif /* ANSI_INCLUDED */
diff --git a/converter/ppm/ppmtompeg/headers/bitio.h b/converter/ppm/ppmtompeg/headers/bitio.h
index a24c21cd..931bcdd9 100644
--- a/converter/ppm/ppmtompeg/headers/bitio.h
+++ b/converter/ppm/ppmtompeg/headers/bitio.h
@@ -63,7 +63,6 @@
 #include <stdio.h>
 
 #include "general.h"
-#include "ansi.h"
 
 
 /*===========*
diff --git a/converter/ppm/ppmtompeg/headers/dct.h b/converter/ppm/ppmtompeg/headers/dct.h
index e024b6c1..3b824cf0 100644
--- a/converter/ppm/ppmtompeg/headers/dct.h
+++ b/converter/ppm/ppmtompeg/headers/dct.h
@@ -31,7 +31,6 @@
 #define DCT_INCLUDED
 
 
-#include "ansi.h"
 
 
 
@@ -47,11 +46,12 @@ typedef DCTELEM DCTBLOCK_2D[DCTSIZE][DCTSIZE];
 /*  
  *  from mfwddct.c:
  */
-extern void mp_fwd_dct_block2 _ANSI_ARGS_((DCTBLOCK_2D src, DCTBLOCK_2D dest));
+void init_fdct (void);
+extern void mp_fwd_dct_block2 (DCTBLOCK_2D src, DCTBLOCK_2D dest);
 
 /* jrevdct.c */
-extern void init_pre_idct _ANSI_ARGS_((void ));
-extern void mpeg_jrevdct _ANSI_ARGS_((DCTBLOCK data ));
+extern void init_pre_idct (void );
+extern void mpeg_jrevdct (DCTBLOCK data );
 
 
 /* We assume that right shift corresponds to signed division by 2 with
diff --git a/converter/ppm/ppmtompeg/headers/frame.h b/converter/ppm/ppmtompeg/headers/frame.h
index acd74419..e1f587a2 100644
--- a/converter/ppm/ppmtompeg/headers/frame.h
+++ b/converter/ppm/ppmtompeg/headers/frame.h
@@ -34,8 +34,7 @@
  * HEADER FILES *
  *==============*/
 
-#include "general.h"
-#include "ansi.h"
+#include "netpbm/pm_c_util.h"
 #include "mtypes.h"
 
 /*===========*
@@ -54,8 +53,10 @@ typedef struct mpegFrame {
     int type;
     char    inputFileName[256];
     int id;           /* the frame number -- starts at 0 */
-    boolean inUse;	/* TRUE iff this frame is currently being used */
-			/* FALSE means any data here can be thrashed */
+    bool inUse;
+	   /* this frame is currently being used (if not, any data here can be
+          thrashed)
+       */
 
     /*  
      *  now, the YCrCb data.  All pixel information is stored in unsigned
@@ -64,17 +65,17 @@ typedef struct mpegFrame {
      *
      *  if orig_y is NULL, then orig_cr, orig_cb are undefined
      */
-    uint8 **orig_y, **orig_cr, **orig_cb;
+    uint8_t **orig_y, **orig_cr, **orig_cb;
 
     /* now, the decoded data -- relevant only if
      *	    referenceFrame == DECODED_FRAME
      *
      * if decoded_y is NULL, then decoded_cr, decoded_cb are undefined 
      */
-    uint8 **decoded_y, **decoded_cr, **decoded_cb;
+    uint8_t **decoded_y, **decoded_cr, **decoded_cb;
 
     /* reference data */
-    uint8 **ref_y, **ref_cr, **ref_cb;
+    uint8_t **ref_y, **ref_cr, **ref_cb;
 
     /*  
      *  these are the Blocks which will ultimately compose MacroBlocks.
@@ -86,9 +87,9 @@ typedef struct mpegFrame {
     /*
      *  this is the half-pixel luminance data (for reference frames)
      */
-    uint8 **halfX, **halfY, **halfBoth;
+    uint8_t **halfX, **halfY, **halfBoth;
 
-    boolean   halfComputed;        /* TRUE iff half-pixels already computed */
+    bool   halfComputed;        /* TRUE iff half-pixels already computed */
 
     struct mpegFrame *next;  /* points to the next B-frame to be encoded, if
 		       * stdin is used as the input. 
@@ -120,7 +121,7 @@ Frame_AllocHalf(MpegFrame * const frameP);
 
 void
 Frame_AllocDecoded(MpegFrame * const frameP,
-                   boolean     const makeReference);
+                   bool        const makeReference);
 
 void
 Frame_Resize(MpegFrame * const omf,
diff --git a/converter/ppm/ppmtompeg/headers/frames.h b/converter/ppm/ppmtompeg/headers/frames.h
index f2bcf6ae..2ec11d69 100644
--- a/converter/ppm/ppmtompeg/headers/frames.h
+++ b/converter/ppm/ppmtompeg/headers/frames.h
@@ -13,7 +13,6 @@
  *==============*/
 
 #include "pm_config.h"  /* For __inline__ */
-#include "ansi.h"
 #include "mtypes.h"
 #include "mheaders.h"
 #include "iframe.h"
@@ -255,7 +254,6 @@ extern int gopSize;
 extern int slicesPerFrame;
 extern int blocksPerSlice;
 extern int referenceFrame;
-extern boolean specificsOn;
 extern int quietTime;       /* shut up for at least quietTime seconds;
                  * negative means shut up forever
                  */
diff --git a/converter/ppm/ppmtompeg/headers/frametype.h b/converter/ppm/ppmtompeg/headers/frametype.h
index 63bee964..33b604e6 100644
--- a/converter/ppm/ppmtompeg/headers/frametype.h
+++ b/converter/ppm/ppmtompeg/headers/frametype.h
@@ -7,7 +7,7 @@ FType_Type(unsigned int const frameNum);
 unsigned int
 FType_FutureRef(unsigned int const currFrameNum);
 
-int	FType_PastRef _ANSI_ARGS_((int currFrameNum));
+int	FType_PastRef (int currFrameNum);
 
 void SetFramePattern(const char * const pattern);
 
diff --git a/converter/ppm/ppmtompeg/headers/general.h b/converter/ppm/ppmtompeg/headers/general.h
index f29c9445..59c33c73 100644
--- a/converter/ppm/ppmtompeg/headers/general.h
+++ b/converter/ppm/ppmtompeg/headers/general.h
@@ -111,15 +111,7 @@ int pclose();
 #define NEWLINE '\n'
 
 
-/*==================*
- * TYPE DEFINITIONS *
- *==================*/
-
-#ifndef HAVE_BOOLEAN
 typedef int boolean;
-#define HAVE_BOOLEAN
-/* JPEG library also defines boolean */
-#endif
 
 /* In the following, we need the "signed" in order to make these typedefs
    match those in AIX system header files.  Otherwise, compile fails on 
@@ -167,8 +159,6 @@ typedef signed int int32;
 #define max(a,b) ((a) > (b) ? (a) : (b))
 #undef min
 #define min(a,b) ((a) < (b) ? (a) : (b))
-#undef abs
-#define abs(a) ((a) >= 0 ? (a) : -(a))
 
 
 #endif
diff --git a/converter/ppm/ppmtompeg/headers/huff.h b/converter/ppm/ppmtompeg/headers/huff.h
index 47ffb843..a6379248 100644
--- a/converter/ppm/ppmtompeg/headers/huff.h
+++ b/converter/ppm/ppmtompeg/headers/huff.h
@@ -24,8 +24,11 @@
  */
 
 /*  
- *  THIS FILE IS MACHINE GENERATED!  DO NOT EDIT!
+ *  THIS FILE WAS ORIGINALLY MACHINE GENERATED
  */
+
+#include "general.h"
+
 #define HUFF_MAXRUN	32
 #define HUFF_MAXLEVEL	41
 
diff --git a/converter/ppm/ppmtompeg/headers/jpeg.h b/converter/ppm/ppmtompeg/headers/jpeg.h
index 62c6f930..76d73d9e 100644
--- a/converter/ppm/ppmtompeg/headers/jpeg.h
+++ b/converter/ppm/ppmtompeg/headers/jpeg.h
@@ -1,5 +1,5 @@
-#include "ansi.h"
-
+#include <stdio.h>
+#include "frame.h"
 
 void
 JMovie2JPEG(const char * const infilename,
diff --git a/converter/ppm/ppmtompeg/headers/mheaders.h b/converter/ppm/ppmtompeg/headers/mheaders.h
index 21d43e3d..edd9552d 100644
--- a/converter/ppm/ppmtompeg/headers/mheaders.h
+++ b/converter/ppm/ppmtompeg/headers/mheaders.h
@@ -54,7 +54,6 @@
  *==============*/
 
 #include "general.h"
-#include "ansi.h"
 #include "bitio.h"
 
 
@@ -62,7 +61,7 @@
  * EXTERNAL PROCEDURE prototypes *
  *===============================*/
 
-void	SetGOPStartTime _ANSI_ARGS_((int index));
+void	SetGOPStartTime (int index);
 
 void
 Mhead_GenSequenceHeader(BitBucket *   const bbPtr, 
@@ -80,21 +79,21 @@ Mhead_GenSequenceHeader(BitBucket *   const bbPtr,
                         uint8 *       const user_data,
                         int32         const user_data_size);
 
-void	Mhead_GenSequenceEnder _ANSI_ARGS_((BitBucket *bbPtr));
-void	Mhead_GenGOPHeader _ANSI_ARGS_((BitBucket *bbPtr,
+void	Mhead_GenSequenceEnder (BitBucket *bbPtr);
+void	Mhead_GenGOPHeader (BitBucket *bbPtr,
 	   int32 drop_frame_flag,
            int32 tc_hrs, int32 tc_min,
            int32 tc_sec, int32 tc_pict,
            int32 closed_gop, int32 broken_link,
            uint8 *ext_data, int32 ext_data_size,
-           uint8 *user_data, int32 user_data_size));
-void	Mhead_GenPictureHeader _ANSI_ARGS_((BitBucket *bbPtr, int frameType,
-					    int pictCount, int f_code));
-void	Mhead_GenSliceHeader _ANSI_ARGS_((BitBucket *bbPtr, uint32 slicenum,
+           uint8 *user_data, int32 user_data_size);
+void	Mhead_GenPictureHeader (BitBucket *bbPtr, int frameType,
+					    int pictCount, int f_code);
+void	Mhead_GenSliceHeader (BitBucket *bbPtr, uint32 slicenum,
 					  uint32 qscale, uint8 *extra_info,
-					  uint32 extra_info_size));
-void	Mhead_GenSliceEnder _ANSI_ARGS_((BitBucket *bbPtr));
-void	Mhead_GenMBHeader _ANSI_ARGS_((BitBucket *bbPtr,
+					  uint32 extra_info_size);
+void	Mhead_GenSliceEnder (BitBucket *bbPtr);
+void	Mhead_GenMBHeader (BitBucket *bbPtr,
 	  uint32 pict_code_type, uint32 addr_incr,
           uint32 q_scale,
           uint32 forw_f_code, uint32 back_f_code,
@@ -103,7 +102,7 @@ void	Mhead_GenMBHeader _ANSI_ARGS_((BitBucket *bbPtr,
           int32 motion_forw, int32 m_horiz_forw,
           int32 m_vert_forw, int32 motion_back,
           int32 m_horiz_back, int32 m_vert_back,
-          uint32 mb_pattern, uint32 mb_intra));
+          uint32 mb_pattern, uint32 mb_intra);
 
 
 #endif /* MHEADERS_INCLUDED */
diff --git a/converter/ppm/ppmtompeg/headers/motion_search.h b/converter/ppm/ppmtompeg/headers/motion_search.h
index 62f3abab..d00509c4 100644
--- a/converter/ppm/ppmtompeg/headers/motion_search.h
+++ b/converter/ppm/ppmtompeg/headers/motion_search.h
@@ -9,7 +9,6 @@
  * HEADER FILES *
  *==============*/
 
-#include "ansi.h"
 
 
 /*===========*
@@ -140,7 +139,7 @@ extern int psearchAlg;
  *  Changed copyrights
  *
  * Revision 1.4  1994/12/07  00:42:01  smoot
- * Added seperate P and B search ranges
+ * Added separate P and B search ranges
  *
  * Revision 1.3  1994/11/12  02:12:58  keving
  * nothing
diff --git a/converter/ppm/ppmtompeg/headers/mpeg.h b/converter/ppm/ppmtompeg/headers/mpeg.h
index 56862c42..fbfaaf2c 100644
--- a/converter/ppm/ppmtompeg/headers/mpeg.h
+++ b/converter/ppm/ppmtompeg/headers/mpeg.h
@@ -34,7 +34,6 @@
 
 #include "pm_c_util.h"
 #include "ppm.h"
-#include "ansi.h"
 #include "mtypes.h"
 #include "frame.h"
 
@@ -80,7 +79,7 @@ ComputeGOPFrames(int            const whichGOP,
                  unsigned int * const lastFrameP, 
                  unsigned int   const numFrames);
 
-extern void	IncrementTCTime _ANSI_ARGS_((void));
+extern void	IncrementTCTime (void);
 void SetReferenceFrameType(const char * const type);
 
 boolean
@@ -93,7 +92,7 @@ ReadDecodedRefFrame(MpegFrame *  const frameP,
 void
 SetBitRateFileName(const char * const fileName);
 
-extern void	SetFrameRate _ANSI_ARGS_((void));
+extern void	SetFrameRate (void);
 
 
 /*==================*
diff --git a/converter/ppm/ppmtompeg/headers/mproto.h b/converter/ppm/ppmtompeg/headers/mproto.h
index d8fefd84..5b003b2e 100644
--- a/converter/ppm/ppmtompeg/headers/mproto.h
+++ b/converter/ppm/ppmtompeg/headers/mproto.h
@@ -70,7 +70,6 @@
  *==============*/
 
 #include "general.h"
-#include "ansi.h"
 #include "bitio.h"
 
 
@@ -86,39 +85,39 @@ typedef DCTELEM DCTBLOCK[DCTSIZE2];
 /*  
  *  from mbasic.c:
  */
-void mp_reset _ANSI_ARGS_((void));
-void mp_free _ANSI_ARGS_((MpegFrame *mf));
-MpegFrame *mp_new _ANSI_ARGS_((int fnumber, char type, MpegFrame *oldFrame));
-void mp_ycc_calc _ANSI_ARGS_((MpegFrame *mf));
-void mp_dct_blocks _ANSI_ARGS_((MpegFrame *mf));
-void	AllocDecoded _ANSI_ARGS_((MpegFrame *frame));
+void mp_reset (void);
+void mp_free (MpegFrame *mf);
+MpegFrame *mp_new (int fnumber, char type, MpegFrame *oldFrame);
+void mp_ycc_calc (MpegFrame *mf);
+void mp_dct_blocks (MpegFrame *mf);
+void	AllocDecoded (MpegFrame *frame);
 
 /*  
  *  from moutput.c:
  */
-boolean mp_quant_zig_block _ANSI_ARGS_((Block in, FlatBlock out, int qscale, int iblock));
-void	UnQuantZig _ANSI_ARGS_((FlatBlock in, Block out, int qscale, boolean iblock));
-void mp_rle_huff_block _ANSI_ARGS_((FlatBlock in, BitBucket *out));
-void mp_rle_huff_pblock _ANSI_ARGS_((FlatBlock in, BitBucket *out));
-void mp_create_blocks _ANSI_ARGS_((MpegFrame *mf));
+boolean mp_quant_zig_block (Block in, FlatBlock out, int qscale, int iblock);
+void	UnQuantZig (FlatBlock in, Block out, int qscale, boolean iblock);
+void mp_rle_huff_block (FlatBlock in, BitBucket *out);
+void mp_rle_huff_pblock (FlatBlock in, BitBucket *out);
+void mp_create_blocks (MpegFrame *mf);
 
 
 
 
-void	ReadEYUV _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer, int width,
-			    int height));
-boolean	ReadPPM _ANSI_ARGS_((MpegFrame *mf, FILE *fpointer));
-void PPMtoYCC _ANSI_ARGS_((MpegFrame * mf));
+void	ReadEYUV (MpegFrame * mf, FILE *fpointer, int width,
+			    int height);
+boolean	ReadPPM (MpegFrame *mf, FILE *fpointer);
+void PPMtoYCC (MpegFrame * mf);
 
-void	ComputeHalfPixelData _ANSI_ARGS_((MpegFrame *frame));
-void mp_validate_size _ANSI_ARGS_((int *x, int *y));
-void AllocYCC _ANSI_ARGS_((MpegFrame * mf));
+void	ComputeHalfPixelData (MpegFrame *frame);
+void mp_validate_size (int *x, int *y);
+void AllocYCC (MpegFrame * mf);
 
 
 /* jrevdct.c */
-void init_pre_idct _ANSI_ARGS_((void ));
-void j_rev_dct_sparse _ANSI_ARGS_((DCTBLOCK data , int pos ));
-void j_rev_dct _ANSI_ARGS_((DCTBLOCK data ));
-void j_rev_dct_sparse _ANSI_ARGS_((DCTBLOCK data , int pos ));
-void j_rev_dct _ANSI_ARGS_((DCTBLOCK data ));
+void init_pre_idct (void );
+void j_rev_dct_sparse (DCTBLOCK data , int pos );
+void j_rev_dct (DCTBLOCK data );
+void j_rev_dct_sparse (DCTBLOCK data , int pos );
+void j_rev_dct (DCTBLOCK data );
 
diff --git a/converter/ppm/ppmtompeg/headers/mtypes.h b/converter/ppm/ppmtompeg/headers/mtypes.h
index 0db5a330..a44ce680 100644
--- a/converter/ppm/ppmtompeg/headers/mtypes.h
+++ b/converter/ppm/ppmtompeg/headers/mtypes.h
@@ -10,12 +10,7 @@
 #ifndef MTYPES_INCLUDED
 #define MTYPES_INCLUDED
 
-
-/*==============*
- * HEADER FILES *
- *==============*/
-
-#include "general.h"
+#include "netpbm/pm_config.h"
 #include "dct.h"
 
 
@@ -48,12 +43,12 @@ typedef struct motion {
 /*  
  *  your basic Block type
  */
-typedef int16 Block[DCTSIZE][DCTSIZE];
-typedef int16 FlatBlock[DCTSIZE_SQ];
+typedef int16_t Block[DCTSIZE][DCTSIZE];
+typedef int16_t FlatBlock[DCTSIZE_SQ];
 typedef	struct {
-    int32 l[2*DCTSIZE][2*DCTSIZE];
+    int32_t l[2*DCTSIZE][2*DCTSIZE];
 } LumBlock;
-typedef	int32 ChromBlock[DCTSIZE][DCTSIZE];
+typedef	int32_t ChromBlock[DCTSIZE][DCTSIZE];
 
 /*========*
  * MACROS *
diff --git a/converter/ppm/ppmtompeg/headers/opts.h b/converter/ppm/ppmtompeg/headers/opts.h
index 5901a677..1756334e 100644
--- a/converter/ppm/ppmtompeg/headers/opts.h
+++ b/converter/ppm/ppmtompeg/headers/opts.h
@@ -38,7 +38,6 @@
  */
 
 #include "general.h"
-#include "ansi.h"
 #include "mtypes.h"
 
 /*
@@ -110,14 +109,14 @@ extern int LaplaceNum, LaplaceCnum;
 extern boolean BSkipBlocks;
 
 /* Procedures Prototypes */
-int	GetIQScale _ANSI_ARGS_((void));
-int	GetPQScale _ANSI_ARGS_((void));
-int	GetBQScale _ANSI_ARGS_((void));
-void	Tune_Init _ANSI_ARGS_((void));
-int     CalcRLEHuffLength _ANSI_ARGS_((FlatBlock in));
+int	GetIQScale (void);
+int	GetPQScale (void);
+int	GetBQScale (void);
+void	Tune_Init (void);
+int     CalcRLEHuffLength (FlatBlock in);
 void    ParseTuneParam(const char * const charPtr);
-int     mse _ANSI_ARGS_((Block blk1, Block blk2));
-void    Mpost_UnQuantZigBlockLaplace _ANSI_ARGS_((FlatBlock in, Block out, int qscale, boolean iblock));
+int     mse (Block blk1, Block blk2);
+void    Mpost_UnQuantZigBlockLaplace (FlatBlock in, Block out, int qscale, boolean iblock);
 extern void CalcLambdas(void);
 
 
diff --git a/converter/ppm/ppmtompeg/headers/parallel.h b/converter/ppm/ppmtompeg/headers/parallel.h
index 90edd874..0a31fac2 100644
--- a/converter/ppm/ppmtompeg/headers/parallel.h
+++ b/converter/ppm/ppmtompeg/headers/parallel.h
@@ -30,7 +30,6 @@
  * HEADER FILES *
  *==============*/
 
-#include "ansi.h"
 #include "bitio.h"
 #include "frame.h"
 
diff --git a/converter/ppm/ppmtompeg/headers/param.h b/converter/ppm/ppmtompeg/headers/param.h
index c7f57b44..46a544b3 100644
--- a/converter/ppm/ppmtompeg/headers/param.h
+++ b/converter/ppm/ppmtompeg/headers/param.h
@@ -1,7 +1,6 @@
 /* COPYRIGHT information is at end of file */
 
 #include "pm_c_util.h"
-#include "ansi.h"
 #include "input.h"
 
 
@@ -51,7 +50,7 @@ extern char machineName[MAX_MACHINES][256];
 extern char userName[MAX_MACHINES][256];
 extern char executable[MAX_MACHINES][1024];
 extern char remoteParamFile[MAX_MACHINES][1024];
-extern boolean remote[MAX_MACHINES];
+extern bool remote[MAX_MACHINES];
 extern char currentPath[MAXPATHLEN];
 extern char currentFramePath[MAXPATHLEN];
 extern char currentGOPPath[MAXPATHLEN];
@@ -62,12 +61,21 @@ extern int realWidth, realHeight;
 extern char ioConversion[1024];
 extern char slaveConversion[1024];
 extern FILE * bitRateFile;
-extern boolean showBitRatePerFrame;
-extern boolean computeMVHist;
 extern const double VidRateNum[9];
-extern boolean keepTempFiles;
+extern bool keepTempFiles;
+extern int outputWidth, outputHeight;
+extern bool specificsOn;
+extern char specificsFile[256];
+extern char specificsDefines[1024];
+extern bool GammaCorrection;
+extern float GammaValue;
+extern char userDataFileName[256];
 
 
+/* Defined in ppmtompeg.c; computed from command line */
+extern bool showBitRatePerFrame;
+extern bool computeMVHist;
+
 /*
  * Copyright (c) 1995 The Regents of the University of California.
  * All rights reserved.
diff --git a/converter/ppm/ppmtompeg/headers/prototypes.h b/converter/ppm/ppmtompeg/headers/prototypes.h
index b421af35..432062e3 100644
--- a/converter/ppm/ppmtompeg/headers/prototypes.h
+++ b/converter/ppm/ppmtompeg/headers/prototypes.h
@@ -30,7 +30,6 @@
  *==============*/
 
 #include "general.h"
-#include "ansi.h"
 #include "frame.h"
 
 
@@ -38,34 +37,34 @@
  * EXTERNAL PROCEDURE prototypes *
  *===============================*/
 
-int GetBQScale _ANSI_ARGS_((void));
-int GetPQScale _ANSI_ARGS_((void));
-void    ResetBFrameStats _ANSI_ARGS_((void));
-void    ResetPFrameStats _ANSI_ARGS_((void));
+int GetBQScale (void);
+int GetPQScale (void);
+void    ResetBFrameStats (void);
+void    ResetPFrameStats (void);
 void SetSearchRange (int const pixelsP,
                      int const pixelsB);
 void
 SetPixelSearch(const char * const searchType);
-void    SetPQScale _ANSI_ARGS_((int qP));
-void    SetBQScale _ANSI_ARGS_((int qB));
-float   EstimateSecondsPerPFrame _ANSI_ARGS_((void));
-float   EstimateSecondsPerBFrame _ANSI_ARGS_((void));
-void    SetGOPSize _ANSI_ARGS_((int size));
+void    SetPQScale (int qP);
+void    SetBQScale (int qB);
+float   EstimateSecondsPerPFrame (void);
+float   EstimateSecondsPerBFrame (void);
+void    SetGOPSize (int size);
 void
 SetStatFileName(const char * const fileName);
 
 
-void DCTFrame _ANSI_ARGS_((MpegFrame * mf));
+void DCTFrame (MpegFrame * mf);
 
-void PPMtoYCC _ANSI_ARGS_((MpegFrame * mf));
+void PPMtoYCC (MpegFrame * mf);
 
-void    MotionSearchPreComputation _ANSI_ARGS_((MpegFrame *frame));
+void    MotionSearchPreComputation (MpegFrame *frame);
 
-void    ComputeHalfPixelData _ANSI_ARGS_((MpegFrame *frame));
-void mp_validate_size _ANSI_ARGS_((int *x, int *y));
+void    ComputeHalfPixelData (MpegFrame *frame);
+void mp_validate_size (int *x, int *y);
 
 
 /* psearch.c */
-void    ShowPMVHistogram _ANSI_ARGS_((FILE *fpointer));
-void    ShowBBMVHistogram _ANSI_ARGS_((FILE *fpointer));
-void    ShowBFMVHistogram _ANSI_ARGS_((FILE *fpointer));
+void    ShowPMVHistogram (FILE *fpointer);
+void    ShowBBMVHistogram (FILE *fpointer);
+void    ShowBFMVHistogram (FILE *fpointer);
diff --git a/converter/ppm/ppmtompeg/headers/rate.h b/converter/ppm/ppmtompeg/headers/rate.h
index df5ca1cc..a5f5076f 100644
--- a/converter/ppm/ppmtompeg/headers/rate.h
+++ b/converter/ppm/ppmtompeg/headers/rate.h
@@ -64,11 +64,11 @@ targetRateControl(MpegFrame * const frameP);
  * MB_RateOut
  *
  *      Prints out sampling of MB rate control data.  Every "nth" block
- *	stats are printed, with "n" controled by global RC_MB_SAMPLE_RATE
+ *	stats are printed, with "n" controlled by global RC_MB_SAMPLE_RATE
  *
  * RETURNS:     nothing
  *===========================================================================*/
-extern void MB_RateOut _ANSI_ARGS_((int type));
+extern void MB_RateOut (int type);
 
 
 /*===========================================================================*
@@ -92,7 +92,7 @@ updateRateControl(int const type);
  *
  * RETURNS:     new Qscale
  *===========================================================================*/
-extern int needQScaleChange _ANSI_ARGS_((int oldQScale,  Block blk0, Block blk1, Block blk2, Block blk3));
+extern int needQScaleChange (int oldQScale,  Block blk0, Block blk1, Block blk2, Block blk3);
 
 /*===========================================================================*
  *
@@ -101,7 +101,7 @@ extern int needQScaleChange _ANSI_ARGS_((int oldQScale,  Block blk0, Block blk1,
  *
  * RETURNS:   nothing
  *===========================================================================*/
-extern void incNumBlocks _ANSI_ARGS_((int num));
+extern void incNumBlocks (int num);
 
 
 /*===========================================================================*
@@ -113,7 +113,7 @@ extern void incNumBlocks _ANSI_ARGS_((int num));
  *
  * RETURNS:   nothing
  *===========================================================================*/
-extern void incMacroBlockBits _ANSI_ARGS_((int num));
+extern void incMacroBlockBits (int num);
 
 
 /*===========================================================================*
@@ -125,7 +125,7 @@ extern void incMacroBlockBits _ANSI_ARGS_((int num));
  *
  * RETURNS:     nothing
  *===========================================================================*/
-extern void SetRateControl _ANSI_ARGS_((char *charPtr));
+extern void SetRateControl (char *charPtr);
 
 
 /*===========================================================================*
@@ -150,7 +150,7 @@ setBufferSize(const char * const charPtr);
  *
  * RETURNS:     int (or -1 if invalid)
  *===========================================================================*/
-extern int getBufferSize _ANSI_ARGS_((void));
+extern int getBufferSize (void);
 
 
 /*===========================================================================*
@@ -178,7 +178,7 @@ setBitRate(const char * const charPtr);
  *
  * RETURNS:     int (-1 if Variable mode operation)
  *===========================================================================*/
-extern int getBitRate _ANSI_ARGS_((void));
+extern int getBitRate (void);
 
 
 /*===========================================================================*
@@ -189,7 +189,7 @@ extern int getBitRate _ANSI_ARGS_((void));
  *
  * RETURNS:     integer
  *===========================================================================*/
-extern int getRateMode _ANSI_ARGS_((void));
+extern int getRateMode (void);
 
 
 /*===========================================================================*
@@ -200,5 +200,5 @@ extern int getRateMode _ANSI_ARGS_((void));
  *
  * RETURNS:   nothing
  *===========================================================================*/
-extern void incQuantOverride  _ANSI_ARGS_((int num));
+extern void incQuantOverride  (int num);
 
diff --git a/converter/ppm/ppmtompeg/headers/specifics.h b/converter/ppm/ppmtompeg/headers/specifics.h
index 7bcf4ace..4f5c7074 100644
--- a/converter/ppm/ppmtompeg/headers/specifics.h
+++ b/converter/ppm/ppmtompeg/headers/specifics.h
@@ -1,4 +1,3 @@
-#include "ansi.h"
 
 
 /*===========*
@@ -29,8 +28,8 @@ typedef struct fsl_def {
 } FrameSpecList;
 
 
-void	Specifics_Init _ANSI_ARGS_((void));
-int     SpecLookup _ANSI_ARGS_((int fn, int typ, int num, 
-				BlockMV **info, int start_qs));
-int SpecTypeLookup _ANSI_ARGS_((int fn));
+void	Specifics_Init (void);
+int     SpecLookup (int fn, int typ, int num,
+			    BlockMV **info, int start_qs);
+int SpecTypeLookup (int fn);
 
diff --git a/converter/ppm/ppmtompeg/huff.c b/converter/ppm/ppmtompeg/huff.c
index 821daca1..f2be16ef 100644
--- a/converter/ppm/ppmtompeg/huff.c
+++ b/converter/ppm/ppmtompeg/huff.c
@@ -24,8 +24,9 @@
  */
 
 /*  
- *  THIS FILE IS MACHINE GENERATED!  DO NOT EDIT!
+ *  THIS FILE WAS ORIGINALLY MACHINE GENERATED
  */
+#include "general.h"
 #include "mtypes.h"
 #include "huff.h"
 
diff --git a/converter/ppm/ppmtompeg/input.c b/converter/ppm/ppmtompeg/input.c
index 2b87afa7..4266d39b 100644
--- a/converter/ppm/ppmtompeg/input.c
+++ b/converter/ppm/ppmtompeg/input.c
@@ -104,17 +104,17 @@ GetNthInputFileName(struct inputSource * const inputSourceP,
         }
 
         if (mapNEntryP->repeat != TRUE)
-            asprintfN(fileNameP, "%s%s%s",
-                      mapNEntryP->left, &numBuffer[32-numPadding],
-                      mapNEntryP->right);
+            pm_asprintf(fileNameP, "%s%s%s",
+                        mapNEntryP->left, &numBuffer[32-numPadding],
+                        mapNEntryP->right);
         else
-            asprintfN(fileNameP, "%s", mapNEntryP->left);
+            pm_asprintf(fileNameP, "%s", mapNEntryP->left);
     } else {
         if (mapNEntryP->repeat != TRUE)
-            asprintfN(fileNameP, "%s%d%s",
-                      mapNEntryP->left, index, mapNEntryP->right);
+            pm_asprintf(fileNameP, "%s%d%s",
+                        mapNEntryP->left, index, mapNEntryP->right);
         else
-            asprintfN(fileNameP, "%s", mapNEntryP->left);
+            pm_asprintf(fileNameP, "%s", mapNEntryP->left);
     }
 
     lastN = n;
diff --git a/converter/ppm/ppmtompeg/jpeg.c b/converter/ppm/ppmtompeg/jpeg.c
index a703cf39..24c9ae2d 100644
--- a/converter/ppm/ppmtompeg/jpeg.c
+++ b/converter/ppm/ppmtompeg/jpeg.c
@@ -17,7 +17,6 @@
 #define _XOPEN_SOURCE    /* Make sure stdio.h contains fileno() */
 #include <unistd.h>
 #include <stdio.h>
-#include "all.h"
 /* With the lossless jpeg patch applied to the Jpeg library
     (ftp://ftp.wizards.dupont.com/pub/ImageMagick/delegates/ljpeg-6b.tar.gz),
     the name of min_DCT_scaled_size changes to min_codec_data_unit,
@@ -26,16 +25,17 @@
 #define min_codec_data_unit min_DCT_scaled_size
 #include <jpeglib.h>
 #undef min_codec_data_unit
-#include "mtypes.h"
-#include "frames.h"
-#include "prototypes.h"
-#include "param.h"
-#include "readframe.h"
+
+#include "netpbm/pm_config.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/pm.h"
+
 #include "fsize.h"
-#include "rgbtoycc.h"
+#include "frame.h"
+
 #include "jpeg.h"
 
-#include "mallocvar.h"
 
 /* make it happier.... */
 #undef DCTSIZE2
@@ -101,7 +101,7 @@ JMovie2JPEG(const char * const infilename,
     char ofname[256];     /* output filename string */
     int Temp = 0, temp = 0;   /* dummy variables */
     int image_offset = 0;     /* counting variable */
-    /* J_Movie header infomation */
+    /* J_Movie header information */
     int ver_no;           /* version number - expected to be 2 */
     int fps;              /* frame rate - frames per second */
     int no_frames;        /* total number of frames in jmovie */
@@ -360,7 +360,7 @@ JMovie2JPEG(const char * const infilename,
  *
  *      allocate and initialize JPEG decompression object
  *      specify data source (eg, a file)
- *      jpeg_read_header();     // obtain image dimensions and other parameters
+ *      jpeg_read_header();      obtain image dimensions and other parameters
  *      set parameters for decompression
  *      jpeg_start_decompress();
  *      while (scan lines remain to be read)
@@ -385,7 +385,7 @@ ReadJPEG(MpegFrame * const mf,
     jpeg_component_info *compptr;
     int buffer_height;
     int current_row[3];
-    uint8 **orig[3];
+    uint8_t **orig[3];
     int h_samp[3],v_samp[3];
     int max_h_samp,max_v_samp;
     int temp_h, temp_v;
@@ -500,7 +500,7 @@ ReadJPEG(MpegFrame * const mf,
 
         (void) jpeg_read_raw_data(&cinfo, scanarray, buffer_height);
 
-        /* alter subsample ratio's if neccessary */
+        /* alter subsample ratio's if necessary */
         if ((h_samp[0]==2) && (h_samp[1]==1) && (h_samp[2]==1) &&
             (v_samp[0]==2) && (v_samp[1]==1) && (v_samp[2]==1)) {
             /* we are 4:1:1 as expected by the encoder*/
diff --git a/converter/ppm/ppmtompeg/jrevdct.c b/converter/ppm/ppmtompeg/jrevdct.c
index 2e99a67a..bf9196c4 100644
--- a/converter/ppm/ppmtompeg/jrevdct.c
+++ b/converter/ppm/ppmtompeg/jrevdct.c
@@ -28,7 +28,6 @@
 
 #include <memory.h>
 #include "all.h"
-#include "ansi.h"
 #include "dct.h"
 
 
@@ -162,9 +161,9 @@ ones here or successive P-frames will drift too much with Reference frame coding
 /*
   Switch on reverse_dct choices
 */
-void reference_rev_dct _ANSI_ARGS_((int16 *block));
-void mpeg_jrevdct_quick _ANSI_ARGS_((int16 *block));
-void init_idctref _ANSI_ARGS_((void));
+void reference_rev_dct (int16 *block);
+void mpeg_jrevdct_quick (int16 *block);
+void init_idctref (void);
 
 extern boolean pureDCT;
 
@@ -199,7 +198,7 @@ mpeg_jrevdct_quick(data)
   dataptr = data;
 
   for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--) {
-    /* Due to quantization, we will usually find that many of the input
+    /* Because of quantization, we will usually find that many of the input
      * coefficients are zero, especially the AC terms.  We can exploit this
      * by short-circuiting the IDCT calculation for any row in which all
      * the AC terms are zero.  In that case each output is equal to the
diff --git a/converter/ppm/ppmtompeg/mfwddct.c b/converter/ppm/ppmtompeg/mfwddct.c
index 4643bf25..75c3a718 100644
--- a/converter/ppm/ppmtompeg/mfwddct.c
+++ b/converter/ppm/ppmtompeg/mfwddct.c
@@ -122,33 +122,68 @@
 #define OSIN_5_16 OCOS_3_16
 #define OCOS_5_16 OSIN_3_16
 
-/* Prototypes */
-void reference_fwd_dct _ANSI_ARGS_((Block block, Block dest));
-void mp_fwd_dct_fast _ANSI_ARGS_((Block data2d, Block dest2d));
-void init_fdct _ANSI_ARGS_((void));
 
-/*
- * --------------------------------------------------------------
- *
- * mp_fwd_dct_block2 --
- *
- * Select the appropriate mp_fwd_dct routine
- *
- * Results: None
- *
- * Side effects: None
- *
- * --------------------------------------------------------------
- */
-extern boolean pureDCT;
-void
-mp_fwd_dct_block2(data, dest)
-    Block data, dest;
+static double trans_coef[8][8]; /* transform coefficients */
+
+
+
+static void reference_fwd_dct(block, dest)
+Block block, dest;
 {
-  if (pureDCT) reference_fwd_dct(data, dest);
-  else mp_fwd_dct_fast(data, dest);
+  int i, j, k;
+  double s;
+  double tmp[64];
+
+  if (DoLaplace) {
+    LaplaceNum++;
+  }
+
+  for (i=0; i<8; i++)
+    for (j=0; j<8; j++)
+    {
+      s = 0.0;
+
+      for (k=0; k<8; k++)
+        s += trans_coef[j][k] * block[i][k];
+
+      tmp[8*i+j] = s;
+    }
+
+  for (i=0; i<8; i++)
+    for (j=0; j<8; j++)
+    {
+      s = 0.0;
+
+      for (k=0; k<8; k++)
+        s += trans_coef[i][k] * tmp[8*k+j];
+
+      if (collect_quant) {
+	fprintf(collect_quant_fp, "%d %f\n", 8*i+j, s);
+      } 
+      if (DoLaplace) {
+	L1[LaplaceCnum][i*8+j] += s*s;
+	L2[LaplaceCnum][i*8+j] += s;
+      }
+
+
+      dest[i][j] = (int)floor(s+0.499999);
+      /*
+       * reason for adding 0.499999 instead of 0.5:
+       * s is quite often x.5 (at least for i and/or j = 0 or 4)
+       * and setting the rounding threshold exactly to 0.5 leads to an
+       * extremely high arithmetic implementation dependency of the result;
+       * s being between x.5 and x.500001 (which is now incorrectly rounded
+       * downwards instead of upwards) is assumed to occur less often
+       * (if at all)
+       */
+    }
 }
 
+
+
+static void
+mp_fwd_dct_fast(data2d, dest2d)
+    Block data2d, dest2d;
 /*
  * --------------------------------------------------------------
  *
@@ -166,9 +201,6 @@ mp_fwd_dct_block2(data, dest)
  * --------------------------------------------------------------
  */
 
-void
-mp_fwd_dct_fast(data2d, dest2d)
-    Block data2d, dest2d;
 {
     int16 *data = (int16 *) data2d;	/* this algorithm wants
 					 * a 1-d array */
@@ -236,10 +268,12 @@ mp_fwd_dct_fast(data2d, dest2d)
 	    tmp13 = tmp0 - tmp3;
 
 	    outptr[0] = (int16) UNFIXH((tmp10 + tmp11) * SIN_1_4);
-	    outptr[DCTSIZE * 4] = (int16) UNFIXH((tmp10 - tmp11) * COS_1_4);
-
-	    outptr[DCTSIZE * 2] = (int16) UNFIXH(tmp13 * COS_1_8 + tmp12 * SIN_1_8);
-	    outptr[DCTSIZE * 6] = (int16) UNFIXH(tmp13 * SIN_1_8 - tmp12 * COS_1_8);
+	    outptr[DCTSIZE * 4] =
+            (int16) UNFIXH((tmp10 - tmp11) * COS_1_4);
+	    outptr[DCTSIZE * 2] =
+            (int16) UNFIXH(tmp13 * COS_1_8 + tmp12 * SIN_1_8);
+	    outptr[DCTSIZE * 6] =
+            (int16) UNFIXH(tmp13 * SIN_1_8 - tmp12 * COS_1_8);
 
 	    tmp16 = UNFIXO((tmp6 + tmp5) * SIN_1_4);
 	    tmp15 = UNFIXO((tmp6 - tmp5) * COS_1_4);
@@ -257,10 +291,14 @@ mp_fwd_dct_fast(data2d, dest2d)
 	    tmp26 = tmp7 - tmp16;
 	    tmp17 = tmp7 + tmp16;
 
-	    outptr[DCTSIZE] = (int16) UNFIXH(tmp17 * OCOS_1_16 + tmp14 * OSIN_1_16);
-	    outptr[DCTSIZE * 7] = (int16) UNFIXH(tmp17 * OCOS_7_16 - tmp14 * OSIN_7_16);
-	    outptr[DCTSIZE * 5] = (int16) UNFIXH(tmp26 * OCOS_5_16 + tmp25 * OSIN_5_16);
-	    outptr[DCTSIZE * 3] = (int16) UNFIXH(tmp26 * OCOS_3_16 - tmp25 * OSIN_3_16);
+	    outptr[DCTSIZE] =
+            (int16) UNFIXH(tmp17 * OCOS_1_16 + tmp14 * OSIN_1_16);
+	    outptr[DCTSIZE * 7] =
+            (int16) UNFIXH(tmp17 * OCOS_7_16 - tmp14 * OSIN_7_16);
+	    outptr[DCTSIZE * 5] =
+            (int16) UNFIXH(tmp26 * OCOS_5_16 + tmp25 * OSIN_5_16);
+	    outptr[DCTSIZE * 3] =
+            (int16) UNFIXH(tmp26 * OCOS_3_16 - tmp25 * OSIN_3_16);
 
 	    inptr += DCTSIZE;	/* advance inptr to next row */
 	    outptr++;		/* advance outptr to next column */
@@ -285,6 +323,28 @@ mp_fwd_dct_fast(data2d, dest2d)
 }
 
 
+extern boolean pureDCT;
+void
+mp_fwd_dct_block2(data, dest)
+    DCTBLOCK_2D data, dest;
+/*
+ * --------------------------------------------------------------
+ *
+ * mp_fwd_dct_block2 --
+ *
+ * Select the appropriate mp_fwd_dct routine
+ *
+ * Results: None
+ *
+ * Side effects: None
+ *
+ * --------------------------------------------------------------
+ */
+{
+  if (pureDCT) reference_fwd_dct(data, dest);
+  else mp_fwd_dct_fast(data, dest);
+}
+
 /* Modifies from the MPEG2 verification coder */
 /* fdctref.c, forward discrete cosine transform, double precision           */
 
@@ -323,9 +383,6 @@ mp_fwd_dct_fast(data2d, dest2d)
 #endif
 #endif
 
-/* private data */
-static double trans_coef[8][8]; /* transform coefficients */
-
 void init_fdct()
 {
   int i, j;
@@ -340,54 +397,5 @@ void init_fdct()
   }
 }
 
-void reference_fwd_dct(block, dest)
-Block block, dest;
-{
-  int i, j, k;
-  double s;
-  double tmp[64];
-
-  if (DoLaplace) {
-    LaplaceNum++;
-  }
-
-  for (i=0; i<8; i++)
-    for (j=0; j<8; j++)
-    {
-      s = 0.0;
-
-      for (k=0; k<8; k++)
-        s += trans_coef[j][k] * block[i][k];
 
-      tmp[8*i+j] = s;
-    }
 
-  for (i=0; i<8; i++)
-    for (j=0; j<8; j++)
-    {
-      s = 0.0;
-
-      for (k=0; k<8; k++)
-        s += trans_coef[i][k] * tmp[8*k+j];
-
-      if (collect_quant) {
-	fprintf(collect_quant_fp, "%d %f\n", 8*i+j, s);
-      } 
-      if (DoLaplace) {
-	L1[LaplaceCnum][i*8+j] += s*s;
-	L2[LaplaceCnum][i*8+j] += s;
-      }
-
-
-      dest[i][j] = (int)floor(s+0.499999);
-      /*
-       * reason for adding 0.499999 instead of 0.5:
-       * s is quite often x.5 (at least for i and/or j = 0 or 4)
-       * and setting the rounding threshold exactly to 0.5 leads to an
-       * extremely high arithmetic implementation dependency of the result;
-       * s being between x.5 and x.500001 (which is now incorrectly rounded
-       * downwards instead of upwards) is assumed to occur less often
-       * (if at all)
-       */
-    }
-}
diff --git a/converter/ppm/ppmtompeg/mpeg.c b/converter/ppm/ppmtompeg/mpeg.c
index 6557f377..24d337ed 100644
--- a/converter/ppm/ppmtompeg/mpeg.c
+++ b/converter/ppm/ppmtompeg/mpeg.c
@@ -30,6 +30,7 @@
  * HEADER FILES *
  *==============*/
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 
 #include "all.h"
@@ -125,9 +126,9 @@ int32 bit_rate, buf_size;
  * INTERNAL PROCEDURE prototypes *
  *===============================*/
 
-static void ComputeDHMSTime _ANSI_ARGS_((int32 someTime, char *timeText));
-static void OpenBitRateFile _ANSI_ARGS_((void));
-static void CloseBitRateFile _ANSI_ARGS_((void));
+static void ComputeDHMSTime (int32 someTime, char *timeText);
+static void OpenBitRateFile (void);
+static void CloseBitRateFile (void);
 
 
 static void
@@ -413,11 +414,11 @@ bitioNew(const char * const outputFileName,
     else {
         const char * fileName;
 
-        asprintfN(&fileName, "%s.frame.%d", outputFileName, frameNumber);
+        pm_asprintf(&fileName, "%s.frame.%d", outputFileName, frameNumber);
 
         bbP = Bitio_New_Filename(fileName);
 
-        strfree(fileName);
+        pm_strfree(fileName);
     }
     return bbP;
 }
@@ -771,12 +772,12 @@ doFirstFrameStuff(enum frameContext const context,
             time_t now;
                     
             time(&now);
-            asprintfN(&userDataString,"MPEG stream encoded by UCB Encoder "
-                      "(mpeg_encode) v%s on %s.",
-                      VERSION, ctime(&now));
+            pm_asprintf(&userDataString,"MPEG stream encoded by UCB Encoder "
+                        "(mpeg_encode) v%s on %s.",
+                        VERSION, ctime(&now));
             userData = strdup(userDataString);
             userDataSize = strlen(userData);
-            strfree(userDataString);
+            pm_strfree(userDataString);
         }
         Mhead_GenSequenceHeader(bbP, Fsize_x, Fsize_y,
                                 /* pratio */ aspectRatio,
@@ -1322,12 +1323,12 @@ PrintStartStats(time_t               const startTime,
             GetNthInputFileName(inputSourceP, 0, &inputFileName);
             fprintf(fpointer, "FIRST FILE:  %s/%s\n", 
                     currentPath, inputFileName);
-            strfree(inputFileName);
+            pm_strfree(inputFileName);
             GetNthInputFileName(inputSourceP, inputSourceP->numInputFiles-1, 
                                 &inputFileName);
             fprintf(fpointer, "LAST FILE:  %s/%s\n", 
                     currentPath, inputFileName);
-            strfree(inputFileName);
+            pm_strfree(inputFileName);
         }    
         fprintf(fpointer, "OUTPUT:  %s\n", outputFileName);
 
@@ -1680,7 +1681,7 @@ ReadDecodedRefFrame(MpegFrame *  const frameP,
     }
 
     if ((fpointer = fopen(fileName, "rb")) == NULL) {
-        sleepN(1000);
+        pm_sleep(1000);
         if ((fpointer = fopen(fileName, "rb")) == NULL) {
             fprintf(stderr, "Cannot open %s\n", fileName);
             exit(1);
diff --git a/converter/ppm/ppmtompeg/opts.c b/converter/ppm/ppmtompeg/opts.c
index c4d0c0b3..841efdab 100644
--- a/converter/ppm/ppmtompeg/opts.c
+++ b/converter/ppm/ppmtompeg/opts.c
@@ -40,6 +40,7 @@
 #include <stdlib.h>
 #include <math.h>
 #include "opts.h"
+#include "dct.h"
 
 /*==============*
  * EXTERNALS    *
@@ -51,8 +52,7 @@ extern int32   qtable[], niqtable[];
 extern int     ZAG[];
 extern boolean printSNR, decodeRefFrames;
 
-void init_idctref _ANSI_ARGS_((void));
-void init_fdct _ANSI_ARGS_((void));
+void init_idctref (void);
 
 
 /*===================*
@@ -255,7 +255,7 @@ SetupLocalDCT(const char * const charPtr)
  *
  * SetupLaplace
  *
- *     Setup encoder to find distrubution for I-frames, and use for -snr
+ *     Setup encoder to find distribution for I-frames, and use for -snr
  *
  * RETURNS:	nothing
  *
diff --git a/converter/ppm/ppmtompeg/parallel.c b/converter/ppm/ppmtompeg/parallel.c
index e13bf221..2835c67c 100644
--- a/converter/ppm/ppmtompeg/parallel.c
+++ b/converter/ppm/ppmtompeg/parallel.c
@@ -156,7 +156,7 @@ machineDebug(const char format[], ...) {
     if (debugMachines) {
         const char * const hostname = GetHostName();
         fprintf(stderr, "%s: ---", hostname);
-        strfree(hostname);
+        pm_strfree(hostname);
         vfprintf(stderr, format, args);
         fputc('\n', stderr);
     }
@@ -175,7 +175,7 @@ errorExit(const char format[], ...) {
     va_start(args, format);
 
     fprintf(stderr, "%s: FATAL ERROR.  ", hostname);
-    strfree(hostname);
+    pm_strfree(hostname);
     vfprintf(stderr, format, args);
     fputc('\n', stderr);
 
@@ -767,13 +767,13 @@ routeFromSocketToDisk(int              const otherSock,
     ReadBytes(otherSock, bigBuffer, numBytes);
     
     /* open file to output this stuff to */
-    asprintfN(&fileName, "%s.frame.%d", outputFileName, frameNumber);
+    pm_asprintf(&fileName, "%s.frame.%d", outputFileName, frameNumber);
     filePtr = fopen(fileName, "wb");
 
     if (filePtr == NULL)
         errorExit("I/O SERVER: Could not open output file(3):  %s", fileName);
 
-    strfree(fileName);
+    pm_strfree(fileName);
 
     /* now write the bytes here */
     fwrite(bigBuffer, sizeof(char), numBytes, filePtr);
@@ -968,7 +968,7 @@ IoServer(struct inputSource * const inputSourceP,
 -----------------------------------------------------------------------------*/
     int       ioPortNum;
     int       serverSocket;
-    boolean   done;
+    bool   done;
     unsigned char   *bigBuffer;
         /* A work buffer that we keep around permanently.  We increase
            its size as needed, but never shrink it.
@@ -1104,8 +1104,8 @@ GetRemoteFrame(MpegFrame * const frameP,
 
                 if (numBytes > sizeof(buffer))
                     errorExit("Invalid message received: numBytes = %d, "
-                              "which is greater than %d\n", 
-                              numBytes, sizeof(numBytes));
+                              "which is greater than %u", 
+                              numBytes, (unsigned)sizeof(numBytes));
                 ReadBytes(clientSocket, buffer, numBytes);
 
                 fwrite(buffer, 1, numBytes, filePtr);
@@ -1209,7 +1209,7 @@ openInputFile(const char * const fileName,
             pm_message("ERROR  Couldn't read frame file '%s' errno = %d (%s)"
                        "attempt %d", 
                        fileName, errno, strerror(errno), attempts);
-            sleepN(1000);
+            pm_sleep(1000);
         }
         ++attempts;
     }
@@ -1246,11 +1246,11 @@ waitForOutputFile(void *        const inputHandle,
         }
         machineDebug("COMBINE SERVER: Wait for frame %u over", frameNumber);
 
-        asprintfN(&fileName, "%s.frame.%u", outputFileName, frameNumber);
+        pm_asprintf(&fileName, "%s.frame.%u", outputFileName, frameNumber);
 
         openInputFile(fileName, ifPP);
 
-        strfree(fileName);
+        pm_strfree(fileName);
     }
 }
 
@@ -1263,11 +1263,11 @@ unlinkFile(void *       const inputHandle,
     if (!keepTempFiles) {
         const char * fileName;
 
-        asprintfN(&fileName, "%s.frame.%u", outputFileName, frameNumber);
+        pm_asprintf(&fileName, "%s.frame.%u", outputFileName, frameNumber);
 
         unlink(fileName);
 
-        strfree(fileName);
+        pm_strfree(fileName);
     }
 }
 
@@ -1340,13 +1340,13 @@ startCombineServer(const char * const encoderName,
     int          otherSock;
     const char * error;
 
-    snprintf(command, sizeof(command), 
-             "%s %s -max_machines %d -output_server %s %d %d %s",
-             encoderName, 
-             debugMachines ? "-debug_machines" : "",
-             numMachines, masterHostName, masterPortNum, 
-             numInputFiles, paramFileName);
-
+    pm_snprintf(command, sizeof(command), 
+                "%s %s -max_machines %d -output_server %s %d %d %s",
+                encoderName, 
+                debugMachines ? "-debug_machines" : "",
+                numMachines, masterHostName, masterPortNum, 
+                numInputFiles, paramFileName);
+    
     machineDebug("MASTER: Starting combine server with shell command '%s'",
                  command);
 
@@ -1382,12 +1382,12 @@ startDecodeServer(const char * const encoderName,
     int          otherSock;
     const char * error;
 
-    snprintf(command, sizeof(command), 
-             "%s %s -max_machines %d -decode_server %s %d %d %s",
-             encoder_name, 
-             debugMachines ? "-debug_machines" : "",
-             numMachines, masterHostName, masterPortNum,
-             numInputFiles, paramFileName);
+    pm_snprintf(command, sizeof(command), 
+                "%s %s -max_machines %d -decode_server %s %d %d %s",
+                encoder_name, 
+                debugMachines ? "-debug_machines" : "",
+                numMachines, masterHostName, masterPortNum,
+                numInputFiles, paramFileName);
 
     machineDebug("MASTER: Starting decode server with shell command '%s'",
                  command);
@@ -1680,23 +1680,23 @@ startChildren(struct scheduler *   const schedulerP,
                 }
                 --childrenLeftCurrentIoServer;
             } 
-            snprintf(command, sizeof(command),
-                     "%s %s -l %s %s "
-                     "%s %s -child %s %d %d %d %d %d %d "
-                     "-frames %d %d %s",
-                     rsh,
-                     machineName[childNum], userName[childNum],
-                     beNice ? "nice" : "",
-                     executable[childNum],
-                     debugMachines ? "-debug_machines" : "",
-                     masterHostName, masterPortNum, 
-                     remote[childNum] ? ioPortNum[numIoServers-1] : 0,
-                     combinePortNum, decodePortNum, childNum,
-                     remote[childNum] ? 1 : 0,
-                     startFrame, startFrame + nFrames - 1,
-                     remote[childNum] ? 
-                         remoteParamFile[childNum] : paramFileName
-            );
+            pm_snprintf(command, sizeof(command),
+                        "%s %s -l %s %s "
+                        "%s %s -child %s %d %d %d %d %d %d "
+                        "-frames %d %d %s",
+                        rsh,
+                        machineName[childNum], userName[childNum],
+                        beNice ? "nice" : "",
+                        executable[childNum],
+                        debugMachines ? "-debug_machines" : "",
+                        masterHostName, masterPortNum, 
+                        remote[childNum] ? ioPortNum[numIoServers-1] : 0,
+                        combinePortNum, decodePortNum, childNum,
+                        remote[childNum] ? 1 : 0,
+                        startFrame, startFrame + nFrames - 1,
+                        remote[childNum] ? 
+                          remoteParamFile[childNum] : paramFileName
+                );
         
             machineDebug("MASTER: Starting child server "
                          "with shell command '%s'", command);
@@ -2070,7 +2070,7 @@ MasterServer(struct inputSource * const inputSourceP,
         fclose(statFile);
 
     free(childState);
-    strfree(hostName);
+    pm_strfree(hostName);
 }
 
 
diff --git a/converter/ppm/ppmtompeg/param.c b/converter/ppm/ppmtompeg/param.c
index a145d33c..45605981 100644
--- a/converter/ppm/ppmtompeg/param.c
+++ b/converter/ppm/ppmtompeg/param.c
@@ -73,14 +73,14 @@
 #define LAST_OPTION           15
 
 /* put any non-required options after LAST_OPTION */
-#define OPTION_RESIZE	      16
+#define OPTION_RESIZE         16
 #define OPTION_IO_CONVERT     17
 #define OPTION_SLAVE_CONVERT  18
-#define OPTION_IQTABLE	      19
-#define OPTION_NIQTABLE	      20
+#define OPTION_IQTABLE        19
+#define OPTION_NIQTABLE       20
 #define OPTION_FRAME_RATE     21
 #define OPTION_ASPECT_RATIO   22
-#define OPTION_YUV_SIZE	      23
+#define OPTION_YUV_SIZE       23
 #define OPTION_SPECIFICS      24
 #define OPTION_DEFS_SPECIFICS 25
 #define OPTION_BUFFER_SIZE    26
@@ -98,48 +98,49 @@
  * GLOBAL VARIABLES *
  *==================*/
 
-extern char currentPath[MAXPATHLEN];
-char	outputFileName[256];
-int	outputWidth, outputHeight;
+char outputFileName[256];
+int outputWidth, outputHeight;
 char inputConversion[1024];
 char ioConversion[1024];
 char slaveConversion[1024];
 char yuvConversion[256];
 char specificsFile[256],specificsDefines[1024]="";
-boolean GammaCorrection=FALSE;
-float   GammaValue;
+bool GammaCorrection=FALSE;
+float GammaValue;
 char userDataFileName[256]={0};
-boolean specificsOn = FALSE;
+bool specificsOn = FALSE;
 char currentGOPPath[MAXPATHLEN];
 char currentFramePath[MAXPATHLEN];
-boolean keepTempFiles;
+bool keepTempFiles;
+int numMachines;
+char machineName[MAX_MACHINES][256];
+char userName[MAX_MACHINES][256];
+char executable[MAX_MACHINES][1024];
+char remoteParamFile[MAX_MACHINES][1024];
+bool remote[MAX_MACHINES];
+int mult_seq_headers = 0;  /* 0 for none, N for header/N GOPs */
+
+
+extern char currentPath[MAXPATHLEN];
 
 static const char * const optionText[LAST_OPTION+1] = { 
     "GOP_SIZE", "PATTERN", "PIXEL", "PQSCALE",
     "OUTPUT", "RANGE", "PSEARCH_ALG", "IQSCALE", "INPUT_DIR",
     "INPUT_CONVERT", "INPUT", "BQSCALE", "BASE_FILE_FORMAT",
     "SLICES_PER_FRAME", "BSEARCH_ALG", "REFERENCE_FRAME"};
-static boolean optionSeen[NUM_OPTIONS+1];
+static bool optionSeen[NUM_OPTIONS+1];
     /* optionSeen[x] means we have seen option x in the parameter file we've
        been reading.
     */
 
-int numMachines;
-char	machineName[MAX_MACHINES][256];
-char	userName[MAX_MACHINES][256];
-char	executable[MAX_MACHINES][1024];
-char	remoteParamFile[MAX_MACHINES][1024];
-boolean	remote[MAX_MACHINES];
-int mult_seq_headers = 0;  /* 0 for none, N for header/N GOPs */
-
 
 /*===========================================================================*
  *
  * SkipSpacesTabs
  *
- *	skip all spaces and tabs
+ *  skip all spaces and tabs
  *
- * RETURNS:	point to next character not a space or tab
+ * RETURNS: point to next character not a space or tab
  *
  * SIDE EFFECTS:    none
  *
@@ -172,13 +173,13 @@ static int
 GetAspectRatio(const char * const p)
 {
   float   ratio;
-  int	  ttRatio;
+  int     ttRatio;
   int     retval;
 
   sscanf(p, "%f", &ratio);
   ttRatio = (int)(0.5+ratio*10000.0);
 
-  if ( ttRatio == 10000 )	      retval = 1;
+  if ( ttRatio == 10000 )         retval = 1;
   else if ( ttRatio ==  6735 )    retval = 2;
   else if ( ttRatio ==  7031 )    retval = 3;
   else if ( ttRatio ==  7615 )    retval = 4;
@@ -205,9 +206,9 @@ GetAspectRatio(const char * const p)
  *
  * ReadMachineNames
  *
- *	read a list of machine names for parallel execution
+ *  read a list of machine names for parallel execution
  *
- * RETURNS:	nothing
+ * RETURNS: nothing
  *
  * SIDE EFFECTS:    machine info updated
  *
@@ -219,7 +220,7 @@ ReadMachineNames(FILE * const fpointer)
   const char *charPtr;
 
   while ( (fgets(input, 256, fpointer) != NULL) &&
-	 (strncmp(input, "END_PARALLEL", 12) != 0) ) {
+     (strncmp(input, "END_PARALLEL", 12) != 0) ) {
     if ( input[0] == '#' || input[0] == '\n') {
       continue;
     }
@@ -229,13 +230,13 @@ ReadMachineNames(FILE * const fpointer)
       remote[numMachines] = TRUE;
 
       sscanf(charPtr, "%s %s %s %s", machineName[numMachines],
-	     userName[numMachines], executable[numMachines],
-	     remoteParamFile[numMachines]);
+         userName[numMachines], executable[numMachines],
+         remoteParamFile[numMachines]);
     } else {
       remote[numMachines] = FALSE;
 
       sscanf(input, "%s %s %s", machineName[numMachines],
-	     userName[numMachines], executable[numMachines]);
+         userName[numMachines], executable[numMachines]);
     }
 
     numMachines++;
@@ -259,13 +260,13 @@ static int
 GetFrameRate(const char * const p)
 {
   float   rate;
-  int	  thouRate;
+  int     thouRate;
   int     retval;
 
   sscanf(p, "%f", &rate);
   thouRate = (int)(0.5+1000.0*rate);
 
-  if ( thouRate == 23976 )	       retval = 1;
+  if ( thouRate == 23976 )         retval = 1;
   else if ( thouRate == 24000 )    retval = 2;
   else if ( thouRate == 25000 )    retval = 3;
   else if ( thouRate == 29970 )    retval = 4;
@@ -988,7 +989,7 @@ ReadParamFile(const char *         const fileName,
                            paramP);
               /* May read additional lines from file */
 
-      strfree(input);
+      pm_strfree(input);
   }
 
   fclose(fpointer);
diff --git a/converter/ppm/ppmtompeg/ppmtompeg.c b/converter/ppm/ppmtompeg/ppmtompeg.c
index f53ffea9..cd94db39 100644
--- a/converter/ppm/ppmtompeg/ppmtompeg.c
+++ b/converter/ppm/ppmtompeg/ppmtompeg.c
@@ -30,12 +30,14 @@
  * HEADER FILES *
  *==============*/
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 
 #include <assert.h>
 
 #include "all.h"
 #include "mtypes.h"
+#include "dct.h"
 #include "mpeg.h"
 #include "motion_search.h"
 #include "prototypes.h"
@@ -57,14 +59,15 @@
 
 #include <time.h>
 
-int main _ANSI_ARGS_((int argc, char **argv));
+int main (int argc, char **argv);
 
 
 /*==================*
  * GLOBAL VARIABLES *
  *==================*/
 
-boolean showBitRatePerFrame;
+bool showBitRatePerFrame;
+bool computeMVHist = FALSE;
 boolean frameSummary;
 
 extern time_t IOtime;
@@ -78,9 +81,7 @@ boolean noFrameSummaryOption = FALSE;
 boolean debugSockets = FALSE;
 boolean debugMachines = FALSE;
 boolean bitRateInfoOption = FALSE;
-boolean computeMVHist = FALSE;
 int     baseFormat;
-extern  boolean specificsOn;
 extern  FrameSpecList *fsl;
 boolean pureDCT=FALSE;
 char    encoder_name[1024];
@@ -91,8 +92,7 @@ const char * hostname;
  * External PROCEDURE prototypes  *
  *================================*/
 
-void init_idctref _ANSI_ARGS_((void));
-void init_fdct _ANSI_ARGS_((void));
+void init_idctref (void);
 
 
 struct cmdlineInfo {
@@ -335,9 +335,9 @@ announceJob(enum frameContext const context,
         const char * combineDest;
 
         if (context == CONTEXT_JUSTFRAMES)
-            asprintfN(&outputDest, "to individual frame files");
+            pm_asprintf(&outputDest, "to individual frame files");
         else
-            asprintfN(&outputDest, "to file '%s'", outputFileName);
+            pm_asprintf(&outputDest, "to file '%s'", outputFileName);
 
         if (childProcess)
             combineDest = strdup("for delivery to combine server");
@@ -347,8 +347,8 @@ announceJob(enum frameContext const context,
         pm_message("%s:  ENCODING FRAMES %u-%u to %s %s",
                    hostname, frameStart, frameEnd, outputDest, combineDest);
 
-        strfree(combineDest);
-        strfree(outputDest);
+        pm_strfree(combineDest);
+        pm_strfree(outputDest);
     }
 }
 
@@ -540,15 +540,15 @@ getUserFrameFile(void *       const handle,
 
         GetNthInputFileName(inputSourceP, frameNumber, &inputFileName);
         
-        asprintfN(&fileName, "%s/%s", currentFramePath, inputFileName);
+        pm_asprintf(&fileName, "%s/%s", currentFramePath, inputFileName);
         
         *ifPP = fopen(fileName, "rb");
         if (*ifPP == NULL)
             pm_error("Unable to open file '%s'.  Errno = %d (%s)",
                      fileName, errno, strerror(errno));
         
-        strfree(inputFileName);
-        strfree(fileName);
+        pm_strfree(inputFileName);
+        pm_strfree(fileName);
     }
 }
 
@@ -702,7 +702,7 @@ main(int argc, char **argv) {
     } 
     Frame_Exit();
         
-    strfree(hostname);
+    pm_strfree(hostname);
 
     return 0;
 }
diff --git a/converter/ppm/ppmtompeg/psearch.c b/converter/ppm/ppmtompeg/psearch.c
index 83c62d04..3cca1241 100644
--- a/converter/ppm/ppmtompeg/psearch.c
+++ b/converter/ppm/ppmtompeg/psearch.c
@@ -962,7 +962,7 @@ ShowBFMVHistogram(fpointer)
  * Changed copyrights
  *
  * Revision 1.8  1994/12/07  00:40:36  smoot
- * Added seperate P and B search ranges
+ * Added separate P and B search ranges
  *
  * Revision 1.7  1994/11/12  02:09:45  eyhung
  * full pixel bug
diff --git a/converter/ppm/ppmtompeg/psocket.c b/converter/ppm/ppmtompeg/psocket.c
index 707f1d84..6a50dc27 100644
--- a/converter/ppm/ppmtompeg/psocket.c
+++ b/converter/ppm/ppmtompeg/psocket.c
@@ -91,7 +91,7 @@ errorExit(const char format[], ...) {
     va_start(args, format);
 
     fprintf(stderr, "%s: FATAL ERROR.  ", hostname);
-    strfree(hostname);
+    pm_strfree(hostname);
     vfprintf(stderr, format, args);
     fputc('\n', stderr);
 
@@ -272,13 +272,13 @@ ConnectToSocket(const char *      const machineName,
     if ((*hostEnt) == NULL) {
         (*hostEnt) = gethostbyname(machineName);
         if ((*hostEnt) == NULL)
-            asprintfN(errorP, "Couldn't get host by name (%s)", machineName);
+            pm_asprintf(errorP, "Couldn't get host by name (%s)", machineName);
     }
     if (!*errorP) {
         rc = socket(AF_INET, SOCK_STREAM, 0);
         if (rc < 0)
-            asprintfN(errorP, "socket() failed with errno %d (%s)", 
-                      errno, strerror(errno));
+            pm_asprintf(errorP, "socket() failed with errno %d (%s)", 
+                        errno, strerror(errno));
         else {
             int const socketFd = rc;
             
@@ -298,10 +298,10 @@ ConnectToSocket(const char *      const machineName,
                          sizeof(struct sockaddr));
             
             if (rc != 0)
-                asprintfN(errorP, 
-                          "connect() to host '%s', port %d failed with "
-                          "errno %d (%s)",
-                          machineName, portNum, errno, strerror(errno));
+                pm_asprintf(errorP, 
+                            "connect() to host '%s', port %d failed with "
+                            "errno %d (%s)",
+                            machineName, portNum, errno, strerror(errno));
             else {
                 *errorP = NULL;
                 *socketFdP = socketFd;
@@ -364,14 +364,14 @@ bindToUnusedPort(int              const socketFd,
             foundPort = TRUE;
             *portNumP = trialPortNum;
         } else if (!portInUseErrno(errno))
-            asprintfN(errorP, "bind() of TCP port number %hu failed "
-                      "with errno %d (%s)", 
-                      trialPortNum, errno, strerror(errno));
+            pm_asprintf(errorP, "bind() of TCP port number %hu failed "
+                        "with errno %d (%s)", 
+                        trialPortNum, errno, strerror(errno));
     }
     
     if (!*errorP && !foundPort)
-        asprintfN(errorP, "Unable to find a free port.  Every TCP port "
-                  "in the range 2048-16383 is in use");
+        pm_asprintf(errorP, "Unable to find a free port.  Every TCP port "
+                    "in the range 2048-16383 is in use");
 }
 
 
@@ -392,10 +392,10 @@ CreateListeningSocket(int *         const socketP,
     
     rc = socket(AF_INET, SOCK_STREAM, 0);
     if (rc < 0)
-        asprintfN(errorP,
-                  "Unable to create socket.  "
-                  "socket() failed with errno %d (%s)",
-                  errno, strerror(errno));
+        pm_asprintf(errorP,
+                    "Unable to create socket.  "
+                    "socket() failed with errno %d (%s)",
+                    errno, strerror(errno));
     else {
         int const socketFd = rc;
 
@@ -414,9 +414,9 @@ CreateListeningSocket(int *         const socketP,
             */
             rc = listen(socketFd, SOMAXCONN);
             if (rc != 0)
-                asprintfN(errorP, "Unable to listen on TCP socket.  "
-                          "listen() fails with errno %d (%s)", 
-                          errno, strerror(errno));
+                pm_asprintf(errorP, "Unable to listen on TCP socket.  "
+                            "listen() fails with errno %d (%s)", 
+                            errno, strerror(errno));
         }
         if (*errorP)
             close(socketFd);
@@ -443,8 +443,8 @@ AcceptConnection(int           const listenSocketFd,
     rc = accept(listenSocketFd, &otherSocket, &otherSize);
 
     if (rc < 0)
-        asprintfN(errorP, "accept() failed with errno %d (%s).  ",
-                  errno, strerror(errno));
+        pm_asprintf(errorP, "accept() failed with errno %d (%s).  ",
+                    errno, strerror(errno));
     else {
         *connectSocketFdP = rc;
         *errorP = NULL;
diff --git a/converter/ppm/ppmtompeg/rate.c b/converter/ppm/ppmtompeg/rate.c
index 1a44cb95..c775e055 100644
--- a/converter/ppm/ppmtompeg/rate.c
+++ b/converter/ppm/ppmtompeg/rate.c
@@ -204,13 +204,13 @@ extern int framePatternLen;
  * INTERNAL PROCEDURE prototypes *
  *===============================*/
 
-int initGOPRateControl _ANSI_ARGS_((void));
-int determineMBCount _ANSI_ARGS_((void));
-void checkBufferFullness _ANSI_ARGS_((int count));
-void checkSpatialActivity _ANSI_ARGS_((Block blk0, Block blk1, Block blk2, Block blk3));
-void incNumBlocks _ANSI_ARGS_((int num));
-void calculateVBVDelay _ANSI_ARGS_((int num));
-int BlockExperiments  _ANSI_ARGS_((int16 *OrigBlock, int16 *NewBlock, int control));
+int initGOPRateControl (void);
+int determineMBCount (void);
+void checkBufferFullness (int count);
+void checkSpatialActivity (Block blk0, Block blk1, Block blk2, Block blk3);
+void incNumBlocks (int num);
+void calculateVBVDelay (int num);
+int BlockExperiments  (int16 *OrigBlock, int16 *NewBlock, int control);
      
      
 
@@ -236,7 +236,7 @@ analyzePattern(const char *  const framePattern,
         case 'p': ++*gop_pP; break;
         case 'b': ++*gop_bP; break;
         default:
-            asprintfN(errorP, "Bad pattern - not composed of i, p, and b");
+            pm_asprintf(errorP, "Bad pattern - not composed of i, p, and b");
         }
     }
     assert(*gop_xP == *gop_iP + *gop_pP + *gop_bP);
@@ -285,7 +285,7 @@ initRateControl(bool const wantUnderflowWarning,
     if (error) {
         pm_message("Unable to set up rate control.  Switching to variable.  "
                    "%s", error);
-        strfree(error);
+        pm_strfree(error);
         RateControlMode = VARIABLE_RATE;
         return -1;
     }
@@ -595,7 +595,7 @@ updateRateControl(int const type) {
  * MB_RateOut
  *
  *      Prints out sampling of MB rate control data.  Every "nth" block
- *	stats are printed, with "n" controled by global RC_MB_SAMPLE_RATE
+ *	stats are printed, with "n" controlled by global RC_MB_SAMPLE_RATE
  *	(NB. "skipped" blocks do not go through this function and thus do not
  *		show up in the sample )
  *
diff --git a/converter/ppm/ppmtompeg/readframe.c b/converter/ppm/ppmtompeg/readframe.c
index d1423c1f..23752706 100644
--- a/converter/ppm/ppmtompeg/readframe.c
+++ b/converter/ppm/ppmtompeg/readframe.c
@@ -67,9 +67,6 @@ struct YuvLine {
  * Global VARIABLES *
  *==================*/
 
-extern boolean GammaCorrection;
-extern float GammaValue;
-extern int outputWidth,outputHeight;
 boolean resizeFrame;
 const char *CurrFile;
 
@@ -77,19 +74,19 @@ const char *CurrFile;
  * INTERNAL PROCEDURE prototypes *
  *===============================*/
 
-static void ReadEYUV _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer,
-                 int width, int height));
-static void ReadAYUV _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer,
-                 int width, int height));
-static void SeparateLine _ANSI_ARGS_((FILE *fpointer, struct YuvLine *lineptr,
-                     int width));
-static void ReadY _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer,
-                 int width, int height));
-static void ReadSub4 _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer,
-                  int width, int height));
-static void DoGamma  _ANSI_ARGS_((MpegFrame *mf, int width, int height));
+static void ReadEYUV (MpegFrame * mf, FILE *fpointer,
+                 int width, int height);
+static void ReadAYUV (MpegFrame * mf, FILE *fpointer,
+                 int width, int height);
+static void SeparateLine (FILE *fpointer, struct YuvLine *lineptr,
+                     int width);
+static void ReadY (MpegFrame * mf, FILE *fpointer,
+                 int width, int height);
+static void ReadSub4 (MpegFrame * mf, FILE *fpointer,
+                  int width, int height);
+static void DoGamma  (MpegFrame *mf, int width, int height);
 
-static void DoKillDim _ANSI_ARGS_((MpegFrame *mf, int w, int h));
+static void DoKillDim (MpegFrame *mf, int w, int h);
 
 #define safe_fread(ptr,sz,len,fileptr)                           \
     if ((safe_read_count=fread(ptr,sz,len,fileptr))!=sz*len) {   \
@@ -155,7 +152,7 @@ openFile(struct inputSource * const inputSourceP,
         
         GetNthInputFileName(inputSourceP, frameNumber, &fileName);
         
-        asprintfN(&fullFileName, "%s/%s", currentPath, fileName);
+        pm_asprintf(&fullFileName, "%s/%s", currentPath, fileName);
         
         CurrFile = fullFileName;
         
@@ -207,8 +204,8 @@ openFile(struct inputSource * const inputSourceP,
             if (baseFormat == JMOVIE_FILE_TYPE)
                 unlink(fullFileName);
         }
-        strfree(fullFileName);
-        strfree(fileName);
+        pm_strfree(fullFileName);
+        pm_strfree(fileName);
     }
 }
 
@@ -377,10 +374,10 @@ ReadFrame(MpegFrame *          const frameP,
 void
 SetFileType(const char * const conversion)
 {
-    if ( strcmp(conversion, "*") == 0 ) {
-    fileType = BASE_FILE_TYPE;
+    if (streq(conversion, "*")) {
+        fileType = BASE_FILE_TYPE;
     } else {
-    fileType = ANY_FILE_TYPE;
+        fileType = ANY_FILE_TYPE;
     }
 }
 
@@ -399,23 +396,23 @@ SetFileType(const char * const conversion)
 void
 SetFileFormat(const char * const format)
 {
-    if ( strcmp(format, "PPM") == 0 ) {
-    baseFormat = PNM_FILE_TYPE;
-    } else if ( strcmp(format, "YUV") == 0 ) {
-    baseFormat = YUV_FILE_TYPE;
-    } else if ( strcmp(format, "Y") == 0 ) {
-    baseFormat = Y_FILE_TYPE;
-    } else if ( strcmp(format, "PNM") == 0 ) {
-    baseFormat = PNM_FILE_TYPE;
-    } else if (( strcmp(format, "JPEG") == 0 ) || ( strcmp(format, "JPG") == 0 )) {
-    baseFormat = JPEG_FILE_TYPE;
-    } else if ( strcmp(format, "JMOVIE") == 0 ) {
-    baseFormat = JMOVIE_FILE_TYPE;
-    } else if ( strcmp(format, "SUB4") == 0 ) {
-    baseFormat = SUB4_FILE_TYPE;
+    if (streq(format, "PPM")) {
+        baseFormat = PNM_FILE_TYPE;
+    } else if (streq(format, "YUV")) {
+        baseFormat = YUV_FILE_TYPE;
+    } else if (streq(format, "Y")) {
+        baseFormat = Y_FILE_TYPE;
+    } else if (streq(format, "PNM")) {
+        baseFormat = PNM_FILE_TYPE;
+    } else if (streq(format, "JPEG") || streq(format, "JPG")) {
+        baseFormat = JPEG_FILE_TYPE;
+    } else if (streq(format, "JMOVIE")) {
+        baseFormat = JMOVIE_FILE_TYPE;
+    } else if (streq(format, "SUB4")) {
+        baseFormat = SUB4_FILE_TYPE;
     } else {
-    fprintf(stderr, "ERROR:  Invalid file format:  %s\n", format);
-    exit(1);
+        fprintf(stderr, "ERROR:  Invalid file format:  %s\n", format);
+        exit(1);
     }
 }
 
@@ -435,9 +432,9 @@ ReadIOConvert(struct inputSource * const inputSourceP,
 
     GetNthInputFileName(inputSourceP, frameNumber, &fileName);
 
-    asprintfN(&fullFileName, "%s/%s", currentPath, fileName);
+    pm_asprintf(&fullFileName, "%s/%s", currentPath, fileName);
 
-    if ( strcmp(ioConversion, "*") == 0 ) {
+    if (streq(ioConversion, "*")) {
         char buff[1024];
         ifp = fopen(fullFileName, "rb");
         sprintf(buff,"fopen \"%s\"",fullFileName);
@@ -483,8 +480,8 @@ ReadIOConvert(struct inputSource * const inputSourceP,
         exit(1);
     }
 
-    strfree(fullFileName);
-    strfree(fileName);
+    pm_strfree(fullFileName);
+    pm_strfree(fileName);
 
     return ifp;
 }
@@ -828,7 +825,7 @@ MpegFrame *mf;
 int w,h;
 {
   static int GammaVal[256];
-  static boolean init_done=FALSE;
+  static bool init_done=FALSE;
   int i,j;
 
   if (!init_done) {
@@ -882,7 +879,7 @@ DoKillDim(mf, w, h)
 MpegFrame *mf;
 int w,h;
 {
-  static boolean init_done=FALSE;
+  static bool init_done=FALSE;
   static unsigned char mapper[256];
   register int i,j;
   double slope, intercept;
diff --git a/converter/ppm/ppmtompeg/rgbtoycc.c b/converter/ppm/ppmtompeg/rgbtoycc.c
index 766b8902..2dd1899a 100644
--- a/converter/ppm/ppmtompeg/rgbtoycc.c
+++ b/converter/ppm/ppmtompeg/rgbtoycc.c
@@ -84,11 +84,11 @@ compute_mult_tables(const pixval maxval) {
         if (mult299 == NULL || mult587 == NULL || mult114 == NULL ||
             mult16874 == NULL || mult33126 == NULL || mult5 == NULL ||
             mult41869 == NULL || mult08131 == NULL) 
-            pm_error("Unable to allocate storage for arithmetic tables.\n"
+            pm_error("Unable to allocate storage for arithmetic tables.  "
                      "We need %d bytes, which is the maxval of the input "
-                     "image, plus 1,\n"
+                     "image, plus 1, "
                      "times the storage size of a floating point value.", 
-                     8 * (table_maxval+1)*sizeof(float));
+                     (unsigned)(8 * (table_maxval+1)*sizeof(float)));
 
         {
             int index;
diff --git a/converter/ppm/ppmtompeg/specifics.c b/converter/ppm/ppmtompeg/specifics.c
index 38e8fc43..fb5e3649 100644
--- a/converter/ppm/ppmtompeg/specifics.c
+++ b/converter/ppm/ppmtompeg/specifics.c
@@ -46,28 +46,26 @@
 #include <stdio.h>
 #include <string.h>
 #include "prototypes.h"
+#include "param.h"
 
 /*==================*
  * GLOBAL VARIABLES *
  *==================*/
 
-extern boolean specificsOn;
-extern char specificsFile[];
-extern char specificsDefines[];
 FrameSpecList *fsl;
 
 /*=====================*
  * Internal procedures *
  *=====================*/
 
-void Parse_Specifics_File _ANSI_ARGS_((FILE *fp));
-void Parse_Specifics_File_v1 _ANSI_ARGS_((FILE *fp));
-void Parse_Specifics_File_v2 _ANSI_ARGS_((FILE *fp));
-FrameSpecList *MakeFslEntry _ANSI_ARGS_((void));
-void AddSlc _ANSI_ARGS_((FrameSpecList *c,int snum, int qs));
-Block_Specifics *AddBs _ANSI_ARGS_((FrameSpecList *c,int bnum, 
-				    boolean rel, int qs));
-FrameSpecList *MakeFslEntry _ANSI_ARGS_((void));
+void Parse_Specifics_File (FILE *fp);
+void Parse_Specifics_File_v1 (FILE *fp);
+void Parse_Specifics_File_v2 (FILE *fp);
+FrameSpecList *MakeFslEntry (void);
+void AddSlc (FrameSpecList *c,int snum, int qs);
+Block_Specifics *AddBs (FrameSpecList *c,int bnum, 
+				    boolean rel, int qs);
+FrameSpecList *MakeFslEntry (void);
 #define my_upper(c) (((c>='a') && (c<='z')) ? (c-'a'+'A') : c)
 #define CvtType(x) ReallyCvt(my_upper(x))
 #define ReallyCvt(x) (x=='I' ? 1 : (x=='P')?2: ((x=='B')?3:-1))
@@ -91,7 +89,7 @@ version N
   Specify the version of the specifics file format (this is 1)
 frame N T M
   Sets frame number N to type T and Qscale M
-  (type T is I,B,P,other, other means unspec.  I recomend - )
+  (type T is I,B,P,other, other means unspec.  I recommend - )
 slice M Q
   Sets slice M (in frame N as defined by a previous frame command)
   to qscale Q
@@ -559,7 +557,7 @@ int start_qs;
       } else {
 	/* if not next, check from the start.
 	   (this allows people to put frames out of order,even
-	   though the spec doesnt allow it.) */
+	   though the spec doesn't allow it.) */
 	tmp = fsl;
 	found_it = FALSE;
 	while (tmp != (FrameSpecList *) NULL) {
diff --git a/converter/ppm/ppmtopcx.c b/converter/ppm/ppmtopcx.c
index edc44149..fa68edc5 100644
--- a/converter/ppm/ppmtopcx.c
+++ b/converter/ppm/ppmtopcx.c
@@ -12,6 +12,10 @@
 **
 ** 11/Dec/94: first version
 ** 12/Dec/94: added handling of "packed" format (16 colors or less)
+** 
+** ZSoft PCX File Format Technical Reference Manual
+** http://bespin.org/~qz/pc-gpe/pcx.txt
+** http://web.archive.org/web/20100206055706/http://www.qzx.com/pc-gpe/pcx.txt
 */
 #include <assert.h>
 
@@ -79,7 +83,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -111,7 +115,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (!xposSpec)
@@ -158,6 +162,8 @@ parseCommandLine(int argc, char ** argv,
         pm_error("Program takes at most one argument "
                  "(input file specification).  You specified %d",
                  argc-1);
+
+    free(option_def);
 }
 
 
@@ -632,6 +638,16 @@ generateStandardPalette(struct pcxCmapEntry ** const pcxcmapP,
         putPcxColorInHash(cht, pcxColor, colorIndex, maxval);
     }
 
+    /* Set remaining slots in palette to black.  The values are not
+       meaningful, but this suppresses a Valgrind warning about our writing
+       undefined values to the file and makes our output constant with input.
+    */
+    for ( ; colorIndex < MAXCOLORS; ++colorIndex) {
+        pcxcmap[colorIndex].r = 0;
+        pcxcmap[colorIndex].g = 0;
+        pcxcmap[colorIndex].b = 0;
+    }
+
     *chtP = cht;
     *colorsP = stdPaletteSize;
 }
diff --git a/converter/ppm/ppmtopict.c b/converter/ppm/ppmtopict.c
index 1b9f2d5c..36464b6c 100644
--- a/converter/ppm/ppmtopict.c
+++ b/converter/ppm/ppmtopict.c
@@ -11,464 +11,462 @@
 ** implied warranty.
 */
 
+#include <assert.h>
+#include "pm_c_util.h"
+#include "pm.h"
 #include "ppm.h"
 
-#define HEADER_SIZE		512
+#define HEADER_SIZE     512
 
-#define	RUN_THRESH		3
-#define	MAX_RUN			128		/* 0xff = 2, 0xfe = 3, etc */
-#define	MAX_COUNT		128		/* 0x00 = 1, 0x01 = 2, etc */
+#define RUN_THRESH      3
+#define MAX_RUN         128     /* 0xff = 2, 0xfe = 3, etc */
+#define MAX_COUNT       128     /* 0x00 = 1, 0x01 = 2, etc */
 
 /* Opcodes */
-#define PICT_NOP		0x00
-#define PICT_clipRgn		0x01
-#define PICT_bkPat		0x02
-#define PICT_txFont		0x03
-#define PICT_txFace		0x04
-#define PICT_txMode		0x05
-#define PICT_spExtra		0x06
-#define PICT_pnSize		0x07
-#define PICT_pnMode		0x08
-#define PICT_pnPat		0x09
-#define PICT_thePat		0x0A
-#define PICT_ovSize		0x0B
-#define PICT_origin		0x0C
-#define PICT_txSize		0x0D
-#define PICT_fgColor		0x0E
-#define PICT_bkColor		0x0F
-#define PICT_txRatio		0x10
-#define PICT_picVersion		0x11
-#define	PICT_blPixPat		0x12
-#define	PICT_pnPixPat		0x13
-#define	PICT_fillPixPat		0x14
-#define	PICT_pnLocHFrac		0x15
-#define	PICT_chExtra		0x16
-#define	PICT_rgbFgCol		0x1A
-#define	PICT_rgbBkCol		0x1B
-#define	PICT_hiliteMode		0x1C
-#define	PICT_hiliteColor	0x1D
-#define	PICT_defHilite		0x1E
-#define	PICT_opColor		0x1F
-#define PICT_line		0x20
-#define PICT_line_from		0x21
-#define PICT_short_line		0x22
-#define PICT_short_line_from	0x23
-#define PICT_long_text		0x28
-#define PICT_DH_text		0x29
-#define PICT_DV_text		0x2A
-#define PICT_DHDV_text		0x2B
-#define PICT_frameRect		0x30
-#define PICT_paintRect		0x31
-#define PICT_eraseRect		0x32
-#define PICT_invertRect		0x33
-#define PICT_fillRect		0x34
-#define PICT_frameSameRect	0x38
-#define PICT_paintSameRect	0x39
-#define PICT_eraseSameRect	0x3A
-#define PICT_invertSameRect	0x3B
-#define PICT_fillSameRect	0x3C
-#define PICT_frameRRect		0x40
-#define PICT_paintRRect		0x41
-#define PICT_eraseRRect		0x42
-#define PICT_invertRRect	0x43
-#define PICT_fillRRect		0x44
-#define PICT_frameSameRRect	0x48
-#define PICT_paintSameRRect	0x49
-#define PICT_eraseSameRRect	0x4A
-#define PICT_invertSameRRect	0x4B
-#define PICT_fillSameRRect	0x4C
-#define PICT_frameOval		0x50
-#define PICT_paintOval		0x51
-#define PICT_eraseOval		0x52
-#define PICT_invertOval		0x53
-#define PICT_fillOval		0x54
-#define PICT_frameSameOval	0x58
-#define PICT_paintSameOval	0x59
-#define PICT_eraseSameOval	0x5A
-#define PICT_invertSameOval	0x5B
-#define PICT_fillSameOval	0x5C
-#define PICT_frameArc		0x60
-#define PICT_paintArc		0x61
-#define PICT_eraseArc		0x62
-#define PICT_invertArc		0x63
-#define PICT_fillArc		0x64
-#define PICT_frameSameArc	0x68
-#define PICT_paintSameArc	0x69
-#define PICT_eraseSameArc	0x6A
-#define PICT_invertSameArc	0x6B
-#define PICT_fillSameArc	0x6C
-#define PICT_framePoly		0x70
-#define PICT_paintPoly		0x71
-#define PICT_erasePoly		0x72
-#define PICT_invertPoly		0x73
-#define PICT_fillPoly		0x74
-#define PICT_frameSamePoly	0x78
-#define PICT_paintSamePoly	0x79
-#define PICT_eraseSamePoly	0x7A
-#define PICT_invertSamePoly	0x7B
-#define PICT_fillSamePoly	0x7C
-#define PICT_frameRgn		0x80
-#define PICT_paintRgn		0x81
-#define PICT_eraseRgn		0x82
-#define PICT_invertRgn		0x83
-#define PICT_fillRgn		0x84
-#define PICT_frameSameRgn	0x88
-#define PICT_paintSameRgn	0x89
-#define PICT_eraseSameRgn	0x8A
-#define PICT_invertSameRgn	0x8B
-#define PICT_fillSameRgn	0x8C
-#define PICT_BitsRect		0x90
-#define PICT_BitsRgn		0x91
-#define PICT_PackBitsRect	0x98
-#define PICT_PackBitsRgn	0x99
-#define PICT_shortComment	0xA0
-#define PICT_longComment	0xA1
-#define PICT_EndOfPicture	0xFF
-#define	PICT_headerOp		0x0C00
-
-static void putFill ARGS(( FILE *fd, int n ));
-static void putShort ARGS(( FILE *fd, int i ));
-static void putLong ARGS(( FILE *fd, long i ));
-static void putFixed ARGS(( FILE *fd, int in, int frac ));
-static void putRect ARGS(( FILE *fd, int x1, int x2, int y1, int y2 ));
-static int putRow ARGS(( FILE *fd, int row, int cols, pixel *rowpixels, char *packed ));
+#define PICT_NOP        0x00
+#define PICT_clipRgn        0x01
+#define PICT_bkPat      0x02
+#define PICT_txFont     0x03
+#define PICT_txFace     0x04
+#define PICT_txMode     0x05
+#define PICT_spExtra        0x06
+#define PICT_pnSize     0x07
+#define PICT_pnMode     0x08
+#define PICT_pnPat      0x09
+#define PICT_thePat     0x0A
+#define PICT_ovSize     0x0B
+#define PICT_origin     0x0C
+#define PICT_txSize     0x0D
+#define PICT_fgColor        0x0E
+#define PICT_bkColor        0x0F
+#define PICT_txRatio        0x10
+#define PICT_picVersion     0x11
+#define PICT_blPixPat       0x12
+#define PICT_pnPixPat       0x13
+#define PICT_fillPixPat     0x14
+#define PICT_pnLocHFrac     0x15
+#define PICT_chExtra        0x16
+#define PICT_rgbFgCol       0x1A
+#define PICT_rgbBkCol       0x1B
+#define PICT_hiliteMode     0x1C
+#define PICT_hiliteColor    0x1D
+#define PICT_defHilite      0x1E
+#define PICT_opColor        0x1F
+#define PICT_line       0x20
+#define PICT_line_from      0x21
+#define PICT_short_line     0x22
+#define PICT_short_line_from    0x23
+#define PICT_long_text      0x28
+#define PICT_DH_text        0x29
+#define PICT_DV_text        0x2A
+#define PICT_DHDV_text      0x2B
+#define PICT_frameRect      0x30
+#define PICT_paintRect      0x31
+#define PICT_eraseRect      0x32
+#define PICT_invertRect     0x33
+#define PICT_fillRect       0x34
+#define PICT_frameSameRect  0x38
+#define PICT_paintSameRect  0x39
+#define PICT_eraseSameRect  0x3A
+#define PICT_invertSameRect 0x3B
+#define PICT_fillSameRect   0x3C
+#define PICT_frameRRect     0x40
+#define PICT_paintRRect     0x41
+#define PICT_eraseRRect     0x42
+#define PICT_invertRRect    0x43
+#define PICT_fillRRect      0x44
+#define PICT_frameSameRRect 0x48
+#define PICT_paintSameRRect 0x49
+#define PICT_eraseSameRRect 0x4A
+#define PICT_invertSameRRect    0x4B
+#define PICT_fillSameRRect  0x4C
+#define PICT_frameOval      0x50
+#define PICT_paintOval      0x51
+#define PICT_eraseOval      0x52
+#define PICT_invertOval     0x53
+#define PICT_fillOval       0x54
+#define PICT_frameSameOval  0x58
+#define PICT_paintSameOval  0x59
+#define PICT_eraseSameOval  0x5A
+#define PICT_invertSameOval 0x5B
+#define PICT_fillSameOval   0x5C
+#define PICT_frameArc       0x60
+#define PICT_paintArc       0x61
+#define PICT_eraseArc       0x62
+#define PICT_invertArc      0x63
+#define PICT_fillArc        0x64
+#define PICT_frameSameArc   0x68
+#define PICT_paintSameArc   0x69
+#define PICT_eraseSameArc   0x6A
+#define PICT_invertSameArc  0x6B
+#define PICT_fillSameArc    0x6C
+#define PICT_framePoly      0x70
+#define PICT_paintPoly      0x71
+#define PICT_erasePoly      0x72
+#define PICT_invertPoly     0x73
+#define PICT_fillPoly       0x74
+#define PICT_frameSamePoly  0x78
+#define PICT_paintSamePoly  0x79
+#define PICT_eraseSamePoly  0x7A
+#define PICT_invertSamePoly 0x7B
+#define PICT_fillSamePoly   0x7C
+#define PICT_frameRgn       0x80
+#define PICT_paintRgn       0x81
+#define PICT_eraseRgn       0x82
+#define PICT_invertRgn      0x83
+#define PICT_fillRgn        0x84
+#define PICT_frameSameRgn   0x88
+#define PICT_paintSameRgn   0x89
+#define PICT_eraseSameRgn   0x8A
+#define PICT_invertSameRgn  0x8B
+#define PICT_fillSameRgn    0x8C
+#define PICT_BitsRect       0x90
+#define PICT_BitsRgn        0x91
+#define PICT_PackBitsRect   0x98
+#define PICT_PackBitsRgn    0x99
+#define PICT_shortComment   0xA0
+#define PICT_longComment    0xA1
+#define PICT_EndOfPicture   0xFF
+#define PICT_headerOp       0x0C00
 
 #define MAXCOLORS 256
 static colorhash_table cht;
 
-int
-main(argc, argv)
-int argc;
-char *argv[];
-{
-	FILE *ifp;
-	int argn, rows, cols, colors, i, row, oc;
-	register pixel **pixels;
-	char *packed;
-	pixval maxval;
-	long lmaxval, rval, gval, bval;
-	colorhist_vector chv;
-
-
-	ppm_init( &argc, argv );
-
-	argn = 1;
-	if (argn < argc)
-	{
-		ifp = pm_openr(argv[1]);
-		argn++;
-	}
-	else
-		ifp = stdin;
-	if (argn != argc)
-		pm_usage("[ppmfile]");
-
-	pixels = ppm_readppm(ifp, &cols, &rows, &maxval);
-	if (cols < 8)
-		pm_error("ppm input too narrow, must be >= 8 pixels wide" );
-	lmaxval = (long)maxval;
-	pm_close(ifp);
-
-	/* Figure out the colormap. */
-	pm_message("computing colormap..." );
-	chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
-	if (chv == (colorhist_vector) 0)
-		pm_error("too many colors - try doing a 'pnmquant %d'", MAXCOLORS);
-	pm_message("%d colors found", colors );
-
-	/* Make a hash table for fast color lookup. */
-	cht = ppm_colorhisttocolorhash(chv, colors);
-
-	/* write the header */
-	putFill(stdout, HEADER_SIZE);
-
-	/* write picSize and picFrame */
-	putShort(stdout, 0);
-	putRect(stdout, 0, 0, rows, cols);
-
-	/* write version op and version */
-	putShort(stdout, PICT_picVersion);
-	putShort(stdout, 0x02FF);
-	putShort(stdout, PICT_headerOp);
-	putLong(stdout, -1L);
-	putFixed(stdout, 0, 0);
-	putFixed(stdout, 0, 0);
-	putFixed(stdout, cols, 0);
-	putFixed(stdout, rows, 0);
-	putFill(stdout, 4);
-
-	/* seems to be needed by many PICT2 programs */
-	putShort(stdout, PICT_clipRgn);
-	putShort(stdout, 10);
-	putRect(stdout, 0, 0, rows, cols);
-
-	/* write picture */
-	putShort(stdout, PICT_PackBitsRect);
-	putShort(stdout, cols | 0x8000);
-	putRect(stdout, 0, 0, rows, cols);
-	putShort(stdout, 0);	/* pmVersion */
-	putShort(stdout, 0);	/* packType */
-	putLong(stdout, 0L);	/* packSize */
-	putFixed(stdout, 72, 0);	/* hRes */
-	putFixed(stdout, 72, 0);	/* vRes */
-	putShort(stdout, 0);	/* pixelType */
-	putShort(stdout, 8);	/* pixelSize */
-	putShort(stdout, 1);	/* cmpCount */
-	putShort(stdout, 8);	/* cmpSize */
-	putLong(stdout, 0L);	/* planeBytes */
-	putLong(stdout, 0L);	/* pmTable */
-	putLong(stdout, 0L);	/* pmReserved */
-	putLong(stdout, 0L);	/* ctSeed */
-	putShort(stdout, 0);	/* ctFlags */
-	putShort(stdout, colors-1);	/* ctSize */
-
-	/* Write out the colormap. */
-	for (i = 0; i < colors; i++)
-	{
-		putShort(stdout, i);
-		rval = PPM_GETR(chv[i].color);
-		gval = PPM_GETG(chv[i].color);
-		bval = PPM_GETB(chv[i].color);
-		if (lmaxval != 65535L)
-		{
-			rval = rval * 65535L / lmaxval;
-			gval = gval * 65535L / lmaxval;
-			bval = bval * 65535L / lmaxval;
-		}
-		putShort(stdout, (short)rval);
-		putShort(stdout, (short)gval);
-		putShort(stdout, (short)bval);
-	}
-
-	putRect(stdout, 0, 0, rows, cols);	/* srcRect */
-	putRect(stdout, 0, 0, rows, cols);	/* dstRect */
-	putShort(stdout, 0);			/* mode */
-
-	/* Finally, write out the data. */
-	packed = (char*) malloc((unsigned)(cols+cols/MAX_COUNT+1));
-	oc = 0;
-	for (row = 0; row < rows; row++)
-		oc += putRow(stdout, row, cols, pixels[row], packed);
-
-	/* if we wrote an odd number of pixdata bytes, pad */
-	if (oc & 1)
-		(void) putc(0, stdout);
-	putShort(stdout, PICT_EndOfPicture);
-
-	lmaxval = ftell(stdout) - HEADER_SIZE;
-	if (fseek(stdout, (long)HEADER_SIZE, 0) >= 0)
-		putShort(stdout, (short)(lmaxval & 0xffff));
-
-	exit(0);
-}
+
 
 static void
-putFill(fd, n)
-FILE *fd;
-int n;
-{
-	register int i;
-
-	for (i = 0; i < n; i++)
-		(void) putc(0, fd);
+putFill(FILE *       const ifP,
+        unsigned int const n) {
+
+    unsigned int i;
+
+    for (i = 0; i < n; ++i)
+        putc(0, ifP);
 }
 
+
+
 static void
-putShort(fd, i)
-FILE *fd;
-int i;
-{
-	(void) putc((i >> 8) & 0xff, fd);
-	(void) putc(i & 0xff, fd);
+putShort(FILE * const ifP,
+         int    const i) {
+    putc((i >> 8) & 0xff, ifP);
+    putc(i & 0xff, ifP);
 }
 
+
+
 static void
-putLong( FILE *fd, long i )
-{
-	(void) putc((int)((i >> 24) & 0xff), fd);
-	(void) putc(((int)(i >> 16) & 0xff), fd);
-	(void) putc(((int)(i >> 8) & 0xff), fd);
-	(void) putc((int)(i & 0xff), fd);
+putLong(FILE * const ifP,
+        long   const i) {
+    putc((int)((i >> 24) & 0xff), ifP);
+    putc(((int)(i >> 16) & 0xff), ifP);
+    putc(((int)(i >> 8) & 0xff), ifP);
+    putc((int)(i & 0xff), ifP);
 }
 
+
+
 static void
-putFixed(fd, in, frac)
-FILE *fd;
-int in, frac;
-{
-	putShort(fd, in);
-	putShort(fd, frac);
+putFixed(FILE * const ifP,
+         int    const in,
+         int    const frac) {
+    putShort(ifP, in);
+    putShort(ifP, frac);
 }
 
+
+
 static void
-putRect(fd, x1, x2, y1, y2)
-FILE *fd;
-int x1, x2, y1, y2;
-{
-	putShort(fd, x1);
-	putShort(fd, x2);
-	putShort(fd, y1);
-	putShort(fd, y2);
+putRect(FILE * const ifP,
+        int    const x1,
+        int    const x2,
+        int    const y1,
+        int    const y2) {
+    putShort(ifP, x1);
+    putShort(ifP, x2);
+    putShort(ifP, y1);
+    putShort(ifP, y2);
 }
 
-#define	RUNLENGTH
-#ifdef	RUNLENGTH
-
-#define		runtochar(c)	(257-(c))
-#define		counttochar(c)	((c)-1)
-
-static int
-putRow(fd, row, cols, rowpixels, packed)
-FILE *fd;
-int row, cols;
-pixel *rowpixels;
-char *packed;
-{
-	register int i;
-	int packcols, count, run, rep, oc;
-	register pixel *pP;
-	pixel lastp;
-	register char *p;
-
-	run = count = 0;
-	for (cols--, i = cols, pP = rowpixels + cols, p = packed, lastp = *pP;
-		i >= 0; i--, lastp = *pP, pP--)
-	{
-		if (PPM_EQUAL(lastp, *pP))
-			run++;
-		else if (run < RUN_THRESH)
-		{
-			while (run > 0)
-			{
-				*p++ = ppm_lookupcolor(cht, &lastp);
-				run--;
-				count++;
-				if (count == MAX_COUNT)
-				{
-					*p++ = counttochar(MAX_COUNT);
-					count -= MAX_COUNT;
-				}
-			}
-			run = 1;
-		}
-		else
-		{
-			if (count > 0)
-				*p++ = counttochar(count);
-			count = 0;
-			while (run > 0)
-			{
-				rep = run > MAX_RUN ? MAX_RUN : run;
-				*p++ = ppm_lookupcolor(cht, &lastp);
-				*p++ = runtochar(rep);
-				run -= rep;
-			}
-			run = 1;
-		}
-	}
-	if (run < RUN_THRESH)
-	{
-		while (run > 0)
-		{
-			*p++ = ppm_lookupcolor(cht, &lastp);
-			run--;
-			count++;
-			if (count == MAX_COUNT)
-			{
-				*p++ = counttochar(MAX_COUNT);
-				count -= MAX_COUNT;
-			}
-		}
-	}
-	else
-	{
-		if (count > 0)
-			*p++ = counttochar(count);
-		count = 0;
-		while (run > 0)
-		{
-			rep = run > MAX_RUN ? MAX_RUN : run;
-			*p++ = ppm_lookupcolor(cht, &lastp);
-			*p++ = runtochar(rep);
-				run -= rep;
-		}
-		run = 1;
-	}
-	if (count > 0)
-		*p++ = counttochar(count);
-
-	packcols = p - packed;		/* how many did we write? */
-	if (cols > 200)
-	{
-		putShort(fd, packcols);
-		oc = packcols + 2;
-	}
-	else
-	{
-		(void) putc(packcols, fd);
-		oc = packcols + 1;
-	}
-
-	/* now write out the packed row */
-	while(p != packed)
-	{
-		--p;
-		(void) putc(*p, fd);
-	}
-
-	return (oc);
+
+
+#define     runtochar(c)    (257-(c))
+#define     counttochar(c)  ((c)-1)
+
+static void
+putRow(FILE *         const ofP,
+       unsigned int   const row,
+       unsigned int   const cols,
+       pixel *        const rowpixels,
+       char *         const outBuf,
+       unsigned int * const outCountP) {
+/*----------------------------------------------------------------------------
+   Write the row rowpixels[], which is 'cols' pixels wide and is row 'row' of
+   the image, to file *ofP in PICT format.
+
+   Return as *outCountP the number of bytes we write to *ofP.
+
+   Use buffer 'outBuf'.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    unsigned int count;
+    unsigned int run;
+    unsigned int rep;
+    unsigned int outCount;
+    pixel lastpix;
+    char * p;
+
+    run = 0;
+    count = 0;
+    lastpix = rowpixels[cols-1];
+
+    for (i = 0, p = &outBuf[0]; i < cols; ++i) {
+
+        pixel const pix = rowpixels[cols - 1 - i];
+
+        if (PPM_EQUAL(lastpix, pix))
+            ++run;
+        else if (run < RUN_THRESH) {
+            while (run > 0) {
+                *p++ = ppm_lookupcolor(cht, &lastpix);
+                --run;
+                ++count;
+                if (count == MAX_COUNT) {
+                    *p++ = counttochar(MAX_COUNT);
+                    count -= MAX_COUNT;
+                }
+            }
+            run = 1;
+        } else {
+            if (count > 0)
+                *p++ = counttochar(count);
+            count = 0;
+            while (run > 0) {
+                rep = MIN(run, MAX_RUN);
+                *p++ = ppm_lookupcolor(cht, &lastpix);
+                *p++ = runtochar(rep);
+                assert(run >= rep);
+                run -= rep;
+            }
+            run = 1;
+        }
+        lastpix = pix;
+    }
+    if (run < RUN_THRESH) {
+        while (run > 0) {
+            *p++ = ppm_lookupcolor(cht, &lastpix);
+            --run;
+            ++count;
+            if (count == MAX_COUNT) {
+                *p++ = counttochar(MAX_COUNT);
+                count -= MAX_COUNT;
+            }
+        }
+    } else {
+        if (count > 0)
+            *p++ = counttochar(count);
+        count = 0;
+        while (run > 0) {
+            rep = MIN(run, MAX_RUN);
+            *p++ = ppm_lookupcolor(cht, &lastpix);
+            *p++ = runtochar(rep);
+            assert(run >= rep);
+            run -= rep;
+        }
+        run = 1;
+    }
+    if (count > 0)
+        *p++ = counttochar(count);
+
+    {
+        unsigned int const packcols = p - outBuf;
+            /* How many we wrote */
+        if (cols-1 > 200) {
+            putShort(ofP, packcols);
+            outCount = packcols + 2;
+        } else {
+            putc(packcols, ofP);
+            outCount = packcols + 1;
+        }
+    }
+    /* now write out the packed row */
+    while (p != outBuf) {
+        --p;
+        putc(*p, ofP);
+    }
+    *outCountP = outCount;
 }
 
-#else	/* RUNLENGTH */
+
+
+# if 0
 
 /* real dumb putRow with no compression */
-static int
-putRow(fd, row, cols, rowpixels, packed)
-FILE *fd;
-int row, cols;
-pixel *rowpixels;
-char *packed;
-{
-	register int i, j, bc, oc;
-	register pixel *pP;
-
-#if notdef
-	bzero(aux, cols); /* aux?? */
-#endif /*notdef*/
-	bc = cols + (cols + MAX_COUNT - 1) / MAX_COUNT;
-	if (bc > 200)
-	{
-		putShort(fd, bc);
-		oc = bc + 2;
-	}
-	else
-	{
-		(void) putc(bc, fd);
-		oc = bc + 1;
-	}
-	for (i = 0, pP = rowpixels; i < cols;)
-	{
-		if (cols - i > MAX_COUNT)
-		{
-			(void) putc(MAX_COUNT - 1, fd);
-			for (j = 0; j < MAX_COUNT; j++)
-			{
-				(void) putc(ppm_lookupcolor(cht, pP), fd);
-				pP++;
-			}
-			i += MAX_COUNT;
-		}
-		else
-		{
-			(void) putc(cols - i - 1, fd);
-			for (j = 0; j < cols - i; j++)
-			{
-				(void) putc(ppm_lookupcolor(cht, pP), fd);
-				pP++;
-			}
-			i = cols;
-		}
-	}
-	return (oc);
+static void
+putRow(FILE *         const ifP,
+       unsigned int   const row,
+       unsigned int   const cols,
+       pixel *        const rowpixels,
+       char *         const outBuf,
+       unsigned int * const outCountP) {
+
+    unsigned int const bc = cols + (cols + MAX_COUNT - 1) / MAX_COUNT;
+
+    unsigned int i;
+
+    if (bc > 200) {
+        putShort(ifP, bc);
+        *outCountP = bc + 2;
+    }  else {
+        putc(bc, ifP);
+        *outCountP = bc + 1;
+    }
+    for (col = 0; col < cols;) {
+        if (cols - col > MAX_COUNT) {
+            unsigned int j;
+            putc(MAX_COUNT - 1, ifP);
+            for (j = 0; j < MAX_COUNT; ++j) {
+                putc(ppm_lookupcolor(cht, &rowPixels[col]), ifP);
+                ++col
+            }
+            col += MAX_COUNT;
+        } else {
+            unsigned int j;
+            putc(cols - col - 1, ifP);
+            for (j = 0; j < cols - col; ++j) {
+                putc(ppm_lookupcolor(cht, &rowPixels[col]), ifP);
+                ++pP;
+            }
+            col = cols;
+        }
+    }
+}
+#endif  /* 0 */
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    FILE * ifP;
+    int nColors;
+    unsigned int oc;
+    unsigned int i;
+    int rows, cols;
+    unsigned int row;
+    pixel ** pixels;
+    char * outBuf;
+    pixval maxval;
+    long lmaxval, rval, gval, bval;
+    colorhist_vector chv;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 > 0)
+        ifP = pm_openr(argv[1]);
+    else
+        ifP = stdin;
+    if (argc-1 > 1)
+        pm_error("Too many arguments.  The only argument is the "
+                 "input file name");
+
+    pixels = ppm_readppm(ifP, &cols, &rows, &maxval);
+    if (cols < 8)
+        pm_error("ppm input too narrow, must be >= 8 pixels wide" );
+    lmaxval = (long)maxval;
+    pm_close(ifP);
+
+    /* Figure out the colormap. */
+    pm_message("computing colormap..." );
+    chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &nColors);
+    if (chv == NULL)
+        pm_error("too many colors - try doing a 'pnmquant %u'", MAXCOLORS);
+    pm_message("%u colors found", nColors);
+
+    /* Make a hash table for fast color lookup. */
+    cht = ppm_colorhisttocolorhash(chv, nColors);
+
+    /* write the header */
+    putFill(stdout, HEADER_SIZE);
+
+    /* write picSize and picFrame */
+    putShort(stdout, 0);
+    putRect(stdout, 0, 0, rows, cols);
+
+    /* write version op and version */
+    putShort(stdout, PICT_picVersion);
+    putShort(stdout, 0x02FF);
+    putShort(stdout, PICT_headerOp);
+    putLong(stdout, -1L);
+    putFixed(stdout, 0, 0);
+    putFixed(stdout, 0, 0);
+    putFixed(stdout, cols, 0);
+    putFixed(stdout, rows, 0);
+    putFill(stdout, 4);
+
+    /* seems to be needed by many PICT2 programs */
+    putShort(stdout, PICT_clipRgn);
+    putShort(stdout, 10);
+    putRect(stdout, 0, 0, rows, cols);
+
+    /* write picture */
+    putShort(stdout, PICT_PackBitsRect);
+    putShort(stdout, cols | 0x8000);
+    putRect(stdout, 0, 0, rows, cols);
+    putShort(stdout, 0);    /* pmVersion */
+    putShort(stdout, 0);    /* packType */
+    putLong(stdout, 0L);    /* packSize */
+    putFixed(stdout, 72, 0);    /* hRes */
+    putFixed(stdout, 72, 0);    /* vRes */
+    putShort(stdout, 0);    /* pixelType */
+    putShort(stdout, 8);    /* pixelSize */
+    putShort(stdout, 1);    /* cmpCount */
+    putShort(stdout, 8);    /* cmpSize */
+    putLong(stdout, 0L);    /* planeBytes */
+    putLong(stdout, 0L);    /* pmTable */
+    putLong(stdout, 0L);    /* pmReserved */
+    putLong(stdout, 0L);    /* ctSeed */
+    putShort(stdout, 0);    /* ctFlags */
+    putShort(stdout, nColors-1); /* ctSize */
+
+    /* Write out the colormap. */
+    for (i = 0; i < nColors; ++i) {
+        putShort(stdout, i);
+        rval = PPM_GETR(chv[i].color);
+        gval = PPM_GETG(chv[i].color);
+        bval = PPM_GETB(chv[i].color);
+        if (lmaxval != 65535L) {
+            rval = rval * 65535L / lmaxval;
+            gval = gval * 65535L / lmaxval;
+            bval = bval * 65535L / lmaxval;
+        }
+        putShort(stdout, (short)rval);
+        putShort(stdout, (short)gval);
+        putShort(stdout, (short)bval);
+    }
+
+    putRect(stdout, 0, 0, rows, cols);  /* srcRect */
+    putRect(stdout, 0, 0, rows, cols);  /* dstRect */
+    putShort(stdout, 0);            /* mode */
+
+    /* Finally, write out the data. */
+    outBuf = malloc((unsigned)(cols+cols/MAX_COUNT+1));
+    for (row = 0, oc = 0; row < rows; ++row) {
+        unsigned int rowSize;
+        putRow(stdout, row, cols, pixels[row], outBuf, &rowSize);
+        oc += rowSize;
+    }
+    /* if we wrote an odd number of pixdata bytes, pad */
+    if (oc & 0x1)
+        putc(0, stdout);
+    putShort(stdout, PICT_EndOfPicture);
+
+    lmaxval = ftell(stdout) - HEADER_SIZE;
+    if (fseek(stdout, (long)HEADER_SIZE, 0) >= 0)
+        putShort(stdout, (short)(lmaxval & 0xffff));
+
+    return 0;
 }
-#endif	/* RUNLENGTH */
+
+
+
diff --git a/converter/ppm/ppmtopjxl.c b/converter/ppm/ppmtopjxl.c
index 91cd1a45..90bcef0f 100644
--- a/converter/ppm/ppmtopjxl.c
+++ b/converter/ppm/ppmtopjxl.c
@@ -12,6 +12,7 @@
  *
  */
 
+#include <assert.h>
 #include <stdio.h>
 #include <math.h>
 #include <string.h>
@@ -19,6 +20,7 @@
 #include "pm_c_util.h"
 #include "nstring.h"
 #include "ppm.h"
+#include "runlength.h"
 
 #define MAXCOLORS 1024
 
@@ -33,7 +35,7 @@ const char * const usage="[-nopack] [-gamma <n>] [-presentation] [-dark]\n\
 #define PCL_MAXHEIGHT 32767
 #define PCL_MAXVAL 255
 
-static int nopack = 0;
+static bool nopack = false;
 static int dark = 0;
 static int diffuse = 0;
 static int dither = 0;
@@ -73,16 +75,28 @@ static const struct options {
    {"-nopack",       BOOL, &nopack },
 };
 
-#define putword(w) (putchar(((w)>>8) & 255), putchar((w) & 255))
 
-static int 
-bitsperpixel(int v) {
-   int bpp = 0;
-   while (v > 0) {  /* calculate # bits for value */
-      ++bpp;
-      v>>=1;
-   }
-   return (bpp);
+
+static void
+putword(unsigned short const w) {
+    putchar((w >> 8) & 0xff);
+    putchar((w >> 0) & 0xff);
+}
+
+
+
+static unsigned int
+bitsperpixel(unsigned int v) {
+
+    unsigned int bpp;
+
+    /* calculate # bits for value */
+    
+    for (bpp = 0; v > 0; ) {
+        ++bpp;
+        v >>= 1;
+    }
+    return bpp;
 }
 
 
@@ -94,101 +108,77 @@ static char *outrow = NULL;
 static signed char *runcnt = NULL;
 
 static void 
-putbits(b, n) {
-    /* put #n bits in b out, packing into bytes; n=0 flushes bits */
-    /* n should never be > 8 */
-
-   static int out = 0;
-   static int cnt = 0;
-   static int num = 0;
-   static int pack = 0;
-   if (n) {
-      int xo = 0;
-      int xc = 0;
-      if (cnt+n > 8) {  /* overflowing current byte? */
-     xc = cnt + n - 8;
-     xo = (b & ~(-1 << xc)) << (8-xc);
-     n -= xc;
-     b >>= xc;
-      }
-      cnt += n;
-      out |= (b & ~(-1 << n)) << (8-cnt);
-      if (cnt >= 8) {
-     inrow[num++] = out;
-     out = xo;
-     cnt = xc;
-      }
-   } else { /* flush row */
-      int i;
-      if (cnt) {
-     inrow[num++] = out;
-     out = cnt = 0;
-      }
-      for (; num > 0 && inrow[num-1] == 0; num--); /* remove trailing zeros */
-      printf("\033*b"); 
-      if (num && !nopack) {            /* TIFF 4.0 packbits encoding */
-     int start = 0;
-     int next;
-     runcnt[start] = 0;
-     for (i = 1; i < num; i++) {
-        if (inrow[i] == inrow[i-1]) {
-           if (runcnt[start] <= 0 && runcnt[start] > -127)
-          runcnt[start]--;
-           else
-          runcnt[start = i] = 0;
-        } else {
-           if (runcnt[start] >= 0 && runcnt[start] < 127)
-          runcnt[start]++;
-           else
-          runcnt[start = i] = 0;
+putbits(int const bArg,
+        int const nArg) {
+/*----------------------------------------------------------------------------
+  Put 'n' bits in 'b' out, packing into bytes; n=0 flushes bits.
+
+  n should never be > 8 
+-----------------------------------------------------------------------------*/
+    static int out = 0;
+    static int cnt = 0;
+    static int num = 0;
+    static bool pack = false;
+
+    int b;
+    int n;
+
+    b = bArg;
+    n = nArg;
+
+    if (n) {
+        int xo = 0;
+        int xc = 0;
+
+        assert(n <= 8);
+
+        if (cnt + n > 8) {  /* overflowing current byte? */
+            xc = cnt + n - 8;
+            xo = (b & ~(-1 << xc)) << (8-xc);
+            n -= xc;
+            b >>= xc;
         }
-     }
-     start = 0;
-     for (i = 0; i < num; i = next) {
-        int count = runcnt[i];
-        int from = i;
-        if (count >= 0) { /* merge two-byte runs */
-           for (;;) {
-          next = i+1+runcnt[i];
-          if(next >= num || runcnt[next] < 0 ||
-             count+runcnt[next]+1 > 127)
-             break;
-          count += runcnt[next]+1;
-          i = next;
-           }
+        cnt += n;
+        out |= (b & ~(-1 << n)) << (8-cnt);
+        if (cnt >= 8) {
+            inrow[num++] = out;
+            out = xo;
+            cnt = xc;
         }
-        next =  i + 1 + ((runcnt[i] < 0) ? -runcnt[i] : runcnt[i]);
-        if (next < num && count > 0 &&
-        runcnt[next] < 0 && runcnt[next] > -127) {
-           count--;
-           next--;
-           runcnt[next] = runcnt[next+1]-1;
+    } else { /* flush row */
+        if (cnt) {
+            inrow[num++] = out;
+            out = cnt = 0;
         }
-        outrow[start++] = count;
-        if (count >= 0) {
-           while (count-- >= 0)
-          outrow[start++] = inrow[from++];
-        } else
-           outrow[start++] = inrow[from];
-     }
-     if (start < num) {
-        num = start;
-        if (!pack) {
-           printf("2m");
-           pack = 1;
+        for (; num > 0 && inrow[num-1] == 0; --num);
+            /* remove trailing zeros */
+        printf("\033*b"); 
+        if (num && !nopack) {            /* TIFF 4.0 packbits encoding */
+            size_t outSize;
+            pm_rlenc_compressbyte(
+                (unsigned char *)inrow, (unsigned char *)outrow,
+                PM_RLE_PACKBITS, num, &outSize); 
+            if (outSize < num) {
+                num = outSize;
+                if (!pack) {
+                    printf("2m");
+                    pack = true;
+                }
+            } else {
+                if (pack) {
+                    printf("0m");
+                    pack = false;
+                }
+            }
         }
-     } else {
-        if (pack) {
-           printf("0m");
-           pack = 0;
+        printf("%dW", num);
+        {
+            unsigned int i;
+            for (i = 0; i < num; ++i)
+                putchar(pack ? outrow[i] : inrow[i]);
         }
-     }
-      }
-      printf("%dW", num);
-      for (i = 0; i < num; i++)
-     putchar(pack ? outrow[i] : inrow[i]);
-      num = 0; /* new row */
-   }
+        num = 0; /* new row */
+    }
 }
 
 
diff --git a/converter/ppm/ppmtorgb3.c b/converter/ppm/ppmtorgb3.c
index b1ac8087..3d2f1c21 100644
--- a/converter/ppm/ppmtorgb3.c
+++ b/converter/ppm/ppmtorgb3.c
@@ -51,11 +51,11 @@ openComponentOut(const char * const suffix,
 
     const char * fileName;
 
-    asprintfN(&fileName, "%s.%s", baseName, suffix);
+    pm_asprintf(&fileName, "%s.%s", baseName, suffix);
 
     *fpP = pm_openw(fileName);
 
-    strfree(fileName);
+    pm_strfree(fileName);
 }
 
 
@@ -90,7 +90,7 @@ main(int argc, const char ** argv) {
     ifP = pm_openr(cmdline.inputFileName);
 
     if (streq(cmdline.inputFileName, "-"))
-        asprintfN(&baseFileName, "noname");
+        baseFileName = strdup("noname");
     else
         baseFileName = strippedOfExtension(cmdline.inputFileName);
 
@@ -131,7 +131,7 @@ main(int argc, const char ** argv) {
 
     pgm_freerow(grayrow);
     ppm_freerow(pixelrow);
-    strfree(baseFileName);
+    pm_strfree(baseFileName);
     pm_close(ifP);
     pm_close(blufP);
     pm_close(grnfP);
diff --git a/converter/ppm/ppmtospu.c b/converter/ppm/ppmtospu.c
new file mode 100644
index 00000000..b558c1fe
--- /dev/null
+++ b/converter/ppm/ppmtospu.c
@@ -0,0 +1,593 @@
+/*
+ *  ppmtpspu.c - Read a raw PPM file on stdin and write an uncompressed
+ *  Spectrum file on stdout.
+ *
+ *  Copyright (C) 1990, Steve Belczyk
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
+
+#define SPU_WIDTH 320
+#define SPU_HEIGHT 200
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;  /* Name of input file */
+    unsigned int dithflag;
+        /* dithering flag */
+};
+
+
+static void
+parseCommandLine(int argc, const 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 d0Spec, d2Spec, d4Spec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "d0",       OPT_FLAG,   
+            NULL,                       &d0Spec, 0);
+    OPTENT3(0,   "d2",       OPT_FLAG,   
+            NULL,                       &d2Spec, 0);
+    OPTENT3(0,   "d4",       OPT_FLAG,   
+            NULL,                       &d4Spec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+
+    if (d4Spec)
+        cmdlineP->dithflag = 4;
+    else if (d2Spec)
+        cmdlineP->dithflag = 2;
+    else if (d0Spec)
+        cmdlineP->dithflag = 0;
+    else
+        cmdlineP->dithflag = 2;
+
+    if (argc-1 < 1) 
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("Program takes zero or one argument (filename).  You "
+                     "specified %u", argc-1);
+    }
+}
+
+
+
+/* This is the stuff to remember about each pixel */
+struct PixelType {
+    unsigned int index4;      /* 4-bit color, used in bitmap */
+    unsigned int x;           /* Pixel's original x-position */
+    unsigned int popularity;  /* Popularity of this pixel's color */
+    unsigned int color9;      /* 9-bit color this pixel actually got */
+};
+
+
+typedef struct {
+    int index[SPU_WIDTH][16];   /* Indices into the 48 color entries */
+} Index48;
+
+typedef struct {
+/* These are the palettes, 3 16-color palettes per each of 200 scan lines */
+    int pal[SPU_HEIGHT][48];  /* -1 means free */
+} Pal;
+
+
+
+static void
+initializePalette(Pal * const palP) {
+/*----------------------------------------------------------------------------
+   Set palettes to zero
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    for (row = 0; row < SPU_HEIGHT; ++row) {
+        unsigned int j;
+        for (j = 0; j < 48; ++j)
+            palP->pal[row][j] = 0;
+    }
+}
+
+
+
+static int
+findIndex(unsigned int const col,
+          unsigned int const index) {
+/*----------------------------------------------------------------------------
+   Given an x-coordinate and a color index, return the corresponding
+   Spectrum palette index.
+-----------------------------------------------------------------------------*/
+    int r, x1;
+    
+    x1 = 10 * index;  /* initial value */
+    if (index & 0x1)
+        x1 -= 5;
+    else
+        ++x1;
+
+    r = index;  /* initial value */
+
+    if ((col >= x1) && (col < (x1+160)))
+        r += 16;
+
+    if (col >= (x1+160))
+        r += 32;
+    
+    return r;
+}
+
+
+
+static void
+setup48(Index48 * const index48P) {
+/*----------------------------------------------------------------------------
+  For each pixel position, set up the indices into the 48-color
+  palette
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+
+    for (col = 0; col < SPU_WIDTH; ++col) {
+        unsigned int i;
+        for (i = 0; i < 16; ++i)
+            index48P->index[col][i] = findIndex(col, i);
+    }
+}
+
+
+
+static void
+dither(unsigned int       const row,
+       tuple *            const tuplerow,
+       unsigned int       const dithflag,
+       struct PixelType * const pixelType) {
+
+    static int const dith4[4][4] = {
+        { 0,  8,  2, 10 },
+        { 12, 4, 14,  6 },
+        { 3, 11,  1,  9 },
+        { 15, 7, 13,  5 }
+    };
+
+    static int const dith2[2][2] = {
+        { 0, 2 },
+        { 3, 1 }
+    };
+    
+    unsigned int c[3];  /* An element for each plane */
+    unsigned int col;
+
+    for (col = 0; col < SPU_WIDTH; ++col) {
+        unsigned int plane;
+
+        for (plane = 0; plane < 3; ++plane) {
+            unsigned int t;
+
+            c[plane] = ((tuplerow[col][plane] & 0xe0) >> 5) & 0x7;
+                /* initial value */
+
+            switch (dithflag) {
+            case 0:
+                break;
+
+            case 2:
+                t = (tuplerow[col][plane] & 0x18 ) >> 3;
+                if (t > dith2[col%2][row%2])
+                    ++c[plane];
+                break;
+
+            case 4:
+                t = (tuplerow[col][plane] & 0x1e) >> 1;
+                if (t > dith4[col%4][row%4])
+                    ++c[plane];
+                break;
+            }
+            c[plane] = MIN(7, c[plane]);
+        }
+        pixelType[col].color9 = (c[0] << 6) | (c[1] << 3) | c[2];
+        pixelType[col].x = col;
+    }
+}
+
+
+
+static void
+swapPixelType(struct PixelType *  const pixelType,
+              unsigned int        const i,
+              unsigned int        const j) {
+
+    struct PixelType const w = pixelType[i];
+
+    pixelType[i] = pixelType[j];
+    pixelType[j] = w;
+}
+
+
+
+static void
+sort(struct PixelType * const pixelType,
+     unsigned int       const left,
+     unsigned int       const right) {
+/*----------------------------------------------------------------------------
+  Sort pixelType[] from element 'left' to (not including) element 'right' in
+  increasing popularity.
+
+  Good ol' Quicksort.
+-----------------------------------------------------------------------------*/
+    unsigned int const pivot = pixelType[(left+right-1)/2].popularity;
+
+    unsigned int i, j;
+
+    /* Rearrange so that everything less than 'pivot' is on the left side of
+       the subject array slice and everything greater than is on the right
+       side and elements equal could be on either side (we won't know until
+       we're done where the dividing line between the sides is), then sort
+       those two sides.
+    */
+
+    assert(left < right);
+
+    for (i = left, j = right; i < j; ) {
+        while (pixelType[i].popularity < pivot)
+            ++i;
+        while (pixelType[j-1].popularity > pivot)
+            --j;
+        
+        if (i < j) {
+            /* An element not less popular than pivot is to the left of a
+               pixel not more popular than pivot, so swap them.  Note that we
+               could be swapping equal (pivot-valued) elements.  Though the
+               swap isn't necessary, moving 'i' and 'j' is.
+            */
+            swapPixelType(pixelType, i, j-1);
+            ++i;
+            --j;
+        }
+    }
+    
+    if (j - left > 1)
+        sort(pixelType, left, j);
+    if (right - i > 1)
+        sort(pixelType, i, right);
+}
+
+
+
+static void
+computePalette(struct PixelType * const pixelType) {
+
+    unsigned int hist[512];      /* Count for each color */
+    unsigned int col;
+    unsigned int i;
+
+    /* Uses popularity algorithm */
+
+    /* Count the occurences of each color */
+
+    for (i = 0; i < 512; ++i)
+        hist[i] = 0;
+
+    for (col = 0; col < SPU_WIDTH; ++col)
+        ++hist[pixelType[col].color9];
+
+    /* Set the popularity of each pixel's color */
+    for (col = 0; col < SPU_WIDTH; ++col)
+        pixelType[col].popularity = hist[pixelType[col].color9];
+
+    /* Sort to find the most popular colors */
+    sort(pixelType, 0, SPU_WIDTH);
+}
+
+
+
+static int
+dist9(unsigned int const x,
+      unsigned int const y) {
+/*----------------------------------------------------------------------------
+    Return the distance between two 9-bit colors.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    unsigned int d;
+    int x0[3], y0[3];
+
+    x0[0] = (x & 0x007);
+    x0[1] = (x & 0x038) >> 3;
+    x0[2] = (x & 0x1c0) >> 6;
+
+    y0[0] = (y & 0x007);
+    y0[1] = (y & 0x038) >> 3;
+    y0[2] = (y & 0x1c0) >> 6;
+
+    for (i = 0, d = 0; i < 3; ++i) {
+        unsigned int const t = x0[i] - y0[i];
+        d += t * t;
+    }
+
+    return d;
+}
+
+
+
+static void
+convertPixel(unsigned int       const col,
+             unsigned int       const row,
+             struct PixelType * const pixelType,
+             Pal *              const palP,
+             const Index48 *    const index48P) {
+
+    int ifree;
+
+    unsigned int const x = pixelType[col].x;
+    unsigned int const c = pixelType[col].color9;
+
+    ifree = -1;       /* Set if free slot found */
+
+    /* Handle each possible case, from easiest to hardest, in the hopes the
+       easy ones are more frequent.
+    */
+
+    /* If it wants black, it gets it */
+    if (c == 0)
+        pixelType[col].index4 = 0;
+    else {
+        /* If another pixel is using this color, it gets it */
+        unsigned int i;
+        for (i = 1; i < 15; ++i) {
+            /* Check for free slots while we're here */
+            if ((ifree < 0) &&
+                (palP->pal[row][index48P->index[x][i]] == -1))
+                ifree = i;
+            else if (c == palP->pal[row][index48P->index[x][i]]) {
+                pixelType[col].index4 = i;
+                return;
+            }
+        }
+
+        /* If there are no free slots, we must use the closest entry in use so
+           far
+        */
+        if (ifree < 0) {
+            unsigned int i;
+            unsigned int d;
+            unsigned int b;
+
+            for (i = 1, d = 1000; i < 15; ++i) {
+                unsigned int const t =
+                    dist9(c, palP->pal[row][index48P->index[x][i]]);
+                if (t < d) {
+                    d = t;
+                    b = i;
+                }
+            }
+
+            /* See if it would be better off with black */
+            if (d > dist9(c, 0))
+                b = 0;
+
+            pixelType[col].index4 = b;
+        } else {
+            /* Use up a slot and give it what it wants */
+            palP->pal[row][index48P->index[x][ifree]] = c;
+            pixelType[col].index4 = ifree;
+        }
+    }
+}
+
+
+
+static void
+setPixel(unsigned int const col,
+         unsigned int const row,
+         unsigned int const c,
+         short *      const screen) {
+
+    unsigned int index, bit, plane;
+
+    /* In the next few statements, the bit operations are a little
+       quicker, but the arithmetic versions are easier to read and
+       maybe more portable.  Please try swapping them if you have
+       trouble on your machine.
+    */
+
+    /*  index = (80 * row) + 4 * (col / 16);    */
+    index = (row << 6) + (row << 4) + ((col >> 4) << 2);
+
+    /*  bit = 0x8000 >> (col % 16);   */
+    bit = 0x8000 >> (col & 0x0f);
+
+    for (plane=0; plane<4; ++plane) {
+        if (c & (1 << plane))
+            screen[index + plane] |= bit;
+    }
+}
+
+
+
+static void
+convertRow(unsigned int       const row,
+           struct PixelType * const pixelType,
+           Pal *              const palP,
+           const Index48 *    const index48P,
+           short *            const screen) {
+
+    unsigned int i;
+
+    /* Mark palette entries as all free */
+    for (i = 0; i < 48; ++i)
+        palP->pal[row][i] = -1;
+    
+    /* Mark reserved palette entries */
+    palP->pal[row][0]  = palP->pal[row][15] = palP->pal[row][16] = 0;
+    palP->pal[row][31] = palP->pal[row][32] = palP->pal[row][47] = 0;
+
+    /* Convert each pixel */
+
+    {
+        /* Process the pixels in order of the popularity of the desired
+           color
+        */
+        int col;
+        for (col = SPU_WIDTH-1; col >= 0; --col) {
+            convertPixel(col, row, pixelType, palP, index48P);
+            setPixel(pixelType[col].x, row, pixelType[col].index4, screen);
+        }
+    }
+}
+
+
+
+static void
+doRow(unsigned int    const row,
+      tuple *         const tuplerow,
+      unsigned int    const dithflag,
+      const Index48 * const index48P,
+      Pal *           const palP,
+      short *         const screen) {
+
+    struct PixelType pixelType[SPU_WIDTH];
+
+    /* Dither and reduce to 9 bits */
+    dither(row, tuplerow, dithflag, pixelType);
+
+    /* Compute the best colors for this row */
+    computePalette(pixelType);
+
+    /* Convert this row */
+    convertRow(row, pixelType, palP, index48P, screen);
+}
+
+
+
+static void
+writeScreen(const short * const screen) {
+
+    /* Write the bitmap */
+
+    unsigned int i;
+    
+    for (i = 0; i < 16000; ++i) {
+        char const c0 = 0xff & (screen[i] >> 8);
+        char const c1 = 0xff & screen[i];
+        putchar(c0);
+        putchar(c1);
+    }
+}
+
+
+
+static void
+writePalettes(const Pal * const palP) {
+
+    unsigned int row;
+
+    for (row = 1; row < SPU_HEIGHT; ++row) {
+        unsigned int i;
+        for (i = 0; i < 48; ++i) {
+            int const p = palP->pal[row][i];
+            unsigned int const q =
+                ((p & 0x1c0) << 2) +
+                ((p & 0x038) << 1) +
+                ((p & 0x007) << 0);
+
+            putchar((q >> 8) & 0xff);
+            putchar((q >> 0) & 0xff);
+        }
+    }
+}
+
+
+
+static void
+writeSpu(const short * const screen,
+         const Pal *   const palP ) {
+
+    writeScreen(screen);
+
+    writePalettes(palP);
+}
+
+
+
+int
+main (int argc, const char ** argv) {
+
+    struct pam pam;
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    tuple ** tuples;
+    Pal pal;
+    Index48 index48;
+    short screen[16000];  /* This is the ST's video RAM */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    tuples = pnm_readpam(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
+
+    if (pam.depth < 3)
+        pm_error("Image must be RGB, so at least 3 deep.  This image is "
+                 "only %u deep", pam.depth);
+
+    if ((pam.width != SPU_WIDTH) || (pam.height != SPU_HEIGHT))
+        pm_error("Image size must be %ux%u.  This one is %u x %u",
+                 SPU_WIDTH, SPU_HEIGHT, pam.width, pam.height);
+
+    {
+        unsigned int i;
+        for (i = 0; i < 16000; screen[i++] = 0);
+    }
+    setup48(&index48);
+
+    initializePalette(&pal);
+
+    {
+        /* Set first row of screen data to black */
+        unsigned int i;
+        for (i = 0; i < 80; ++i)
+            screen[i] = 0;
+    }
+    {
+        unsigned int row;
+        for (row = 0; row < SPU_HEIGHT; ++row)
+            doRow(row, tuples[row], cmdline.dithflag, &index48, &pal, screen);
+    }
+    writeSpu(screen, &pal);
+
+    return 0;
+}
+
+
+
diff --git a/converter/ppm/ppmtoterm.c b/converter/ppm/ppmtoterm.c
index 81df614e..d388f77d 100644
--- a/converter/ppm/ppmtoterm.c
+++ b/converter/ppm/ppmtoterm.c
@@ -19,32 +19,37 @@
 **
 */
 
+#include <assert.h>
 #include <string.h>
 
 #include "pm_c_util.h"
-#include "ppm.h"
+#include "mallocvar.h"
 #include "shhopt.h"
+#include "ppm.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;  /* Filespec of input file */
+    const char * inputFileName;  /* Name of input file */
     unsigned int verbose;
 };
 
 
 
 static void
-parseCommandLine(int argc, char **argv,
-                 struct cmdlineInfo *cmdlineP) {
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+
+    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 */
     OPTENT3(0, "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0);
 
@@ -52,123 +57,176 @@ parseCommandLine(int argc, char **argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    switch (argc-1) {
-    case 0:
-        cmdlineP->inputFilespec = "-";
-        break;
-    case 1:
-        cmdlineP->inputFilespec = argv[1];
-        break;
-    case 2:
-        break;
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("Too many arguments: %u.  The only possible argument "
+                     "is the input file name", argc-1);
     }
 }
 
 
-#define ESC         "\x1B\x5B"
-#define NUM_COLORS      128
-#define MAX_ANSI_STR_LEN    16
 
+#define ESC "\x1B\x5B"
+#define ANSI_BRIGHT_CMD_PAT ESC "%dm"
+#define ANSI_FGCOLOR_CMD_PAT ESC "3%dm"
+#define ANSI_BGCOLOR_CMD_PAT ESC "4%dm"
+#define MAX_ANSI_STR_LEN 16
+#define NUM_COLORS 128
+    /* 1 bit each RGB = 8 colors.
+       8 BG colors * 8 FG colors * 2 brightnesses
+    */
 
-static int __inline__ sqr(const int x) {
-    return x*x;
-}
 
-/*
-    Generates some sort of color palette mixing the available
-    colors as different values of background, foreground & brightness.
-*/
-static int 
-generate_palette(unsigned char rgb[NUM_COLORS][3], 
-                 char ansi_code[NUM_COLORS][MAX_ANSI_STR_LEN]) {
-    int code, col=0, cd2=0;
-    
-    memset((void *)rgb, 0, NUM_COLORS*3);
-    memset((void *)ansi_code, 0, NUM_COLORS*MAX_ANSI_STR_LEN);
-
-    for(col=cd2=0; cd2<8; cd2++) {
-        unsigned int b;
-        for(b=0;b<2;b++) {
-            for(code=0; code<8; code++) {
-                unsigned int c;
-                for(c=0;c<3;c++) {
-                    if(code&(1<<c)) {
-                        rgb[col][c]=(192|(b?63:0));
-                    }
-                    if(cd2&(1<<c)) {
-                        rgb[col][c]|=(128);
+
+static void
+generatePalette(unsigned char        rgb[NUM_COLORS][3], 
+                char                 ansiCode[NUM_COLORS][MAX_ANSI_STR_LEN],
+                unsigned int * const paletteSizeP) {
+/*----------------------------------------------------------------------------
+  Generate some sort of color palette mixing the available colors as different
+  values of background, foreground & brightness.
+
+  We return as rgb[I] the RGB triple for the color with palette index I.
+  Component intensities are in the range 0..255.  rgb[I][0] is red;
+  rgb[I][1] is green; rgb[I][2] is blue.
+
+  We return as ansiCode[I] the sequence you send to a terminal to generate
+  the color with palette index I.
+-----------------------------------------------------------------------------*/
+    unsigned int idx;
+        /* palette index of the color being considered */
+    unsigned int bgColorCode;
+        /* This is the ANSI color code for the background.  An ANSI color code
+           is a 3 bit code in which LSB means red; middle bit means green, and
+           MSB means blue.
+        */
+
+    /* We develop the palette backwards: consider every permutation of the
+       three terminal controls -- background, foreground, and brightness --
+       and for each figure out what RGB color it represents and fill in
+       that element of RGB[][]
+    */
+
+    for (bgColorCode = 0, idx = 0; bgColorCode < 8; ++bgColorCode) {
+        unsigned int brightness;  /* 0 = dim; 1 = bright */
+        for (brightness = 0; brightness < 2; ++brightness) {
+            unsigned int fgColorCode;
+                /* ANSI color code for the foreground.  See bgColorCode. */
+            for (fgColorCode = 0; fgColorCode < 8; ++fgColorCode) {
+                unsigned int rgbComp;
+                    /* 0 = red; 1 = green; 2 = blue */
+                for (rgbComp = 0; rgbComp < 3; ++rgbComp) {
+                    assert (idx < NUM_COLORS);
+                    rgb[idx][rgbComp] = 0x00;  /* initial value */
+                    if ((fgColorCode & (0x1 << rgbComp)) != 0) {
+                        rgb[idx][rgbComp] |= 0xC0;
+                        if (brightness == 1)
+                            rgb[idx][rgbComp] |= 0x3F;
                     }
+                    if ((bgColorCode & (0x1 << rgbComp)) != 0)
+                        rgb[idx][rgbComp] |= 0x80;
                 }
-                sprintf(ansi_code[col],
-                        ESC"%dm"ESC"3%dm"ESC"4%dm",
-                        b, code, cd2);
-                col++;
+                sprintf(ansiCode[idx],
+                        ANSI_BRIGHT_CMD_PAT
+                        ANSI_FGCOLOR_CMD_PAT
+                        ANSI_BGCOLOR_CMD_PAT,
+                        brightness, fgColorCode, bgColorCode);
+                ++idx;
             }
         }
     }
-    return col;
+    *paletteSizeP = idx;
+}
+
+
+
+static void
+lookupInPalette(pixel          const pixel,
+                pixval         const maxval,
+                unsigned char        rgb[NUM_COLORS][3], 
+                unsigned int   const palLen,
+                unsigned int * const paletteIdxP) {
+/*----------------------------------------------------------------------------
+   Look up the color 'pixel' (which has maxval 'maxval') in the palette
+   palette[], which has 'palLen' elements and uses maxval 255.  Return the
+   index into palette[] of the color that is closes to 'pixel' as
+   *paletteIdxP.
+-----------------------------------------------------------------------------*/
+    pixval const r = PPM_GETR(pixel) * 255 / maxval;
+    pixval const g = PPM_GETG(pixel) * 255 / maxval;
+    pixval const b = PPM_GETB(pixel) * 255 / maxval;
+
+    unsigned int paletteIdxSoFar;
+    unsigned int dist;
+    unsigned int i;
+            
+    /* The following loop calculates the index that corresponds to the
+       minimum color distance between the given RGB values and the
+       values available in the palette.
+    */
+    for (i = 0, dist = SQR(255)*3, paletteIdxSoFar = 0; i < palLen; ++i) {
+        pixval const pr=rgb[i][0];
+        pixval const pg=rgb[i][1];
+        pixval const pb=rgb[i][2];
+        unsigned int const j = SQR(r-pr) + SQR(b-pb) + SQR(g-pg);
+
+        if (j  < dist) {
+            dist = j;
+            paletteIdxSoFar = i;
+        }
+    }
+    *paletteIdxP = paletteIdxSoFar;
 }
 
 
 
-int main(int argc, char **argv)
-{
-    FILE            *ifp;
-    pixel           **pixels;
-    int             rows, row, cols, col,
-                    pal_len, i;
+int
+main(int argc, const char ** argv) {
+
+    FILE *          ifP;
+    pixel **        pixels;
+    int             rows, cols;
+    unsigned int    row;
+    unsigned int    palLen;
     pixval          maxval;
-    struct cmdlineInfo
-                    cmdline;
+    struct cmdlineInfo cmdline;
     unsigned char   rgb[NUM_COLORS][3];
-    char            ansi_code[NUM_COLORS][MAX_ANSI_STR_LEN];
+    char            ansiCode[NUM_COLORS][MAX_ANSI_STR_LEN];
 
-    
-    ppm_init(&argc, argv);    
+    pm_proginit(&argc, argv);    
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifp = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFileName);
     
-    pixels = ppm_readppm(ifp, &cols, &rows, &maxval);
+    pixels = ppm_readppm(ifP, &cols, &rows, &maxval);
 
-    pm_close(ifp);
+    pm_close(ifP);
         
-    pal_len=generate_palette(rgb, ansi_code);
+    generatePalette(rgb, ansiCode, &palLen);
     
     for (row = 0; row < rows; ++row) {
-        for (col = 0; col < cols; col++) {
-            pixval const r=(int)PPM_GETR(pixels[row][col])*255/maxval;
-            pixval const g=(int)PPM_GETG(pixels[row][col])*255/maxval;
-            pixval const b=(int)PPM_GETB(pixels[row][col])*255/maxval;
-            int val, dist;
-            
-            /*
-            The following loop calculates the index that
-            corresponds to the minimum color distance
-            between the given RGB values and the values
-            available in the palette.
-            */
-            for(i=0, dist=sqr(255)*3, val=0; i<pal_len; i++) {
-                pixval const pr=rgb[i][0];
-                pixval const pg=rgb[i][1];
-                pixval const pb=rgb[i][2];
-                unsigned int j;
-                if( (j=sqr(r-pr)+sqr(b-pb)+sqr(g-pg))<dist ) {
-                    dist=j;
-                    val=i;
-                }
-            }
-            printf("%s%c", ansi_code[val],0xB1);
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            unsigned int paletteIdx;
+
+            lookupInPalette(pixels[row][col], maxval, rgb, palLen,
+                            &paletteIdx);
+
+            printf("%s\xB1", ansiCode[paletteIdx]);
         }
-        printf(ESC"\x30m\n");
+        printf(ESC "\x30m\n");
     }
-    printf(ESC"\x30m");
+    printf(ESC "\x30m");
 
     ppm_freearray(pixels, rows);
     
-    exit(0);
+    return 0;
 }
diff --git a/converter/ppm/ppmtowinicon.c b/converter/ppm/ppmtowinicon.c
index a96840a1..c673798f 100644
--- a/converter/ppm/ppmtowinicon.c
+++ b/converter/ppm/ppmtowinicon.c
@@ -58,7 +58,7 @@ parseCommandLine(int                 argc,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -82,7 +82,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);
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!outputSpec)
@@ -524,9 +524,9 @@ makePalette(pixel **          const xorPPMarray,
     xorChv = ppm_computecolorhist(xorPPMarray, xorCols, xorRows, MAXCOLORS, 
                                   &colors);
     if (xorChv == NULL)
-        asprintfN(errorP,
-                  "image has too many colors - try doing a 'pnmquant %d'",
-                  MAXCOLORS);
+        pm_asprintf(errorP,
+                    "image has too many colors - try doing a 'pnmquant %d'",
+                    MAXCOLORS);
     else {
         *errorP = NULL;
 
@@ -584,10 +584,10 @@ getOrFakeAndMap(const char *      const andPgmFname,
         pm_close(andfile);
 
         if ((andCols != xorCols) || (andRows != xorRows)) {
-            asprintfN(errorP,
-                      "And mask and image have different dimensions "
-                     "(%d x %d vs %d x %d).  Aborting.",
-                     andCols, xorCols, andRows, xorRows);
+            pm_asprintf(errorP,
+                        "And mask and image have different dimensions "
+                        "(%d x %d vs %d x %d).  Aborting.",
+                        andCols, xorCols, andRows, xorRows);
         } else
             *errorP = NULL;
     }
@@ -670,7 +670,7 @@ addEntryToIcon(MS_Ico       const MSIconData,
     * All the icons I found seemed to pad the palette to the max entries
     * for that bitdepth.
     * 
-    * The spec indicates this isn't neccessary, but I'll follow this behaviour
+    * The spec indicates this isn't necessary, but I'll follow this behaviour
     * just in case.
     */
     if (colors < 3) {
diff --git a/converter/ppm/ppmtoxpm.c b/converter/ppm/ppmtoxpm.c
index fc8ba9d7..38d99972 100644
--- a/converter/ppm/ppmtoxpm.c
+++ b/converter/ppm/ppmtoxpm.c
@@ -126,7 +126,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 == 0) 
@@ -317,7 +317,7 @@ genCmap(colorhist_vector const chv,
     MALLOCARRAY(cmap, cmapSize);
     if (cmapP == NULL)
         pm_error("Out of memory allocating %u bytes for a color map.",
-                 sizeof(cixel_map) * (ncolors+1));
+                 (unsigned)sizeof(cixel_map) * (ncolors+1));
 
     xpmMaxval = xpmMaxvalFromMaxval(maxval);
 
@@ -363,13 +363,13 @@ genCmap(colorhist_vector const chv,
 
             PPM_DEPTH(scaledColor, color, maxval, xpmMaxval);
 
-            asprintfN(&hexString, xpmMaxval == 0x000F ? "#%X%X%X" :
-                      xpmMaxval == 0x00FF ? "#%02X%02X%02X" :
-                      xpmMaxval == 0x0FFF ? "#%03X%03X%03X" :
-                      "#%04X%04X%04X", 
-                      PPM_GETR(scaledColor),
-                      PPM_GETG(scaledColor),
-                      PPM_GETB(scaledColor)
+            pm_asprintf(&hexString, xpmMaxval == 0x000F ? "#%X%X%X" :
+                        xpmMaxval == 0x00FF ? "#%02X%02X%02X" :
+                        xpmMaxval == 0x0FFF ? "#%03X%03X%03X" :
+                        "#%04X%04X%04X", 
+                        PPM_GETR(scaledColor),
+                        PPM_GETG(scaledColor),
+                        PPM_GETB(scaledColor)
                 );
 
             if (hexString == NULL)
@@ -399,7 +399,7 @@ destroyCmap(cixel_map *  const cmap,
     int i;
     /* Free the real color entries */
     for (i = 0; i < cmapSize; i++) {
-        strfree(cmap[i].rgbname);
+        pm_strfree(cmap[i].rgbname);
         free(cmap[i].cixel);
     }
     free(cmap);
diff --git a/converter/ppm/ppmtoyuv.c b/converter/ppm/ppmtoyuv.c
index 7d843cc0..75f79c1e 100644
--- a/converter/ppm/ppmtoyuv.c
+++ b/converter/ppm/ppmtoyuv.c
@@ -19,79 +19,102 @@
 
 #include "ppm.h"
 
-int
-main(argc, argv)
-char **argv;
-{
-	FILE *ifp;
-	pixel          *pixelrow;
-	register pixel *pP;
-	int             rows, cols, format, row;
-	register int    col;
-	pixval          maxval;
-	unsigned long   y1, y2=0, u=0, v=0, u0=0, u1, u2, v0=0, v1, v2;
-	unsigned char  *yuvbuf;
-
-
-	ppm_init(&argc, argv);
 
-	if (argc > 2) pm_usage("[ppmfile]");
 
-	if (argc == 2) ifp = pm_openr(argv[1]);
-	else ifp = stdin;
-
-	ppm_readppminit(ifp, &cols, &rows, &maxval, &format);
+static void
+convertRow(const pixel *   const pixelrow,
+           unsigned int    const cols,
+           unsigned char * const yuvBuf,
+           unsigned long * const uP,
+           unsigned long * const vP,
+           unsigned long * const u0P,
+           unsigned long * const v0P,
+           unsigned long * const y2CarryP) {
+
+    unsigned int col;
+    unsigned char * yuvptr;
+
+    for (col = 0, yuvptr = &yuvBuf[0]; col < cols; col += 2) {
+        unsigned long y1, y2, u1, u2, v1, v2;
+
+        {
+            /* first pixel gives Y and 0.5 of chroma */
+            pixval const r = PPM_GETR(pixelrow[col]);
+            pixval const g = PPM_GETG(pixelrow[col]);
+            pixval const b = PPM_GETB(pixelrow[col]);
+            
+            y1 = 16829 * r + 33039 * g +  6416 * b + (*y2CarryP & 0xffff);
+            u1 = -4853 * r -  9530 * g + 14383 * b;
+            v1 = 14386 * r - 12046 * g -  2340 * b;
+        }
+        {
+            /* second pixel gives Y and 0.25 of chroma */
+            pixval const r = PPM_GETR(pixelrow[col + 1]);
+            pixval const g = PPM_GETG(pixelrow[col + 1]);
+            pixval const b = PPM_GETB(pixelrow[col + 1]);
+
+            y2 = 16829 * r + 33039 * g + 6416 * b + (y1 & 0xffff);
+            u2 = -2426 * r -  4765 * g + 7191 * b;
+            v2 =  7193 * r -  6023 * g - 1170 * b;
+        }
+        /* filter the chroma */
+        *uP = *u0P + u1 + u2 + (*uP & 0xffff);
+        *vP = *v0P + v1 + v2 + (*vP & 0xffff);
+
+        *u0P = u2;
+        *v0P = v2;
+
+        *yuvptr++ = (*uP >> 16) + 128;
+        *yuvptr++ = (y1  >> 16) +  16;
+        *yuvptr++ = (*vP >> 16) + 128;
+        *yuvptr++ = (y2  >> 16) +  16;
+
+        *y2CarryP = y2;
+    }
+}
 
-    if (cols % 2 != 0)
-        pm_error("Image must have even number of columns.\n"
-                 "This image is %d columns wide.  Try Pnmcut.", cols);
 
-	pixelrow = ((pixel*) pm_allocrow( cols, sizeof(pixel) ));
-	yuvbuf = (unsigned char *) pm_allocrow( cols, 2 );
 
-	for (row = 0; row < rows; ++row) {
-		unsigned char *yuvptr;
+int
+main(int argc, const char **argv) {
 
-		ppm_readppmrow(ifp, pixelrow, cols, maxval, format);
+    FILE * ifP;
+    pixel * pixelrow;
+    int rows, cols, format;
+    pixval maxval;
+    unsigned int row;
+    unsigned char  * yuvBuf;
+    unsigned long u, v, u0, v0, y2Carry;
 
-		for (col = 0, pP = pixelrow, yuvptr=yuvbuf; col < cols; col += 2, ++pP) {
-			pixval r, g, b;
+    pm_proginit(&argc, argv);
 
-			/* first pixel gives Y and 0.5 of chroma */
-			r = PPM_GETR(*pP);
-			g = PPM_GETG(*pP);
-			b = PPM_GETB(*pP);
+    if (argc-1 > 1)
+        pm_error("Too many arguments: %u.  The only possible argument "
+                 "is the name of the input file", argc-1);
 
-			y1 = 16829 * r + 33039 * g + 6416 * b + (0xffff & y2);
-			u1 = -4853 * r - 9530 * g + 14383 * b;
-			v1 = 14386 * r - 12046 * g - 2340 * b;
+    if (argc-1 == 1)
+        ifP = pm_openr(argv[1]);
+    else
+        ifP = stdin;
 
-			pP++;
-			/* second pixel just yields a Y and 0.25 U, 0.25 V */
-			r = PPM_GETR(*pP);
-			g = PPM_GETG(*pP);
-			b = PPM_GETB(*pP);
+    ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
 
-			y2 = 16829 * r + 33039 * g + 6416 * b + (0xffff & y1);
-			u2 = -2426 * r - 4765 * g + 7191 * b;
-			v2 = 7193 * r - 6023 * g - 1170 * b;
+    if (cols % 2 != 0)
+        pm_error("Image must have even number of columns.\n"
+                 "This image is %u columns wide.  Try Pamcut.", cols);
 
-			/* filter the chroma */
-			u = u0 + u1 + u2 + (0xffff & u);
-			v = v0 + v1 + v2 + (0xffff & v);
+    pixelrow = ppm_allocrow(cols);
+    yuvBuf = (unsigned char *) pm_allocrow(cols, 2);
 
-			u0 = u2;
-			v0 = v2;
+    for (row = 0, u = v = u0 = v0 = y2Carry = 0; row < rows; ++row) {
+        ppm_readppmrow(ifP, pixelrow, cols, maxval, format);
 
-			*yuvptr++ = (u >> 16) + 128;
-			*yuvptr++ = (y1 >> 16) + 16;
-			*yuvptr++ = (v >> 16) + 128;
-			*yuvptr++ = (y2 >> 16) + 16;
-		}
-		fwrite(yuvbuf, cols*2, 1, stdout);
-	}
+        convertRow(pixelrow, cols, yuvBuf, &u, &v, &u0, &v0, &y2Carry);
+        
+        fwrite(yuvBuf, cols*2, 1, stdout);
+    }
 
-	pm_close(ifp);
+    pm_close(ifP);
 
-	exit(0);
+    return 0;
 }
diff --git a/converter/ppm/ppmtoyuvsplit.c b/converter/ppm/ppmtoyuvsplit.c
index b5809d0b..eb89ad29 100644
--- a/converter/ppm/ppmtoyuvsplit.c
+++ b/converter/ppm/ppmtoyuvsplit.c
@@ -46,9 +46,9 @@ static void
 makeOutputFileName(const char *         const baseName,
                    struct FileNameSet * const fnameP) {
 
-    asprintfN(&fnameP->u, "%s.U", baseName);
-    asprintfN(&fnameP->v, "%s.V", baseName);
-    asprintfN(&fnameP->y, "%s.Y", baseName);
+    pm_asprintf(&fnameP->u, "%s.U", baseName);
+    pm_asprintf(&fnameP->v, "%s.V", baseName);
+    pm_asprintf(&fnameP->y, "%s.Y", baseName);
 }
 
 
@@ -56,9 +56,9 @@ makeOutputFileName(const char *         const baseName,
 static void
 termFileNameSet(struct FileNameSet const fname) {
 
-    strfree(fname.u);
-    strfree(fname.v);
-    strfree(fname.y);
+    pm_strfree(fname.u);
+    pm_strfree(fname.v);
+    pm_strfree(fname.y);
 }
 
 
diff --git a/converter/ppm/sldtoppm.c b/converter/ppm/sldtoppm.c
index ab9f3447..550eed5b 100644
--- a/converter/ppm/sldtoppm.c
+++ b/converter/ppm/sldtoppm.c
@@ -26,23 +26,20 @@
 #include <string.h>
 #include <math.h>
 
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "ppmdraw.h"
 #include "nstring.h"
-#ifdef DEBUG
 #include <assert.h>
-#else
-#define assert(x)
-#endif
+
+#include "autocad.h"                  /* AutoCAD standard color assignments */
+
 
 /*  Define a variable type accepting numbers -127 <= n <= 127.  But note
     that we still expect it to act UNSIGNED. */
 
 #define smallint unsigned char        /* Small integers */
 
-#define TRUE     1
-#define FALSE    0
-
 #define EOS     '\0'
 
 /* Screen point */
@@ -70,18 +67,17 @@ typedef void (slvecfn)(struct svector * vec, int color);
 typedef void (slfloodfn)(struct spolygon * poly, int color);
 
 
+static unsigned long const pixmaxval = 255;  /* Largest pixel value */
+
 static int ixdots, iydots;        /* Screen size in dots */
-static FILE *slfile;              /* Slide file descriptor */
-static int blither = FALSE;       /* Dump slide file information ? */
-static int info = FALSE;          /* Print header information */
+static FILE * slfile;             /* Slide file descriptor */
+static bool blither;              /* Dump slide file information ? */
+static bool info;                 /* Print header information */
 static pixel **pixels;            /* Pixel map */
-static int pixcols, pixrows;          /* Pixel map size */
-#define pixmaxval 255             /* Largest pixel value */
+static int pixcols, pixrows;      /* Pixel map size */
 static double uscale = -1;        /* Uniform scale factor */
 static int sxsize = -1, sysize = -1;  /* Scale to X, Y size ? */
 
-#include "autocad.h"                  /* AutoCAD standard color assignments */
-
 /*  Local variables  */
 
 struct slhead {
@@ -94,11 +90,11 @@ struct slhead {
     char spad;                /* Pad to even byte length */
 };
 
-static int adjust = FALSE;        /* Adjust to correct aspect ratio ? */
-static struct slhead slfrof;          /* Slide file header */
-static long xfac, yfac;           /* Aspect ratio scale factors */
+static bool adjust;           /* Adjust to correct aspect ratio ? */
+static struct slhead slfrof;  /* Slide file header */
+static long xfac, yfac;       /* Aspect ratio scale factors */
 
-static int sdrawkcab;
+static bool sdrawkcab;
     /* Slide drawing kinematic conversion of ass-backwards data flag */
 
 
@@ -108,7 +104,7 @@ static int sdrawkcab;
 */
 
 static int
-extend(smallint ch) {
+extend(unsigned char const ch) {
     return ((int) ((ch & 0x80) ? (ch | ~0xFF) : ch));
 }
 
@@ -136,9 +132,9 @@ sli(void) {
 
 static int 
 slib(void) {
-    smallint ch = 0;
+    unsigned char ch;
 
-    if (fread(&ch, sizeof ch, 1, slfile) != 1) {
+    if (fread(&ch, sizeof(ch), 1, slfile) != 1) {
         pm_error("error reading slide file");
     }
     return extend(ch);
@@ -171,22 +167,28 @@ slidefind(const char * const sname,
     char uname[32];
     unsigned char libent[36];
     long pos;
+    bool found;
+    bool eof;
 
     if (dironly)
         pm_message("Slides in library:");
     else {
-        int i;
+        unsigned int i;
         const char * ip;
         
         ip = sname; /* initial value */
         
-        for (i = 0; i < 31; i++) {
-            char ch = *ip++;
+        for (i = 0; i < 31; ++i) {
+            char const ch = *ip++;
             if (ch == EOS)
                 break;
-            if (ucasen && ISLOWER(ch))
-                ch = TOUPPER(ch);
-            uname[i] = ch;
+
+            {
+                char const upperCh =
+                    ucasen && islower(ch) ? toupper(ch) : ch;
+                
+                uname[i] = upperCh;
+            }
         }
         uname[i] = EOS;
     }
@@ -199,31 +201,38 @@ slidefind(const char * const sname,
     }
     pos = 32;
     
-    /* Search for a slide with the requested name. */
+    /* Search for a slide with the requested name or list the directory */
     
-    while (TRUE) {
-        if ((fread(libent, 36, 1, slfile) != 1) ||
-            (strnlen((char *)libent, 32) == 0)) {
+    for (found = false, eof = false; !found && !eof; ) {
+        size_t readCt;
+        readCt = fread(libent, 36, 1, slfile);
+        if (readCt != 1)
+            eof = true;
+        else if (strnlen((char *)libent, 32) == 0)
+            eof = true;
+
+        if (!eof) {
+            pos += 36;
             if (dironly) {
-                return;
-            }
-            pm_error("slide %s not in library.", sname);
-        }
-        pos += 36;
-        if (dironly) {
-            pm_message("  %s", libent);
-        } else if (strneq((char *)libent, uname, 32)) {
-            long dpos = (((((libent[35] << 8) | libent[34]) << 8) |
-                          libent[33]) << 8) | libent[32];
-            if ((slfile == stdin) || (fseek(slfile, dpos, 0) == -1)) {
-                dpos -= pos;
+                pm_message("  %s", libent);
+            } else if (strneq((char *)libent, uname, 32)) {
+                long dpos;
+
+                dpos = (((((libent[35] << 8) | libent[34]) << 8) |
+                         libent[33]) << 8) | libent[32];
         
-                while (dpos-- > 0)
-                    getc(slfile);
+                if ((slfile == stdin) || (fseek(slfile, dpos, 0) == -1)) {
+                    dpos -= pos;
+
+                    while (dpos-- > 0)
+                        getc(slfile);
+                }
+                found = true;
             }
-            break;
         }
     }
+    if (!found && !dironly)
+        pm_error("slide '%s' not in library.", sname);
 }
 
 
@@ -329,7 +338,7 @@ slider(slvecfn   slvec,
     
     /* Process the header of the slide file.  */
     
-    sdrawkcab = FALSE;            /* Initially guess byte order is OK */
+    sdrawkcab = false;            /* Initially guess byte order is OK */
     fread(slfrof.slh, 17, 1, slfile);
     fread(&slfrof.sntype, sizeof(char), 1, slfile);
     fread(&slfrof.slevel, sizeof(char), 1, slfile);
@@ -364,12 +373,12 @@ slider(slvecfn   slvec,
     */
 
     if (btest != rtest) {
-        sdrawkcab = TRUE;
-#define rshort(x) x = ((x >> 8) & 0xFF) | (x << 8)
+        sdrawkcab = true;
+        #define rshort(x) x = ((x >> 8) & 0xFF) | (x << 8)
         rshort(slfrof.sxdots);
         rshort(slfrof.sydots);
         rshort(slfrof.shwfill);
-#undef rshort
+        #undef rshort
     }
     
     /* Dump the header if we're blithering. */
@@ -413,7 +422,7 @@ slider(slvecfn   slvec,
         ixdots = slfrof.sxdots;
         iydots = slfrof.sydots;
         dsar = slfrof.sdsar;
-        adjust = FALSE;           /* Mark no adjustment needed */
+        adjust = false;           /* Mark no adjustment needed */
     }
 
     /* If there's a uniform scale factor specified, apply it. */
@@ -571,32 +580,42 @@ slider(slvecfn   slvec,
 /*  Main program. */
 
 int
-main(int    argc,
-     char * argv[]) {
+main(int          argc,
+     const char * argv[]) {
 
     int argn;
     const char * const usage = "[-verbose] [-info] [-adjust] [-scale <s>]\n\
 [-dir] [-lib|-Lib <name>]\n\
 [-xsize|-width <x>] [-ysize|-height <y>] [sldfile]";
-    int scalespec = FALSE, widspec = FALSE, hgtspec = FALSE, dironly = FALSE,
-        ucasen;
+    bool dironly;
+    bool hgtspec;
+    bool widspec;
+    bool scalespec;
+    bool ucasen;
     const char * slobber;       /* Slide library item */
 
+    pm_proginit(&argc, argv);
+    argn = 1;
 
     slobber = NULL;
-
-    ppm_init(&argc, argv);
-    argn = 1;
+    dironly = false;
+    hgtspec = false;
+    widspec = false;
+    scalespec = false;
+    ucasen = false;
+    blither = false;
+    info = false;
+    adjust = false;
 
     while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') {
         if (pm_keymatch(argv[argn], "-verbose", 2)) {
-            blither = TRUE;
+            blither = true;
         } else if (pm_keymatch(argv[argn], "-adjust", 2)) {
-            adjust = TRUE;
+            adjust = true;
         } else if (pm_keymatch(argv[argn], "-dir", 2)) {
-            dironly = TRUE;
+            dironly = true;
         } else if (pm_keymatch(argv[argn], "-info", 2)) {
-            info = TRUE;
+            info = true;
         } else if (pm_keymatch(argv[argn], "-lib", 2)) {
             if (slobber)
                 pm_error("already specified a library item");
@@ -616,7 +635,7 @@ main(int    argc,
             if (uscale <= 0.0) {
                 pm_error("scale factor must be greater than 0");
             }
-            scalespec = TRUE;
+            scalespec = true;
         } else if (pm_keymatch(argv[argn], "-xsize", 2) ||
                    pm_keymatch(argv[argn], "-width", 2)) {
             if (widspec) {
@@ -625,7 +644,7 @@ main(int    argc,
             argn++;
             if ((argn == argc) || (sscanf(argv[argn], "%d", &sxsize) != 1))
                 pm_usage(usage);
-            widspec = TRUE;
+            widspec = true;
         } else if (pm_keymatch(argv[argn], "-ysize", 2) ||
                    pm_keymatch(argv[argn], "-height", 2)) {
             if (hgtspec) {
@@ -634,7 +653,7 @@ main(int    argc,
             argn++;
             if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1))
                 pm_usage(usage);
-            hgtspec = TRUE;
+            hgtspec = true;
         } else {
             pm_usage(usage);
         }
@@ -665,7 +684,7 @@ main(int    argc,
  
     if (!dironly) {
         slider(draw, flood);
-        ppm_writeppm(stdout, pixels, pixcols, pixrows, pixmaxval, FALSE);
+        ppm_writeppm(stdout, pixels, pixcols, pixrows, pixmaxval, 0);
     }
     pm_close(slfile);
     pm_close(stdout);
diff --git a/converter/ppm/sputoppm.c b/converter/ppm/sputoppm.c
index 2671bffa..acf61c1b 100644
--- a/converter/ppm/sputoppm.c
+++ b/converter/ppm/sputoppm.c
@@ -1,4 +1,4 @@
-/* sputoppm.c - read an uncompressed Spectrum file and produce a portable pixmap
+/* sputoppm.c - read an uncompressed Spectrum file and produce a PPM
 **
 ** Copyright (C) 1991 by Steve Belczyk and Jef Poskanzer
 **
@@ -16,93 +16,114 @@
 #define COLS 320
 #define MAXVAL 7
 
-static pixel pal[ROWS][48];                /* Spectrum palettes, three per row */
-static short screen[ROWS*COLS/4];          /* simulates the Atari's video RAM */
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
+typedef struct {
+    pixel pal[ROWS][48];  /* Spectrum palettes, three per row */
+} Pal;
+
+
+
+static void
+readPalettes(FILE * const ifP,
+             Pal *  const palP) {
+
+    unsigned int row;
+
+    /* Clear the first palette line. */
     {
-    FILE* ifp;
-    int i, j;
-    pixel* pixelrow;
-    register pixel* pP;
-    int row, col;
+        unsigned int j;
+        for (j = 0; j < 48; ++j)
+            PPM_ASSIGN(palP->pal[0][j], 0, 0, 0);
+    }
+    /* Read the palettes. */
+    for (row = 1; row < ROWS; ++row) {
+        unsigned int j;
+        for (j = 0; j < 48; ++j) {
+            short k;
+            pm_readbigshort(ifP, &k);
+            PPM_ASSIGN(palP->pal[row][j],
+                       (k & 0x700) >> 8,
+                       (k & 0x070) >> 4,
+                       (k & 0x007) >> 0);
+        }
+    }
+}
+
+
+
+int
+main(int argc, const char ** argv) {
 
+    FILE * ifP;
+    unsigned int i;
+    pixel * pixelrow;
+    unsigned int row;
+    Pal pal;
+    short screen[ROWS*COLS/4];      /* simulates the Atari's video RAM */
 
-    ppm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     /* Check args. */
     if ( argc > 2 )
         pm_usage( "[spufile]" );
 
     if ( argc == 2 )
-        ifp = pm_openr( argv[1] );
+        ifP = pm_openr( argv[1] );
     else
-        ifp = stdin;
+        ifP = stdin;
 
     /* Read the SPU file */
 
     /* Read the screen data. */
-    for ( i = 0; i < ROWS*COLS/4; ++i )
-        (void) pm_readbigshort( ifp, &screen[i] );
-
-    /* Clear the first palette line. */
-    for ( j = 0; j < 48; ++j )
-        PPM_ASSIGN( pal[0][j], 0, 0, 0 );
+    for (i = 0; i < ROWS*COLS/4; ++i)
+        pm_readbigshort(ifP, &screen[i]);
 
-    /* Read the palettes. */
-    for ( i = 1; i < ROWS; ++i )
-        for ( j = 0; j < 48; ++j )
-            {
-            short k;
-            (void) pm_readbigshort( ifp, &k );
-            PPM_ASSIGN( pal[i][j],
-                ( k & 0x700 ) >> 8,
-                ( k & 0x070 ) >> 4,
-                ( k & 0x007 ) );
-            }
+    readPalettes(ifP, &pal);
 
-    pm_close( ifp );
+    pm_close(ifP);
 
     /* Ok, get set for writing PPM. */
-    ppm_writeppminit( stdout, COLS, ROWS, (pixval) MAXVAL, 0 );
-    pixelrow = ppm_allocrow( COLS );
+    ppm_writeppminit(stdout, COLS, ROWS, MAXVAL, 0);
+    pixelrow = ppm_allocrow(COLS);
 
     /* Now do the conversion. */
-    for ( row = 0; row < ROWS; ++row )
-        {
-        for ( col = 0, pP = pixelrow; col < COLS; ++col, ++pP )
-            {
-            int c, ind, b, plane, x1;
-
+    for (row = 0; row < ROWS; ++row) {
+        unsigned int col;
+        for (col = 0; col < COLS; ++col) {
             /* Compute pixel value. */
-            ind = 80 * row + ( ( col >> 4 ) << 2 );
-            b = 0x8000 >> (col & 0xf);
-            c = 0;
-            for ( plane = 0; plane < 4; ++plane )
-                if ( b & screen[ind+plane] )
+            unsigned int const ind = 80 * row + ((col >> 4) << 2);
+            unsigned int const b = 0x8000 >> (col & 0xf);
+            unsigned int c;
+            unsigned int plane;
+            unsigned int x1;
+
+            c = 0;  /* initial value */
+            for (plane = 0; plane < 4; ++plane) {
+                if (b & screen[ind + plane])
                     c |= (1 << plane);
-
+            }
             /* Compute palette index. */
             x1 = 10 * c;
-            if ( c & 1 )
+            if ((c & 1) != 0)
                 x1 -= 5;
             else
                 ++x1;
-            if ( ( col >= x1 ) && ( col < ( x1 + 160 ) ) )
+            if ((col >= x1 ) && (col < (x1 + 160)))
                 c += 16;
-            if ( col >= ( x1 + 160 ) )
+            if (col >= (x1 + 160))
                 c += 32;
 
-            /* Store the proper color. */
-            *pP = pal[row][c];
-            }
-        ppm_writeppmrow( stdout, pixelrow, COLS, (pixval) MAXVAL, 0 );
+            /* Set the proper color. */
+            pixelrow[col] = pal.pal[row][c];
         }
+        ppm_writeppmrow(stdout, pixelrow, COLS, MAXVAL, 0);
+    }
+
+    ppm_freerow(pixelrow);
+    pm_close(stdout);
+
+    return 0;
+}
+
 
-    pm_close( stdout );
 
-    exit( 0 );
-    }
diff --git a/converter/ppm/tgatoppm.c b/converter/ppm/tgatoppm.c
index f8538214..95893089 100644
--- a/converter/ppm/tgatoppm.c
+++ b/converter/ppm/tgatoppm.c
@@ -71,7 +71,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc - 1 == 0)
diff --git a/converter/ppm/winicontoppm.c b/converter/ppm/winicontoppm.c
index ad859c94..6b1376b2 100644
--- a/converter/ppm/winicontoppm.c
+++ b/converter/ppm/winicontoppm.c
@@ -31,10 +31,9 @@
 #define MAJVERSION 0
 #define MINVERSION 4
 
-static int file_offset = 0;    /* not actually used, but useful for debug */
+static int fileOffset = 0;    /* not actually used, but useful for debug */
 static const char     er_read[] = "%s: read error";
 static const char *   infname;
-static FILE *   ifp;
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -53,8 +52,8 @@ struct cmdlineInfo {
 
 
 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.  
@@ -65,13 +64,15 @@ 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));
-        /* Instructions to optParseOptions3 on how to parse our options.
+    optEntry * option_def;
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
     unsigned int option_def_index;
 
+    MALLOCARRAY(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0, "allicons",     OPT_FLAG,   NULL,                  
             &cmdlineP->allicons,       0 );
@@ -88,10 +89,9 @@ parseCommandLine ( int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-
     if (argc-1 < 1) 
         cmdlineP->inputFilespec = "-";
     else
@@ -116,37 +116,37 @@ parseCommandLine ( int argc, char ** argv,
 
 
 static int 
-GetByte(void) {
+GetByte(FILE * const ifP) {
+
     int v;
    
-    if ((v = getc(ifp)) == EOF)
-    {
+    v = getc(ifP);
+    if (v == EOF)
         pm_error(er_read, infname);
-    }
    
     return v;
 }
+
+
    
 static short 
-GetShort(void) {
+GetShort(FILE * const ifP) {
+
     short v;
    
-    if (pm_readlittleshort(ifp, &v) == -1)
-    {
-        pm_error(er_read, infname);
-    }
-   
+    pm_readlittleshort(ifP, &v);
+
     return v;
 }
+
+
    
 static long 
-GetLong(void) {
+GetLong(FILE * const ifP) {
+
     long v;
    
-    if (pm_readlittlelong(ifp, &v) == -1)
-    {
-        pm_error(er_read, infname);
-    }
+    pm_readlittlelong(ifP, &v);
    
     return v;
 }
@@ -158,14 +158,18 @@ GetLong(void) {
  * functions.
  */
 static u1 
-readU1 (void) {
-    file_offset++;
-    return GetByte();
+readU1(FILE * const ifP) {
+
+    ++fileOffset;
+
+    return GetByte(ifP);
 }
 
+
+
 static u1 * 
-readU1String (int length)
-{
+readU1String (FILE *       const ifP,
+              unsigned int const length) {
    
     u1 * string;
     
@@ -173,150 +177,173 @@ readU1String (int length)
     if (string == NULL)
         pm_error("out of memory");
 
-    fread(string,sizeof(u1),length,ifp);
+    fread(string, sizeof(u1), length, ifP);
     string[length] = 0;
-    file_offset += length * sizeof(u1);
+    fileOffset += length * sizeof(u1);
+
     return string;
 }
 
+
+
 static u2 
-readU2 (void) {
-    file_offset +=2;
-    return GetShort();
+readU2 (FILE * const ifP) {
+
+    fileOffset +=2;
+
+    return GetShort(ifP);
 }
 
+
+
 static u4 
-readU4 (void) {
-    file_offset += 4;
-    return GetLong();
+readU4 (FILE * const ifP) {
+
+    fileOffset += 4;
+
+    return GetLong(ifP);
 }
 
+
+
 static IC_Entry 
-readICEntry (void) 
-{
-    IC_Entry entry;
+readICEntry(FILE * const ifP) {
+
+    IC_Entry entryP;
 
-    MALLOCVAR(entry);
+    MALLOCVAR(entryP);
 
-    if (entry == NULL)
+    if (entryP == NULL)
         pm_error("Unable to allcoate memory for IC entry");
 
-    entry->width         = readU1();
-    entry->height        = readU1();
-    entry->color_count   = readU1();
-    entry->reserved      = readU1();
-    entry->planes        = readU2();
-    entry->bitcount      = readU2();
-    entry->size_in_bytes = readU4();
-    entry->file_offset   = readU4();
-    entry->colors        = NULL;
-    entry->ih            = NULL;
-    entry->xorBitmap     = NULL;
-    entry->andBitmap     = NULL;
+    entryP->width         = readU1(ifP);
+    entryP->height        = readU1(ifP);
+    entryP->color_count   = readU1(ifP);
+    entryP->reserved      = readU1(ifP);
+    entryP->planes        = readU2(ifP);
+    entryP->bitcount      = readU2(ifP);
+    entryP->size_in_bytes = readU4(ifP);
+    entryP->file_offset   = readU4(ifP);
+    entryP->colors        = NULL;
+    entryP->ih            = NULL;
+    entryP->xorBitmap     = NULL;
+    entryP->andBitmap     = NULL;
     
-    return entry;
+    return entryP;
 }
 
 
 
 static IC_InfoHeader 
-readInfoHeader (IC_Entry entry) 
-{
-    IC_InfoHeader ih;
+readInfoHeader (FILE *   const ifP,
+                IC_Entry const entryP) {
+
+    IC_InfoHeader ihP;
 
-    MALLOCVAR(ih);
+    MALLOCVAR(ihP);
     
-    if (ih == NULL)
+    if (ihP == NULL)
         pm_error("Unable to allocate memory for info header");
 
-    ih->size            = readU4();
-    ih->width           = readU4();
-    ih->height          = readU4();
-    ih->planes          = readU2();
-    ih->bitcount        = readU2();
-    ih->compression     = readU4();
-    ih->imagesize       = readU4();
-    ih->x_pixels_per_m  = readU4();
-    ih->y_pixels_per_m  = readU4();
-    ih->colors_used     = readU4();
-    ih->colors_important = readU4();
+    ihP->size             = readU4(ifP);
+    ihP->width            = readU4(ifP);
+    ihP->height           = readU4(ifP);
+    ihP->planes           = readU2(ifP);
+    ihP->bitcount         = readU2(ifP);
+    ihP->compression      = readU4(ifP);
+    ihP->imagesize        = readU4(ifP);
+    ihP->x_pixels_per_m   = readU4(ifP);
+    ihP->y_pixels_per_m   = readU4(ifP);
+    ihP->colors_used      = readU4(ifP);
+    ihP->colors_important = readU4(ifP);
     
-    if (!entry->bitcount) entry->bitcount = ih->bitcount;
-    if (entry->color_count == 0 && 
-        entry->bitcount <= 8) entry->color_count = 256;
-    if (ih->compression) {
+    if (!entryP->bitcount)
+        entryP->bitcount = ihP->bitcount;
+    
+    if (entryP->color_count == 0 && entryP->bitcount <= 8)
+        entryP->color_count = 256;
+
+    if (ihP->compression) {
         pm_error("Can't handle compressed icons");
     }
-    return ih;
+    return ihP;
 }
 
-/*
- * I don't know why this isn't the same as the spec, it just <b>isn't</b>
- * The colors honestly seem to be stored BGR.  Bizarre.
- * 
- * I've checked this in the BMP code for bmptoppm and the gimp.  Guess the
- * spec I have is just plain wrong.
- */
+
+
 static IC_Color 
-readICColor (void) 
-{
-    IC_Color col;
+readICColor(FILE * const ifP)  {
+
+    IC_Color colorP;
 
-    MALLOCVAR(col);
+    MALLOCVAR(colorP);
 
-    if (col == NULL)
+    if (colorP == NULL)
         pm_error("Unable to allocate memory for color");
 
-    col->blue     = readU1();
-    col->green    = readU1();
-    col->red      = readU1();
-    col->reserved = readU1();
-    return col;
+    /* I don't know why this isn't the same as the spec, it just isn't.
+       The colors honestly seem to be stored BGR.  Bizarre.
+      
+       I've checked this in the BMP code for bmptoppm and the gimp.  Guess the
+       spec I have is just plain wrong.
+    */
+
+    colorP->blue     = readU1(ifP);
+    colorP->green    = readU1(ifP);
+    colorP->red      = readU1(ifP);
+    colorP->reserved = readU1(ifP);
+
+    return colorP;
 }
    
 
 
-/*
- * Depending on if the image is stored as 1bpp, 4bpp or 8bpp, the 
- * encoding mechanism is different.
- * 
- * 8bpp => 1 byte/palette index.
- * 4bpp => High Nibble, Low Nibble
- * 1bpp => 1 palette value per bit, high bit 1st.
- */
 static u1 * 
-read1Bitmap (int width, int height) 
-{
-    int tmp;
-    int xBytes;
+read1Bitmap (FILE *       const ifP,
+             unsigned int const width,
+             unsigned int const height) {
+
+    unsigned int row;
+    unsigned int xByteCt;
     u1 * bitmap;
-    int wt = width;
+    unsigned int wt;
 
     MALLOCARRAY(bitmap, width * height);
     if (bitmap == NULL)
         pm_error("out of memory");
 
-    wt >>= 3;
-    if (wt & 3) {
-        wt = (wt & ~3) + 4;
+    /* Depending on if the image is stored as 1bpp, 4bpp or 8bpp, the 
+       encoding mechanism is different.
+  
+       8bpp => 1 byte/palette index.
+       4bpp => High Nibble, Low Nibble
+       1bpp => 1 palette value per bit, high bit 1st.
+    */
+
+    wt = width >> 3;
+
+    if ((wt & 0x3) != 0) {
+        wt = (wt & ~0x3) + 4;
     }
-    xBytes = wt;
-    for (tmp = 0; tmp<height; tmp++ ) {
-        int x;
-        int rowByte = 0;
-        int xOrVal = 128;
-        u1 * row = readU1String(xBytes);
-        for (x = 0; x< width; x++) {
-            *(bitmap+((height-tmp-1)*width) + (x)) = 
-                (row[rowByte] & xOrVal) / xOrVal;
-            if (xOrVal == 1) {
-                xOrVal = 128;
-                rowByte++;
-            } else {
+    xByteCt = wt;
+
+    for (row = 0; row < height; ++row) {
+        u1 * const imgRow = readU1String(ifP, xByteCt);
+
+        unsigned int col;
+        unsigned int rowByte;
+        unsigned int xOrVal;
+        
+        for (col = 0, rowByte = 0, xOrVal = 0x80; col < width; ++col) {
+            *(bitmap+((height - row - 1) * width) + col) = 
+                (imgRow[rowByte] & xOrVal) / xOrVal;
+            if (xOrVal == 0x01) {
+                xOrVal = 0x80;
+                ++rowByte;
+            } else
                 xOrVal >>= 1;
-            }
         }
-        free(row);
+        free(imgRow);
     }
     return bitmap;
 }
@@ -324,43 +351,46 @@ read1Bitmap (int width, int height)
 
    
 static u1 * 
-read4Bitmap (int width, int height) 
-{
-    int tmp;
+read4Bitmap (FILE *       const ifP,
+             unsigned int const width,
+             unsigned int const height) {
+
+    unsigned int row;
     u1 * bitmap;
 
-    int wt = width;
-    int xBytes;
+    unsigned int wt;
+    unsigned int xByteCt;
 
     MALLOCARRAY(bitmap, width * height);
     if (bitmap == NULL)
         pm_error("out of memory");
 
+    wt = width >> 1;
 
-    wt >>= 1;
-    if (wt & 3) {
-        wt = (wt & ~3) + 4;
+    if (wt & 0x3) {
+        wt = (wt & ~0x3) + 4;
     }
-    xBytes = wt;
-    for (tmp = 0; tmp<height ; tmp++ ) {
-        int rowByte = 0;
-        int bottom = 1;
-        int x;
-        u1 * row = readU1String(xBytes);
-        for (x = 0; x< width; x++) {
-            /*
-             * 2 nibbles, 2 values.
-             */
+    xByteCt = wt;
+
+    for (row = 0; row < height; ++row) {
+        u1 * const imgRow = readU1String(ifP, xByteCt);
+
+        unsigned int rowByte;
+        bool bottom;
+        unsigned int col;
+        for (col = 0, rowByte = 0, bottom = true; col < width; ++col) {
+            /* 2 nibbles, 2 values */
             if (bottom) {
-                *(bitmap+((height-tmp-1)*width) + (x)) = 
-                    (row[rowByte] & 0xF0) >> 4;
+                *(bitmap + ((height - row - 1) * width) + col) = 
+                    (imgRow[rowByte] & 0xF0) >> 4;
             } else {
-                *(bitmap+((height-tmp-1)*width) + (x)) = (row[rowByte] & 0xF);
-                rowByte++;
+                *(bitmap + ((height - row -1) * width) + col) =
+                    (imgRow[rowByte] & 0xF);
+                ++rowByte;
             }
             bottom = !bottom;
         }
-    free(row);
+        free(imgRow);
     }
     return bitmap;
 }
@@ -368,52 +398,57 @@ read4Bitmap (int width, int height)
 
    
 static u1 * 
-read8Bitmap (int width, int height) 
-{
-    int tmp;
-    unsigned int xBytes;
-    unsigned int wt = width;
+read8Bitmap (FILE *       const ifP,
+             unsigned int const width,
+             unsigned int const height) {
+
+    unsigned int row;
+    unsigned int xByteCt;
+    unsigned int wt;
     u1 * bitmap;
    
     MALLOCARRAY(bitmap, width * height);
     if (bitmap == NULL)
         pm_error("out of memory");
 
-    if (wt & 3) {
-        wt = (wt & ~3) + 4;
+    wt = width;
+    if (wt & 0x3) {
+        wt = (wt & ~0x3) + 4;
     }
-    xBytes = wt;
-    for (tmp = 0; tmp<height ; tmp++ ) {
-        int rowByte = 0;
-        int x;
-        u1 * row = readU1String(xBytes);
-        for ( x = 0; x< width; x++) {
-            *(bitmap+((height-tmp-1)*width) + (x)) = row[rowByte];
-            rowByte++;
-        }
-        free(row);
+    xByteCt = wt;
+
+    for (row = 0; row < height; ++row) {
+        u1 * imgRow = readU1String(ifP, xByteCt);
+
+        unsigned int rowByte;
+        unsigned int col;
+        for (col = 0, rowByte = 0; col < width; ++col)
+            *(bitmap + ((height - row - 1) * width) + col) = imgRow[rowByte++];
+
+        free(imgRow);
     }
     return bitmap;
 }
 
 
 
-/*
- * Read a true color bitmap. (24/32 bits)
- * 
- * The output routine deplanarizes it for us, we keep it flat here.
- */
 static u1 *
-readXBitmap (int const width, 
-             int const height, 
-             int const bpp) {
-    int          const bytes  = bpp >> 3;
-    unsigned int const xBytes = width * bytes;
+readXBitmap (FILE *       const ifP,
+             unsigned int const width, 
+             unsigned int const height, 
+             unsigned int const bpp) {
+/*----------------------------------------------------------------------------
+  Read a true color bitmap. (24/32 bits)
+  
+  The output routine deplanarizes it for us, we keep it flat here.
+-----------------------------------------------------------------------------*/
+    unsigned int const byteCt = bpp >> 3;
+    unsigned int const xByteCt = width * byteCt;
 
     u1 * bitmap;
         /* remember - bmp (dib) stored upside down, so reverse */
 
-    MALLOCARRAY(bitmap, bytes * width * height);
+    MALLOCARRAY(bitmap, byteCt * width * height);
     if (bitmap == NULL)
         pm_error("out of memory allocating bitmap array");
 
@@ -421,12 +456,12 @@ readXBitmap (int const width,
         unsigned int i;
         u1 * bitcurptr;
 
-        for (i = 0, bitcurptr = &bitmap[bytes * width * (height-1)];
+        for (i = 0, bitcurptr = &bitmap[byteCt * width * (height-1)];
              i < height; 
-             ++i, bitcurptr -= xBytes) {
+             ++i, bitcurptr -= xByteCt) {
 
-            u1 * const row = readU1String(xBytes);
-            memcpy(bitcurptr, row, xBytes);
+            u1 * const row = readU1String(ifP, xByteCt);
+            memcpy(bitcurptr, row, xByteCt);
             free(row);
         }
     }
@@ -436,80 +471,70 @@ readXBitmap (int const width,
 
 
 static MS_Ico 
-readIconFile (bool const verbose) {
-    int iter,iter2;
+readIconFile(FILE * const ifP,
+             bool   const verbose) {
+
+    unsigned int i;
 
     MS_Ico MSIconData;
 
     MALLOCVAR(MSIconData);
    
-    /*
-     * reserved - should equal 0.
-     */
-    MSIconData->reserved = readU2();
-    /*
-     * Type - should equal 1
-     */
-    MSIconData->type     = readU2();
-    /*
-     * count - no of icons in file..
-     */
-    MSIconData->count    = readU2();
-    /*
-     * Allocate "count" array of entries.
-     */
+    MSIconData->reserved = readU2(ifP);  /* should be 0 */
+    MSIconData->type     = readU2(ifP);  /* should be 1 */
+    MSIconData->count    = readU2(ifP);  /* # icons in file */
+
     if (verbose) 
         pm_message("Icon file contains %d icons.", MSIconData->count);
 
     MALLOCARRAY(MSIconData->entries, MSIconData->count);
     if (MSIconData->entries == NULL)
         pm_error("out of memory");
-    /*
-     * Read in each of the entries
-     */
-    for (iter = 0;iter < MSIconData->count ; iter++ ) {
-        MSIconData->entries[iter] = readICEntry();
-    }
-    /* After that, we have to read in the infoheader, color map (if
-     * any) and the actual bit/pix maps for the icons.  
-     */
+
+    /* Read in each of the entries */
+    for (i = 0; i < MSIconData->count; ++i)
+        MSIconData->entries[i] = readICEntry(ifP);
+
+    /* Read in the infoheader, color map (if any) and the actual bit/pix maps
+       for the icons.
+    */
     if (verbose) 
-        fprintf (stderr,"#\tColors\tBPP\tWidth\tHeight\n");
-    for (iter = 0;iter < MSIconData->count ; iter++ ) {
-        int bpp;
-        MSIconData->entries[iter]->ih =
-            readInfoHeader (MSIconData->entries[iter]);
+        pm_message("#\tColors\tBPP\tWidth\tHeight\n");
+
+    for (i = 0; i < MSIconData->count; ++i) {
+        unsigned int bpp;  /* bits per pixel */
+
+        MSIconData->entries[i]->ih =
+            readInfoHeader(ifP, MSIconData->entries[i]);
        
-        /* What's the bits per pixel? */
-        bpp = MSIconData->entries[iter]->bitcount; 
+        bpp = MSIconData->entries[i]->bitcount; 
+
         /* Read the palette, if appropriate */
         switch (bpp) {
         case 24:
         case 32:
             /* 24/32 bpp icon has no palette */
             break;
-        default:
-            MALLOCARRAY(MSIconData->entries[iter]->colors, 
-                        MSIconData->entries[iter]->color_count);
-            if (MSIconData->entries[iter]->colors == NULL)
+        default: {
+            unsigned int j;
+
+            MALLOCARRAY(MSIconData->entries[i]->colors, 
+                        MSIconData->entries[i]->color_count);
+            if (MSIconData->entries[i]->colors == NULL)
                 pm_error("out of memory");
 
-            for (iter2 = 0;
-                 iter2 < MSIconData->entries[iter]->color_count ; 
-                 iter2++ ) {
-                MSIconData->entries[iter]->colors[iter2] = readICColor();
-            }
-            break;
+            for (j = 0; j < MSIconData->entries[i]->color_count; ++j)
+                MSIconData->entries[i]->colors[j] = readICColor(ifP);
+        }
         }
         if (verbose) {
-            char cols_text[10];
-            sprintf (cols_text, "%d", MSIconData->entries[iter]->color_count);
-            fprintf (stderr,
-                     "%d\t%s\t%d\t%d\t%d\n", iter,
-                     MSIconData->entries[iter]->color_count ? 
-                     cols_text : "TRUE",
-                     bpp, MSIconData->entries[iter]->width, 
-                     MSIconData->entries[iter]->height);
+            char colsText[10];
+            sprintf (colsText, "%d", MSIconData->entries[i]->color_count);
+            pm_message("%d\t%s\t%d\t%d\t%d\n", i,
+                       MSIconData->entries[i]->color_count ? 
+                       colsText : "TRUE",
+                       bpp, MSIconData->entries[i]->width, 
+                       MSIconData->entries[i]->height);
         }
         /* Pixels are stored bottom-up, left-to-right. Pixel lines are
          * padded with zeros to end on a 32bit (4byte) boundary. Every
@@ -528,35 +553,40 @@ readIconFile (bool const verbose) {
              */
             switch (bpp) {
             case 1:
-                MSIconData->entries[iter]->xorBitmap = 
-                    read1Bitmap(MSIconData->entries[iter]->width,
-                                MSIconData->entries[iter]->height);
+                MSIconData->entries[i]->xorBitmap = 
+                    read1Bitmap(ifP,
+                                MSIconData->entries[i]->width,
+                                MSIconData->entries[i]->height);
                 break;
             case 4:
-                MSIconData->entries[iter]->xorBitmap = 
-                    read4Bitmap(MSIconData->entries[iter]->width,
-                                MSIconData->entries[iter]->height);
+                MSIconData->entries[i]->xorBitmap = 
+                    read4Bitmap(ifP,
+                                MSIconData->entries[i]->width,
+                                MSIconData->entries[i]->height);
                 break;
             case 8:
-                MSIconData->entries[iter]->xorBitmap = 
-                    read8Bitmap(MSIconData->entries[iter]->width,
-                                MSIconData->entries[iter]->height);
+                MSIconData->entries[i]->xorBitmap = 
+                    read8Bitmap(ifP,
+                                MSIconData->entries[i]->width,
+                                MSIconData->entries[i]->height);
                 break;
             case 24:
             case 32:
-                MSIconData->entries[iter]->xorBitmap = 
-                    readXBitmap(MSIconData->entries[iter]->width,
-                                MSIconData->entries[iter]->height,bpp);
+                MSIconData->entries[i]->xorBitmap = 
+                    readXBitmap(ifP,
+                                MSIconData->entries[i]->width,
+                                MSIconData->entries[i]->height,bpp);
                 break;
             default:
-                pm_error("Uncatered bit depth %d",bpp);
+                pm_error("Uncatered bit depth %u", bpp);
             }
             /*
              * Read AND Bitmap
              */
-            MSIconData->entries[iter]->andBitmap = 
-                read1Bitmap(MSIconData->entries[iter]->width,
-                            MSIconData->entries[iter]->height);
+            MSIconData->entries[i]->andBitmap = 
+                read1Bitmap(ifP,
+                            MSIconData->entries[i]->width,
+                            MSIconData->entries[i]->height);
         }
       
     }
@@ -566,15 +596,14 @@ readIconFile (bool const verbose) {
 
 
 static char * 
-trimOutputName(const char inputName[])
-{
+trimmedOutputName(const char inputName[]) {
     /*
      * Just trim off the final ".ppm", if there is one, else return as is.
      * oh, for =~ ... :)
      */
-    char * outFile = strdup(inputName);
-    if (streq(outFile + (strlen (outFile) - 4), ".ppm")) {
-        *(outFile + (strlen (outFile) - 4)) = 0;
+    char * const outFile = strdup(inputName);
+    if (streq(outFile + (strlen(outFile) - 4), ".ppm")) {
+        *(outFile + (strlen(outFile) - 4)) = 0;
     }
     return outFile;
 
@@ -585,29 +614,34 @@ trimOutputName(const char inputName[])
 static int 
 getBestQualityIcon(MS_Ico MSIconData)
 {
-    int x,best,best_size,best_bpp,bpp,size;
-    IC_Entry entry;
-
-    best_size = best_bpp = 0;
-    for (x = 0; x < MSIconData->count; x++) {
-        entry =  MSIconData->entries[x];
-        size = entry->width * entry->height;
-        bpp  = entry->bitcount ? entry->bitcount : entry->ih->bitcount;
-        if (size > best_size) {
-            best = x;
-            best_size = size;
-        } else if (size == best_size && bpp > best_bpp) {
-            best = x;
-            best_bpp = bpp;
+    unsigned int i;
+    unsigned int best;
+    unsigned int bestSize;
+    unsigned int bestBpp;
+
+    for (i = 0, bestSize = 0, bestBpp = 0; i < MSIconData->count; ++i) {
+        IC_Entry const entryP = MSIconData->entries[i];
+        unsigned int const size = entryP->width * entryP->height;
+        unsigned int const bpp  =
+            entryP->bitcount ? entryP->bitcount : entryP->ih->bitcount;
+
+        if (size > bestSize) {
+            best = i;
+            bestSize = size;
+        } else if (size == bestSize && bpp > bestBpp) {
+            best = i;
+            bestBpp = bpp;
         }
     }
     return best;
 }
 
+
+
 static void
 writeXors(FILE *   const multiOutF,
-          char           outputFileBase[], 
-          IC_Entry const entry,
+          char *   const outputFileBase,
+          IC_Entry const entryP,
           int      const entryNum,
           bool     const multiple, 
           bool     const xor) {
@@ -625,85 +659,79 @@ writeXors(FILE *   const multiOutF,
    we are to open a file using outputFileBase[] and 'entryNum' and 'xor'
    to derive its name, and close it afterward.
 -----------------------------------------------------------------------------*/
-    FILE * outF;
-    pixel ** ppm_array;
-    int row;
-    int pel_size;
-    const char *outputFile;
-    int maxval;
-    int forcetext;
+    FILE * ofP;
+    pixel ** pixArray;
+    unsigned int row;
+    const char * outputFileName;
 
     if (multiOutF) {
-        outF = multiOutF;
-        outputFile = strdup("");
+        ofP = multiOutF;
+        outputFileName = strdup("");
     } else {
         if (outputFileBase) {
             if (multiple) {
-                asprintfN(&outputFile, "%s%s_%d.ppm",
-                          outputFileBase,(xor ? "_xor" : ""), entryNum);
+                pm_asprintf(&outputFileName, "%s%s_%d.ppm",
+                            outputFileBase,(xor ? "_xor" : ""), entryNum);
             } else { 
-                asprintfN(&outputFile, "%s%s.ppm",
-                          outputFileBase,(xor ? "_xor" : ""));
+                pm_asprintf(&outputFileName, "%s%s.ppm",
+                            outputFileBase,(xor ? "_xor" : ""));
             }
         } else
-            outputFile = strdup("-");
+            outputFileName = strdup("-");
         
-        outF = pm_openw(outputFile);
+        ofP = pm_openw(outputFileName);
     }
     /* 
-     * allocate an array to save the bmp data into.
-     * note that entry->height will be 1/2 entry->ih->height,
-     * as the latter adds "and" and "xor" height.
-     */
-    ppm_array = ppm_allocarray(entry->width, entry->height);
-    for (row=0; row < entry->height; row++) {
-        u1 * xorRow;
-        int col;
-        switch (entry->bitcount) {
+       Allocate an array to save the bmp data into.
+       note that entry->height will be 1/2 entry->ih->height,
+       as the latter adds "and" and "xor" height.
+    */
+    pixArray = ppm_allocarray(entryP->width, entryP->height);
+    for (row = 0; row < entryP->height; ++row) {
+        switch (entryP->bitcount) {
         case 24:
-        case 32:
-            pel_size = entry->bitcount >> 3;
-            xorRow = entry->xorBitmap + row * entry->width * pel_size;
-            for (col=0; col < entry->width*pel_size;col+=pel_size) {
-                PPM_ASSIGN(ppm_array[row][col/pel_size],
-                           xorRow[col+2],xorRow[col+1],xorRow[col]);
-            }
-            break;
-        default:
-            xorRow = entry->xorBitmap + row * entry->width;
-            for (col=0; col < entry->width; col++) {
-                int colorIndex;
-                IC_Color color;
-                colorIndex  = xorRow[col];
-                color = entry->colors[colorIndex];
-                PPM_ASSIGN(ppm_array[row][col],
-                           color->red,color->green,color->blue);
+        case 32: {
+            unsigned int const pixelSize = entryP->bitcount >> 3;
+            u1 * const xorRow =
+                entryP->xorBitmap + row * entryP->width * pixelSize;
+            unsigned int col;
+            for (col = 0; col < entryP->width * pixelSize; col += pixelSize)
+                PPM_ASSIGN(pixArray[row][col/pixelSize],
+                           xorRow[col+2], xorRow[col+1], xorRow[col]);
+        } break;
+        default: {
+            u1 * const xorRow = entryP->xorBitmap + row * entryP->width;
+            unsigned int col;
+            for (col = 0; col < entryP->width; ++col) {
+                unsigned int const colorIndex = xorRow[col];
+                IC_Color const colorP = entryP->colors[colorIndex];
+                PPM_ASSIGN(pixArray[row][col],
+                           colorP->red, colorP->green, colorP->blue);
             }
-            break;
+        } break;
         }
     }    
     
-    maxval = 255;
-    forcetext = 0;
-
-    ppm_writeppm(outF,ppm_array,entry->width, entry->height, 
-                 (pixval) maxval, forcetext);
-    ppm_freearray(ppm_array,entry->height);
+    ppm_writeppm(ofP, pixArray, entryP->width, entryP->height, 
+                 255 /* maxval */, false /* text */);
+    ppm_freearray(pixArray, entryP->height);
 
-    strfree(outputFile);
+    pm_strfree(outputFileName);
     
     if (!multiOutF) 
-        pm_close(outF);
+        pm_close(ofP);
 }
             
 
 
 static void
-writeAnds(FILE * const multiOutF, 
-          char outputFileBase[], IC_Entry const entry, int const entryNum, 
-          bool multiple) {
+writeAnds(FILE *       const multiOutF, 
+          char         const outputFileBase[],
+          IC_Entry     const entryP,
+          unsigned int const entryNum, 
+          bool         const multiple) {
 /*----------------------------------------------------------------------------
-   Write the "and" image (i.e. the alpha mask) of the image 'IC_Entry' out.
+   Write the "and" image (i.e. the alpha mask) of the image *entryP out.
 
    'multiple' means this is one of multiple images that are being written.
    'entryNum' is the sequence number within the winicon file of the image
@@ -714,114 +742,122 @@ writeAnds(FILE * const multiOutF,
    we are to open a file using outputFileBase[] and 'entryNum' and 'xor'
    to derive its name, and close it afterward.
 -----------------------------------------------------------------------------*/
-    FILE * outF;
-    bit ** pbm_array;
-    u1 * andRow;
-    int row;
+    FILE * ofP;
+    bit ** bitArray;
+    unsigned int row;
 
     if (multiOutF)
-        outF = multiOutF;
+        ofP = multiOutF;
     else {
-        const char *outputFile;
+        const char * outputFileName;
 
         assert(outputFileBase);
 
         if (multiple) 
-            asprintfN(&outputFile, "%s_and_%d.pbm", outputFileBase, entryNum);
+            pm_asprintf(&outputFileName, "%s_and_%u.pbm",
+                        outputFileBase, entryNum);
         else 
-            asprintfN(&outputFile, "%s_and.pbm", outputFileBase);
-        outF = pm_openw(outputFile);
-        strfree(outputFile);
+            pm_asprintf(&outputFileName, "%s_and.pbm", outputFileBase);
+        ofP = pm_openw(outputFileName);
+        pm_strfree(outputFileName);
     }
-    pbm_array = pbm_allocarray(entry->width, entry->height);
-    for (row=0; row < entry->height; row++) {
-        int col;
-        andRow = entry->andBitmap + row * entry->width;
-        for (col=0; col < entry->width; col++) {
+    bitArray = pbm_allocarray(entryP->width, entryP->height);
+    for (row = 0; row < entryP->height; ++row) {
+        u1 * const andRow = entryP->andBitmap + row * entryP->width;
+        unsigned int col;
+        for (col = 0; col < entryP->width; ++col) {
             /* Note: black is transparent in a Netpbm alpha mask */
-            pbm_array[row][col] = andRow[col] ? PBM_BLACK: PBM_WHITE;
+            bitArray[row][col] = andRow[col] ? PBM_BLACK: PBM_WHITE;
         }
     }
 
-    pbm_writepbm(outF, pbm_array, entry->width, entry->height, 0);
+    pbm_writepbm(ofP, bitArray, entryP->width, entryP->height, 0);
 
-    pbm_freearray(pbm_array, entry->height);
+    pbm_freearray(bitArray, entryP->height);
     if (!multiOutF)
-        pm_close (outF);     
+        pm_close(ofP);
 }
 
 
 
 static void
-openMultiXor(char          outputFileBase[], 
+openMultiXor(char    const outputFileBase[], 
              bool    const writeands,
              FILE ** const multiOutFP) {
 
-    const char *outputFile;
+    const char * outputFileName;
 
     if (outputFileBase) {
-        asprintfN(&outputFile, "%s%s.ppm",
-                  outputFileBase, (writeands ? "_xor" : ""));
+        pm_asprintf(&outputFileName, "%s%s.ppm",
+                    outputFileBase, (writeands ? "_xor" : ""));
     } else
-        outputFile = strdup("-");
+        outputFileName = strdup("-");
 
-    /*
-     * Open the output file now, it'll stay open the whole time.
-     */
-    *multiOutFP = pm_openw(outputFile);
+    *multiOutFP = pm_openw(outputFileName);
 
-    strfree(outputFile);
+    pm_strfree(outputFileName);
 }
 
 
 
 static void
-openMultiAnd(char outputFileBase[], FILE ** const multiAndOutFP) {
+openMultiAnd(char    const outputFileBase[],
+             FILE ** const multiAndOutFP) {
 
-    const char *outputFile;
+    const char * outputFileName;
 
     assert(outputFileBase);
 
-    asprintfN(&outputFile, "%s_and.pbm", outputFileBase);
+    pm_asprintf(&outputFileName, "%s_and.pbm", outputFileBase);
     
-    *multiAndOutFP = pm_openw(outputFile);
+    *multiAndOutFP = pm_openw(outputFileName);
 
-    strfree(outputFile);
+    pm_strfree(outputFileName);
 }
 
-static void free_iconentry(IC_Entry entry) {
-    int x;
-    if (entry->colors && entry->color_count) {
-        for (x=0;x<entry->color_count;x++) free(entry->colors[x]);
-        free(entry->colors);
+
+
+static void
+freeIconentry(IC_Entry const entryP) {
+
+    if (entryP->colors && entryP->color_count) {
+        unsigned int i;
+        for (i = 0; i <entryP->color_count; ++i)
+            free(entryP->colors[i]);
+        free(entryP->colors);
     }
-    if (entry->andBitmap) free(entry->andBitmap);
-    if (entry->xorBitmap) free(entry->xorBitmap);
-    if (entry->ih) free(entry->ih);
-    free(entry);
+    if (entryP->andBitmap) free(entryP->andBitmap);
+    if (entryP->xorBitmap) free(entryP->xorBitmap);
+    if (entryP->ih) free(entryP->ih);
+    free(entryP);
 }
 
-static void free_icondata(MS_Ico MSIconData)
-{
-    int x;
-    for (x=0;x<MSIconData->count;x++) {
-    free_iconentry(MSIconData->entries[x]);
-    }
-    free(MSIconData);
+
+
+static void
+freeIcondata(MS_Ico const MSIconDataP) {
+
+    unsigned int i;
+    for (i = 0; i < MSIconDataP->count; ++i)
+        freeIconentry(MSIconDataP->entries[i]);
+
+    free(MSIconDataP);
 }
 
 
+
 int 
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;
-    int startEntry, endEntry;
-    MS_Ico MSIconData;
+    FILE * ifP;
+    unsigned int startEntry, endEntry;
+    MS_Ico MSIconDataP;
     char * outputFileBase;
     FILE * multiOutF;
     FILE * multiAndOutF;
    
-    ppm_init (&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -832,13 +868,13 @@ main(int argc, char *argv[]) {
     if (streq(cmdline.outputFilespec, "-"))
         outputFileBase = NULL;
     else
-        outputFileBase = trimOutputName(cmdline.outputFilespec);
+        outputFileBase = trimmedOutputName(cmdline.outputFilespec);
 
-    ifp = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFilespec);
 
     infname = cmdline.inputFilespec;
 
-    MSIconData = readIconFile(cmdline.verbose);
+    MSIconDataP = readIconFile(ifP, cmdline.verbose);
     /*
      * Now we've read the icon file in (Hopefully! :)
      * Go through each of the entries, and write out files of the
@@ -852,18 +888,15 @@ main(int argc, char *argv[]) {
     /*
      * If allicons is set, we want everything, if not, just go through once.
      */
-    startEntry = 0;
-    if (cmdline.allicons) {
-        endEntry = MSIconData->count;
-    } else {
-        endEntry = 1;
-    }
-    /*
-     * If bestqual is set, find the icon with highest size & bpp.
-     */
     if (cmdline.bestqual) {
-        startEntry = getBestQualityIcon(MSIconData);
+        startEntry = getBestQualityIcon(MSIconDataP);
         endEntry = startEntry+1;
+    } else {
+        startEntry = 0;
+        if (cmdline.allicons)
+            endEntry = MSIconDataP->count;
+        else
+            endEntry = 1;
     }
    
     if (cmdline.multippm) 
@@ -877,24 +910,25 @@ main(int argc, char *argv[]) {
         multiAndOutF = NULL;
 
     {
-        int entryNum;
+        unsigned int entryNum;
 
-        for (entryNum = startEntry ; entryNum < endEntry ; entryNum++ ) {
-            IC_Entry const entry = MSIconData->entries[entryNum];
+        for (entryNum = startEntry; entryNum < endEntry; ++entryNum) {
+            IC_Entry const entryP = MSIconDataP->entries[entryNum];
 
-            writeXors(multiOutF, outputFileBase, entry, entryNum, 
+            writeXors(multiOutF, outputFileBase, entryP, entryNum, 
                       cmdline.allicons, cmdline.writeands);
             if (cmdline.writeands)
                 writeAnds(multiAndOutF, outputFileBase, 
-                          entry, entryNum, cmdline.allicons);
+                          entryP, entryNum, cmdline.allicons);
         }
     }
     if (multiOutF)
-        pm_close (multiOutF);    
+        pm_close(multiOutF);    
     if (multiAndOutF)
         pm_close(multiAndOutF);
     
     /* free up the image data here. */
-    free_icondata(MSIconData);
+    freeIcondata(MSIconDataP);
+
     return 0;
 }
diff --git a/converter/ppm/xim.h b/converter/ppm/xim.h
index ffe60fb6..27556b30 100644
--- a/converter/ppm/xim.h
+++ b/converter/ppm/xim.h
@@ -112,7 +112,7 @@ typedef struct XimAsciiHeader {
 /* Note:
 * - All data is in char's in order to maintain easily portability
 *   across machines, and some human readibility.
-* - Images may be stored as pixmaps (8 bits/pixel) or as seperate
+* - Images may be stored as pixmaps (8 bits/pixel) or as separate
 *   red, green, blue channel data (24+ bits/pixel).
 * - An alpha channel is optional and is found after every num_channels
 *   of data.
diff --git a/converter/ppm/ximtoppm.c b/converter/ppm/ximtoppm.c
index 15589c16..ce5e6396 100644
--- a/converter/ppm/ximtoppm.c
+++ b/converter/ppm/ximtoppm.c
@@ -56,7 +56,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and all of *cmdlineP. */
 
     if (!alphaoutSpec)
@@ -208,7 +208,7 @@ ReadImageChannel(FILE *         const infp,
             }
             marker += i;
         }
-        /* return to the begining of the next image's bufffer */
+        /* return to the beginning of the next image's bufffer */
         if (fseek(infp, marker, 0) == -1) {
             pm_message("ReadImageChannel: can't fseek to location in image buffer" );
             return(0);
diff --git a/converter/ppm/xpmtoppm.c b/converter/ppm/xpmtoppm.c
index 235c3867..27f17931 100644
--- a/converter/ppm/xpmtoppm.c
+++ b/converter/ppm/xpmtoppm.c
@@ -1,43 +1,19 @@
-/* xpmtoppm.c - read an X11 pixmap file and produce a portable pixmap
-**
-** Copyright (C) 1991 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.
-**
-** Upgraded to handle XPM version 3 by
-**   Arnaud Le Hors (lehors@mirsa.inria.fr)
-**   Tue Apr 9 1991
-**
-** Rainer Sinkwitz sinkwitz@ifi.unizh.ch - 21 Nov 91:
-**  - Bug fix, no advance of read ptr, would not read 
-**    colors like "ac c black" because it would find 
-**    the "c" of "ac" and then had problems with "c"
-**    as color.
-**    
-**  - Now understands multiword X11 color names
-**  
-**  - Now reads multiple color keys. Takes the color
-**    of the hightest available key. Lines no longer need
-**    to begin with key 'c'.
-**    
-**  - expanded line buffer to from 500 to 2048 for bigger files
+/* xpmtoppm.c - convert XPM file (X11 pixmap) to PPM
+
+   Copyright and history information is at end of file
 */
 
 #define _BSD_SOURCE   /* 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 "pm_c_util.h"
-#include "ppm.h"
+#include "mallocvar.h"
 #include "shhopt.h"
 #include "nstring.h"
-#include "mallocvar.h"
+#include "ppm.h"
 
 #define MAX_LINE (8 * 1024)
   /* The maximum size XPM input line we can handle. */
@@ -47,72 +23,321 @@
 
 const char *xpmColorKeys[] =
 {
- "s",					/* key #1: symbol */
- "m",					/* key #2: mono visual */
- "g4",					/* key #3: 4 grays visual */
- "g",					/* key #4: gray visual */
- "c",					/* key #5: color visual */
+ "s",                   /* key #1: symbol */
+ "m",                   /* key #2: mono visual */
+ "g4",                  /* key #3: 4 grays visual */
+ "g",                   /* key #4: gray visual */
+ "c",                   /* key #5: color visual */
 };
 
-struct cmdline_info {
+struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    char *input_filespec;  /* Filespecs of input files */
-    char *alpha_filename;
+    const char * input_filespec;  /* Filespecs of input files */
+    const char * alpha_filename;
     int alpha_stdout;
     int verbose;
 };
 
 
-static int verbose;
+static bool verbose;
+
 
 
 static void
-parse_command_line(int argc, char ** argv,
-                   struct cmdline_info *cmdline_p) {
+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.
 -----------------------------------------------------------------------------*/
-    optStruct *option_def = malloc(100*sizeof(optStruct));
+    optEntry * option_def;
         /* Instructions to OptParseOptions2 on how to parse our options.
          */
-    optStruct2 opt;
+    optStruct3 opt;
 
     unsigned int option_def_index;
 
-    option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENTRY(0,   "alphaout",   OPT_STRING, &cmdline_p->alpha_filename, 0);
-    OPTENTRY(0,   "verbose",    OPT_FLAG,   &cmdline_p->verbose,        0);
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "alphaout",   OPT_STRING, &cmdlineP->alpha_filename,
+            NULL, 0);
+    OPTENT3(0,   "verbose",    OPT_FLAG,   &cmdlineP->verbose,
+            NULL, 0);
 
-    cmdline_p->alpha_filename = NULL;
-    cmdline_p->verbose = FALSE;
+    cmdlineP->alpha_filename = NULL;
+    cmdlineP->verbose = FALSE;
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = TRUE;  /* We may have parms that are negative numbers */
 
-    optParseOptions2(&argc, argv, opt, 0);
-        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc - 1 == 0)
-        cmdline_p->input_filespec = NULL;  /* he wants stdin */
+        cmdlineP->input_filespec = NULL;  /* he wants stdin */
     else if (argc - 1 == 1)
-        cmdline_p->input_filespec = strdup(argv[1]);
+        cmdlineP->input_filespec = strdup(argv[1]);
     else 
         pm_error("Too many arguments.  The only argument accepted\n"
                  "is the input file specification");
 
-    if (cmdline_p->alpha_filename && 
-        streq(cmdline_p->alpha_filename, "-"))
-        cmdline_p->alpha_stdout = TRUE;
+    if (cmdlineP->alpha_filename && 
+        streq(cmdlineP->alpha_filename, "-"))
+        cmdlineP->alpha_stdout = TRUE;
     else 
-        cmdline_p->alpha_stdout = FALSE;
+        cmdlineP->alpha_stdout = FALSE;
+
+}
+
+
+
+struct ColorNameHashTableEntry {
+/*----------------------------------------------------------------------------
+   An entry in the color name hash table.  It maps a color name to a
+   color, or is empty.
+-----------------------------------------------------------------------------*/
+    bool empty;
+    char colorName[3];
+        /* Actual length 0-3.  NOT NUL-terminated */
+    pixel color;
+};
+
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is a color map which is primarily a hash table that maps an XPM
+   color name to a color.  An XPM color name is a 0-3 character name that
+   appears in the raster of an XPM image to uniquely identify a color.
+   The header of the XPM contains a listing of all the color names that
+   appear in the raster, identifying a color for each.
+
+   We represent a color as a 'pixel'.
+-----------------------------------------------------------------------------*/
+    unsigned int nameSize;
+        /* Size of color names in this hash.  0-3 */
+    struct ColorNameHashTableEntry * transparentP;
+        /* The element of 'table' that is for the transparent color.
+           NULL if there is none.
+        */
+
+    /* This is an internally chained hash table, i.e. there are no linked
+       lists.  You use the hash function to get an index into the hash table.
+       If the entry indexed by that is not for the color name you're looking
+       for, you look at the next entry down, and keep going down until you
+       either find the color name you're looking for or hit an empty entry.
+
+       So that we never run out of space for new color names, we make the
+       creator of the hash table tell us the maximum number of colors there
+       will be.  We allocate twice that size in order to reduce average hash
+       chain length.
+    */
+    unsigned int size;
+    struct ColorNameHashTableEntry * table;
+} ColorNameHash;
+
+
+
+static ColorNameHash *
+hash_create(unsigned int const nColors,
+            unsigned int const nameSize) {
+
+    ColorNameHash * hashP;
+
+    MALLOCVAR_NOFAIL(hashP);
+
+    hashP->nameSize = nameSize;
+
+    hashP->size = nColors * 2;
+
+    MALLOCARRAY(hashP->table, hashP->size);
+
+    if (!hashP->table)
+        pm_error("Failed to allocate memory for a %u-entry "
+                 "color name hash table.", hashP->size);
+
+    {
+        unsigned int i;
+        for (i = 0; i < hashP->size; ++i)
+            hashP->table[i].empty = true;
+    }
+
+    hashP->transparentP = NULL;
+
+    return hashP;
+}
+
+
+
+static void
+hash_destroy(ColorNameHash * const hashP) {
+
+    free(hashP->table);
+
+    free(hashP);
+}
+
+
+
+static unsigned int
+hashColorName(const char * const name,
+              unsigned int const size,
+              unsigned int const hashTableSize) {
+/*----------------------------------------------------------------------------
+   Return the hash value (initial index into the color name hash table)
+   for the color name 'name', which is 'size' characters long.  The hash
+   is to be in the range [0, hashTableSize).
+-----------------------------------------------------------------------------*/
+    /* I have no idea if this is an appropriate hash function.  I borrowed
+       it from pnm_hashTuple()
+    */
+
+    unsigned int const hash_factor[] = {1, 33, 33*33};
+
+    unsigned int i;
+    unsigned int hash;
 
+    hash = 0;  /* initial value */
+    for (i = 0; i < size; ++i) {
+        hash += name[i] * hash_factor[i];
+    }
+    hash %= hashTableSize;
+    return hash;
+}
+
+
+
+static bool
+entryMatch(struct ColorNameHashTableEntry const entry,
+           const char *                   const name,
+           unsigned int                   const size) {
+
+    if (entry.empty)
+        return true;
+
+    assert(size <= ARRAY_SIZE(entry.colorName));
+
+    {
+        unsigned int i;
+        
+        for (i = 0; i < size; ++i) {
+            if (name[i] != entry.colorName[i])
+                return false;
+        }
+    }
+
+    return true;
+}
+
+
+
+static void
+bumpIndex(unsigned int * const indexP,
+          unsigned int   const tableSize,
+          unsigned int   const limit) {
+/*----------------------------------------------------------------------------
+   Bump *indexP to the next entry in a table of size 'tableSize', in a
+   circular fashion.  But abort the program if this would take us to
+   'limit'.
+-----------------------------------------------------------------------------*/
+    *indexP += 1;
+    if (*indexP >= tableSize)
+        *indexP = 0;
+
+    if (*indexP == limit)
+        pm_error("INTERNAL ERROR: color name hash table is full");
+}
+
+
+
+static void
+hash_find(const ColorNameHash *             const hashP,
+          const char *                      const name,
+          struct ColorNameHashTableEntry ** const entryPP) {
+/*----------------------------------------------------------------------------
+   Find the entry in the color hash table *hashP for the color
+   named 'name' in the lexicon of this XPM file.  If the color is in the
+   table, this is where it is.  If it isn't, this is where it should go.
+-----------------------------------------------------------------------------*/
+    unsigned int const initialIndex  =
+        hashColorName(name, hashP->nameSize, hashP->size);
+
+    unsigned int i;
+
+    for (i = initialIndex;
+         !entryMatch(hashP->table[i], name, hashP->nameSize);
+         bumpIndex(&i, hashP->size, initialIndex));
+         
+    *entryPP = &hashP->table[i];
 }
 
 
+
+static void
+hash_add(ColorNameHash * const hashP,
+         const char *    const name,
+         pixel           const color,
+         bool            const isTransparent) {
+
+    struct ColorNameHashTableEntry * entryP;
+
+    hash_find(hashP, name, &entryP);
+
+    if (!entryP->empty)
+        pm_error("Color name appears multiple times in color map");
+
+    entryP->empty = false;
+    {
+        unsigned int i;
+        for (i = 0; i < hashP->nameSize; ++i)
+            entryP->colorName[i] = name[i];
+    }
+    entryP->color = color;
+
+    if (isTransparent) {
+        if (hashP->transparentP)
+            pm_error("There are multiple NONE (transparent) entries in "
+                     "the XPM color map");
+        else
+            hashP->transparentP = entryP;
+    }
+}
+
+
+
+static pixel
+hash_color(const ColorNameHash * const hashP,
+           const char *          const name) {
+
+    struct ColorNameHashTableEntry * entryP;
+
+    hash_find(hashP, name, &entryP);
+
+    if (entryP->empty)
+        pm_error("Color name in raster is not in color map");
+
+    return entryP->color;
+}
+
+
+
+static bool
+hash_isTransparent(const ColorNameHash * const hashP,
+                   const char *          const name) {
+
+    struct ColorNameHashTableEntry * entryP;
+
+    hash_find(hashP, name, &entryP);
+
+    return (entryP == hashP->transparentP);
+}
+
+
+
 static char lastInputLine[MAX_LINE+1];
     /* contents of line most recently read from input */
 static bool backup;
@@ -121,6 +346,7 @@ static bool backup;
     */
 
 
+
 static void
 getLine(char * const line,
         size_t const size,
@@ -158,21 +384,6 @@ getLine(char * const line,
 
 
 
-static unsigned int
-getNumber(char * const p, unsigned int const size) {
-
-    unsigned int retval;
-    unsigned char * q;
-    
-    retval = 0;
-    for (q = p; q < p+size; ++q)
-        retval = (retval << 8) + *q;
-
-    return retval;
-}
-
-
-
 static void
 getword(char * const output, char ** const cursorP) {
 
@@ -191,90 +402,97 @@ getword(char * const output, char ** const cursorP) {
 
 
 static void
-addToColorMap(unsigned int const seqNum,
-              unsigned int const colorNumber, 
-              pixel * const colors, int * const ptab, 
-              char colorspec[], int const isTransparent,
-              int * const transparentP) {
+addToColorMap(ColorNameHash * const hashP,
+              const char *    const colorName,
+              char            const colorspec[],
+              bool            const isTransparent) {
 /*----------------------------------------------------------------------------
-   Add the color named by colorspec[] to the colormap contained in
-   'colors' and 'ptab', as the color associated with XPM color number
-   'colorNumber', which is the seqNum'th color in the XPM color map.
+   Add the color named by colorspec[] to the colormap represented by *hashP,
+   as the color associated with XPM color name 'colorNumber'.
 
-   Iff 'transparent', set *transparentP to the colormap index that 
-   corresponds to this color.
+   Note that *hashP determines how long 'colorName' is.
 -----------------------------------------------------------------------------*/
-    if (ptab == NULL) {
-        /* Index into table. */
-        colors[colorNumber] = ppm_parsecolor(colorspec,
-                                             (pixval) PPM_MAXMAXVAL);
-        if (isTransparent) 
-            *transparentP = colorNumber;
-    } else {
-        /* Set up linear search table. */
-        colors[seqNum] = ppm_parsecolor(colorspec,
-                                        (pixval) PPM_MAXMAXVAL);
-        ptab[seqNum] = colorNumber;
-        if (isTransparent)
-            *transparentP = seqNum;
+    hash_add(hashP, colorName, ppm_parsecolor(colorspec, PPM_MAXMAXVAL),
+             isTransparent);
+}
+
+
+
+static void
+validateColorName(const char * const name,
+                  unsigned int const charsPerPixel) {
+
+    unsigned int i;
+
+    for (i = 0; i < charsPerPixel; ++i) {
+        if (name[i] == '"')
+            pm_error("A color map entry ends in the middle of the colormap "
+                     "index");
+        else if (name[i] == '\0')
+            pm_error("The XPM file ends in the middle of a color map entry");
     }
 }
 
 
 
 static void
-interpretXpm3ColorTableLine(char line[], int const seqNum, 
-                            int const chars_per_pixel,
-                            pixel * const colors, int * const ptab,
-                            int * const transparentP) {
+interpretXpm3ColorTableLine(char               const line[],
+                            unsigned int       const seqNum, 
+                            unsigned int       const charsPerPixel,
+                            ColorNameHash *    const hashP) {
 /*----------------------------------------------------------------------------
-   Interpret one line of the color table in the XPM header.  'line' is
-   the line from the XPM file.  It is the seqNum'th color table entry in
-   the file.  The file uses 'chars_per_pixel' characters per pixel.
+   Interpret one line of the color table in the XPM header.  'line' is the
+   line from the XPM file.  It is the seqNum'th color table entry in the file.
+   The raster in the file uses 'charsPerPixel' characters per pixel (i.e.
+   a an XPM color name is 'charsPerPixel' characters).
 
-   Add the information from this color table entry to the color table
-   'colors' and, if it isn't NULL, the corresponding lookup shadow table
-   'ptab' (see readXpm3ColorTable for a description of these data 
-   structures).
+   Add the information from this color table entry to the color name hash
+   *hashP.
 
    The line may include values for multiple kinds of color (grayscale,
    color, etc.).  We take the highest of these (e.g. color over grayscale).
 
    If a color table entry indicates transparency, set *transparentP
-   to the colormap index that corresponds to the indicated color.
+   to indicate the XPM color name.
 -----------------------------------------------------------------------------*/
     /* Note: this code seems to allow for multi-word color specifications,
        but I'm not aware that such are legal.  Ultimately, ppm_parsecolor()
-       interprets the name, and I believe it only takes single word 
+       interprets the name, and I believe it takes only single word 
        color specifications.  -Bryan 2001.05.06.
     */
     char str2[MAX_LINE+1];    
-    char *t1;
-    char *t2;
+    char * t1;
+    char * t2;
     int endOfEntry;   /* boolean */
     
-    unsigned int curkey, key, highkey;	/* current color key */
-    unsigned int lastwaskey;	
+    unsigned int curkey, key, highkey;  /* current color key */
+    bool lastwaskey;    
         /* The last token we processes was a key, and we have processed
            at least one token.
         */
-    char curbuf[BUFSIZ];		/* current buffer */
-    int isTransparent;
+    char curbuf[BUFSIZ];        /* current buffer */
+    bool isTransparent;
     
-    int colorNumber;
-        /* A color number that appears in the raster */
+    const char * colorName;
+        /* The 0-3 character name this color map line gives the color
+           (i.e. the name that the raster uses).  This is NOT NUL-terminated.
+           It's length is bytesPerPixel.
+        */
+
     /* read the chars */
     t1 = strchr(line, '"');
     if (t1 == NULL)
         pm_error("A line that is supposed to be an entry in the color "
                  "table does not start with a quote.  The line is '%s'.  "
-                 "It is the %dth entry in the color table.", 
+                 "It is the %uth entry in the color table.", 
                  line, seqNum);
     else
-        t1++;  /* Points now to first color number character */
+        ++t1;  /* Points now to first color number character */
+    
+    validateColorName(t1, charsPerPixel);
+    colorName = t1;
 
-    colorNumber = getNumber(t1, chars_per_pixel);
-    t1 += chars_per_pixel;
+    t1 += charsPerPixel;
 
     /*
      * read color keys and values 
@@ -306,30 +524,30 @@ interpretXpm3ColorTableLine(char line[], int const seqNum,
                     pm_error("Missing color key token in color table line "
                              "'%s' before '%s'.", line, str2);
                 if (!lastwaskey) 
-                    strcat(curbuf, " ");		/* append space */
-                if ( (strncmp(str2, "None", 4) == 0) 
-                     || (strncmp(str2, "none", 4) == 0) ) {
+                    strcat(curbuf, " ");        /* append space */
+                if ( (strneq(str2, "None", 4)) 
+                     || (strneq(str2, "none", 4)) ) {
                     /* This entry identifies the transparent color number */
                     strcat(curbuf, "#000000");  /* Make it black */
                     isTransparent = TRUE;
                 } else 
-                    strcat(curbuf, str2);		/* append buf */
-                lastwaskey = 0;
+                    strcat(curbuf, str2);       /* append buf */
+                lastwaskey = FALSE;
             } else { 
                 /* This word is a key.  So we've seen the last of the 
                    info for the previous key, and we must either put it
                    in the color map or ignore it if we already have a higher
                    color form in the colormap for this colormap entry.
                 */
-                if (curkey > highkey) {	/* flush string */
-                    addToColorMap(seqNum, colorNumber, colors, ptab, curbuf,
-                                  isTransparent, transparentP);
+                if (curkey > highkey) { /* flush string */
+                    addToColorMap(hashP, colorName, curbuf, isTransparent);
                     highkey = curkey;
                 }
-                curkey = key;			/* set new key  */
-                curbuf[0] = '\0';		/* reset curbuf */
+                /* intialize state to process this new key */
+                curkey = key;
+                curbuf[0] = '\0';
                 isTransparent = FALSE;
-                lastwaskey = 1;
+                lastwaskey = TRUE;
             }
             if (*t2 == '"') break;
         }
@@ -339,8 +557,7 @@ interpretXpm3ColorTableLine(char line[], int const seqNum,
        entry in it)
     */
     if (curkey > highkey) {
-        addToColorMap(seqNum, colorNumber, colors, ptab, curbuf,
-                      isTransparent, transparentP);
+        addToColorMap(hashP, colorName, curbuf, isTransparent);
         highkey = curkey;
     }
     if (highkey == 1) 
@@ -350,152 +567,208 @@ interpretXpm3ColorTableLine(char line[], int const seqNum,
 
 
 static void
-readXpm3Header(FILE * const stream, int * const widthP, int * const heightP, 
-               int * const chars_per_pixelP, int * const ncolorsP,
-               pixel ** const colorsP, int ** const ptabP,
-               int * const transparentP) {
+readV3ColorTable(FILE *             const ifP,
+                 ColorNameHash **   const colorNameHashPP,
+                 unsigned int       const nColors,
+                 unsigned int       const charsPerPixel) {
 /*----------------------------------------------------------------------------
-  Read the header of the XPM file on stream 'stream'.  Assume the
+   Read the color table from the XPM Version 3 header.
+
+   Assume *ifP is positioned to the color table; leave it positioned after.
+-----------------------------------------------------------------------------*/
+    ColorNameHash * const colorNameHashP = hash_create(nColors, charsPerPixel);
+
+    unsigned int seqNum;
+        /* Sequence number of entry within color table in XPM header */
+
+    for (seqNum = 0; seqNum < nColors; ++seqNum) {
+        char line[MAX_LINE+1];
+        getLine(line, sizeof(line), ifP);
+        /* skip the comment line if any */
+        if (strneq(line, "/*", 2))
+            getLine(line, sizeof(line), ifP);
+            
+        interpretXpm3ColorTableLine(line, seqNum, charsPerPixel,
+                                    colorNameHashP);
+                                    
+    }
+    *colorNameHashPP = colorNameHashP;
+}
+
+
+
+static void
+readXpm3Header(FILE *             const ifP,
+               unsigned int *     const widthP,
+               unsigned int *     const heightP, 
+               unsigned int *     const charsPerPixelP,
+               ColorNameHash **   const colorNameHashPP) {
+/*----------------------------------------------------------------------------
+  Read the header of the XPM file on stream *ifP.  Assume the
   getLine() stream is presently positioned to the beginning of the
   file and it is a Version 3 XPM file.  Leave the stream positioned
   after the header.
 
-  We have two ways to return the colormap, depending on the number of
-  characters per pixel in the XPM:  
-  
-  If it is 1 or 2 characters per pixel, we return the colormap as a
-  Netpbm 'pixel' array *colorsP (in newly malloc'ed storage), such
-  that if a color in the raster is identified by index N, then
-  (*colorsP)[N] is that color.  So this array is either 256 or 64K
-  pixels.  In this case, we return *ptabP = NULL.
-
-  If it is more than 2 characters per pixel, we return the colormap as
-  both a Netpbm 'pixel' array *colorsP and a lookup table *ptabP (both
-  in newly malloc'ed storage).
-
-  If a color in the raster is identified by index N, then for some I,
-  (*ptabP)[I] is N and (*colorsP)[I] is the color in question.  So 
-  you iterate through *ptabP looking for N and then look at the 
-  corresponding entry in *colorsP to get the color.
-
-  Return as *transColorNumberP the value of the XPM color number that
-  represents a transparent pixel, or -1 if no color number does.
+  Return as *widthP and *heightP the dimensions of the image indicated
+  by the header.
+
+  Return as *charsPerPixelP the number of characters the header says the
+  raster uses for each pixel, i.e. the XPM color name length.
+
+  Return the color map as *colorNameHashPP.
 -----------------------------------------------------------------------------*/
     char line[MAX_LINE+1];
     const char * xpm3_signature = "/* XPM */";
     
-    *widthP = *heightP = *ncolorsP = *chars_per_pixelP = -1;
+    unsigned int width, height;
+    unsigned int nColors;
+    unsigned int charsPerPixel;
 
     /* Read the XPM signature comment */
-    getLine(line, sizeof(line), stream);
-    if (strncmp(line, xpm3_signature, strlen(xpm3_signature)) != 0) 
+    getLine(line, sizeof(line), ifP);
+    if (!strneq(line, xpm3_signature, strlen(xpm3_signature))) 
         pm_error("Apparent XPM 3 file does not start with '/* XPM */'.  "
                  "First line is '%s'", xpm3_signature);
 
     /* Read the assignment line */
-    getLine(line, sizeof(line), stream);
-    if (strncmp(line, "static char", 11) != 0)
+    getLine(line, sizeof(line), ifP);
+    if (!strneq(line, "static char", 11))
         pm_error("Cannot find data structure declaration.  Expected a "
                  "line starting with 'static char', but found the line "
                  "'%s'.", line);
 
-	/* Read the hints line */
-    getLine(line, sizeof(line), stream);
-    /* skip the comment line if any */
-    if (!strncmp(line, "/*", 2)) {
+    getLine(line, sizeof(line), ifP);
+
+    /* Skip the comment block, if one starts here */
+    if (strneq(line, "/*", 2)) {
         while (!strstr(line, "*/"))
-            getLine(line, sizeof(line), stream);
-        getLine(line, sizeof(line), stream);
+            getLine(line, sizeof(line), ifP);
+        getLine(line, sizeof(line), ifP);
     }
-    if (sscanf(line, "\"%d %d %d %d\",", widthP, heightP,
-               ncolorsP, chars_per_pixelP) != 4)
+
+    /* Parse the hints line */
+    if (sscanf(line, "\"%u %u %u %u\",", &width, &height,
+               &nColors, &charsPerPixel) != 4)
         pm_error("error scanning hints line");
 
-    if (verbose == 1) 
-    {
-        pm_message("Width x Height:  %d x %d", *widthP, *heightP);
-        pm_message("no. of colors:  %d", *ncolorsP);
-        pm_message("chars per pixel:  %d", *chars_per_pixelP);
+    if (verbose) {
+        pm_message("Width x Height:  %u x %u", width, height);
+        pm_message("no. of colors:  %u", nColors);
+        pm_message("chars per pixel:  %u", charsPerPixel);
     }
 
-    /* Allocate space for color table. */
-    if (*chars_per_pixelP <= 2) {
-        /* Set up direct index (see above) */
-        *colorsP = ppm_allocrow(*chars_per_pixelP == 1 ? 256 : 256*256);
-        *ptabP = NULL;
-    } else {
-        /* Set up lookup table (see above) */
-        *colorsP = ppm_allocrow(*ncolorsP);
-        MALLOCARRAY(*ptabP, *ncolorsP);
-        if (*ptabP == NULL)
-            pm_error("Unable to allocate memory for %d colors", *ncolorsP);
-    }
-    
-    { 
-        /* Read the color table */
-        int seqNum;
-            /* Sequence number of entry within color table in XPM header */
-
-        *transparentP = -1;  /* initial value */
-
-        for (seqNum = 0; seqNum < *ncolorsP; seqNum++) {
-            getLine(line, sizeof(line), stream);
-            /* skip the comment line if any */
-            if (!strncmp(line, "/*", 2))
-                getLine(line, sizeof(line), stream);
-            
-            interpretXpm3ColorTableLine(line, seqNum, *chars_per_pixelP, 
-                                        *colorsP, *ptabP, transparentP);
-        }
+    readV3ColorTable(ifP, colorNameHashPP, nColors, charsPerPixel);
+
+    *widthP         = width;
+    *heightP        = height;
+    *charsPerPixelP = charsPerPixel;
+}
+
+
+
+static void
+readV1ColorTable(FILE *           const ifP,
+                 ColorNameHash ** const colorNameHashPP,
+                 unsigned int     const nColors,
+                 unsigned int     const charsPerPixel) {
+/*----------------------------------------------------------------------------
+   Read the color table from the XPM Version 1 header.
+
+   Assume *ifP is positioned to the color table; leave it positioned after.
+-----------------------------------------------------------------------------*/
+    ColorNameHash * const colorNameHashP = hash_create(nColors, charsPerPixel);
+
+    unsigned int i;
+
+    for (i = 0; i < nColors; ++i) {
+        char line[MAX_LINE+1];
+        char str1[MAX_LINE+1];
+        char str2[MAX_LINE+1];
+        char * t1;
+        char * t2;
+
+        getLine(line, sizeof(line), ifP);
+
+        if ((t1 = strchr(line, '"')) == NULL)
+            pm_error("D error scanning color table");
+        if ((t2 = strchr(t1 + 1, '"')) == NULL)
+            pm_error("E error scanning color table");
+        if (t2 - t1 - 1 != charsPerPixel)
+            pm_error("wrong number of chars per pixel in color table");
+        strncpy(str1, t1 + 1, t2 - t1 - 1);
+        str1[t2 - t1 - 1] = '\0';
+
+        if ((t1 = strchr(t2 + 1, '"')) == NULL)
+            pm_error("F error scanning color table");
+        if ((t2 = strchr(t1 + 1, '"')) == NULL)
+            pm_error("G error scanning color table");
+        strncpy(str2, t1 + 1, t2 - t1 - 1);
+        str2[t2 - t1 - 1] = '\0';
+
+        addToColorMap(colorNameHashP, str1, str2, false);
     }
+    *colorNameHashPP = colorNameHashP;
 }
 
 
+
 static void
-readXpm1Header(FILE * const stream, int * const widthP, int * const heightP, 
-               int * const chars_per_pixelP, int * const ncolorsP, 
-               pixel ** const colorsP, int ** const ptabP) {
+readXpm1Header(FILE *           const ifP,
+               unsigned int *   const widthP,
+               unsigned int *   const heightP, 
+               unsigned int *   const charsPerPixelP,
+               ColorNameHash ** const colorNameHashPP) {
 /*----------------------------------------------------------------------------
-  Read the header of the XPM file on stream 'stream'.  Assume the
+  Read the header of the XPM file on stream *ifP.  Assume the
   getLine() stream is presently positioned to the beginning of the
   file and it is a Version 1 XPM file.  Leave the stream positioned
   after the header.
   
   Return the information from the header the same as for readXpm3Header.
 -----------------------------------------------------------------------------*/
-    char line[MAX_LINE+1], str1[MAX_LINE+1], str2[MAX_LINE+1];
-    char *t1;
-    char *t2;
-    int format;
-    unsigned int v;
-    int i, j;
+    int format, v;
     bool processedStaticChar;  
         /* We have read up to and interpreted the "static char..." line */
+    char * t1;
+    unsigned int nColors;
+    bool gotPixel, gotNColors, gotWidth, gotHeight, gotFormat;
 
-    *widthP = *heightP = *ncolorsP = *chars_per_pixelP = format = -1;
+    gotNColors = false;
+    gotWidth   = false;
+    gotHeight  = false;
+    gotFormat  = false;
+    gotPixel   = false;
 
     /* Read the initial defines. */
     processedStaticChar = FALSE;
     while (!processedStaticChar) {
-        getLine(line, sizeof(line), stream);
+        char line[MAX_LINE+1];
+        char str1[MAX_LINE+1];
+
+        getLine(line, sizeof(line), ifP);
 
         if (sscanf(line, "#define %s %d", str1, &v) == 2) {
-            char *t1;
             if ((t1 = strrchr(str1, '_')) == NULL)
                 t1 = str1;
             else
                 ++t1;
-            if (streq(t1, "format"))
+            if (streq(t1, "format")) {
+                gotFormat = true;
                 format = v;
-            else if (streq(t1, "width"))
+            } else if (streq(t1, "width")) {
+                gotWidth = true;
                 *widthP = v;
-            else if (streq(t1, "height"))
+            } else if (streq(t1, "height")) {
+                gotHeight = true;
                 *heightP = v;
-            else if (streq(t1, "ncolors"))
-                *ncolorsP = v;
-            else if (streq(t1, "pixel"))
-                *chars_per_pixelP = v;
-        } else if (!strncmp(line, "static char", 11)) {
+            } else if (streq(t1, "nColors")) {
+                gotNColors = true;
+                nColors = v;
+            } else if (streq(t1, "pixel")) {
+                gotPixel = TRUE;
+                *charsPerPixelP = v;
+            }
+        } else if (strneq(line, "static char", 11)) {
             if ((t1 = strrchr(line, '_')) == NULL)
                 t1 = line;
             else
@@ -507,85 +780,40 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP,
        t1 points to position of last "_" in the line, or the beginning of
        the line if there is no "_"
     */
-    if (format == -1)
+    if (!gotPixel)
+        pm_error("No 'pixel' value (characters per pixel)");
+    if (!gotFormat)
         pm_error("missing or invalid format");
     if (format != 1)
         pm_error("can't handle XPM version %d", format);
-    if (*widthP == -1)
+    if (!gotWidth)
         pm_error("missing or invalid width");
-    if (*heightP == -1)
+    if (!gotHeight)
         pm_error("missing or invalid height");
-    if (*ncolorsP == -1)
-        pm_error("missing or invalid ncolors");
-    if (*chars_per_pixelP == -1)
-        pm_error("missing or invalid *chars_per_pixelP");
-    if (*chars_per_pixelP > 2)
-        pm_message("WARNING: *chars_per_pixelP > 2 uses a lot of memory");
+    if (!gotNColors)
+        pm_error("missing or invalid nColors");
+
+    if (*charsPerPixelP > 2)
+        pm_message("WARNING: > 2 characters per pixel uses a lot of memory");
 
     /* If there's a monochrome color table, skip it. */
-    if (!strncmp(t1, "mono", 4)) {
+    if (strneq(t1, "mono", 4)) {
         for (;;) {
-            getLine(line, sizeof(line), stream);
-            if (!strncmp(line, "static char", 11))
+            char line[MAX_LINE+1];
+            getLine(line, sizeof(line), ifP);
+            if (strneq(line, "static char", 11))
                 break;
         }
     }
-    /* Allocate space for color table. */
-    if (*chars_per_pixelP <= 2) {
-        /* Up to two chars per pixel, we can use an indexed table. */
-        v = 1;
-        for (i = 0; i < *chars_per_pixelP; ++i)
-            v *= 256;
-        *colorsP = ppm_allocrow(v);
-        *ptabP = NULL;
-    } else {
-        /* Over two chars per pixel, we fall back on linear search. */
-        *colorsP = ppm_allocrow(*ncolorsP);
-        MALLOCARRAY(*ptabP, *ncolorsP);
-        if (*ptabP == NULL)
-            pm_error("Unable to allocate memory for %d colors", *ncolorsP);
-    }
-
-    /* Read color table. */
-    for (i = 0; i < *ncolorsP; ++i) {
-        getLine(line, sizeof(line), stream);
+    readV1ColorTable(ifP, colorNameHashPP, nColors, *charsPerPixelP);
 
-        if ((t1 = strchr(line, '"')) == NULL)
-            pm_error("D error scanning color table");
-        if ((t2 = strchr(t1 + 1, '"')) == NULL)
-            pm_error("E error scanning color table");
-        if (t2 - t1 - 1 != *chars_per_pixelP)
-            pm_error("wrong number of chars per pixel in color table");
-        strncpy(str1, t1 + 1, t2 - t1 - 1);
-        str1[t2 - t1 - 1] = '\0';
-
-        if ((t1 = strchr(t2 + 1, '"')) == NULL)
-            pm_error("F error scanning color table");
-        if ((t2 = strchr(t1 + 1, '"')) == NULL)
-            pm_error("G error scanning color table");
-        strncpy(str2, t1 + 1, t2 - t1 - 1);
-        str2[t2 - t1 - 1] = '\0';
-
-        v = 0;
-        for (j = 0; j < *chars_per_pixelP; ++j)
-            v = (v << 8) + str1[j];
-        if (*chars_per_pixelP <= 2)
-            /* Index into table. */
-            (*colorsP)[v] = ppm_parsecolor(str2,
-                                           (pixval) PPM_MAXMAXVAL);
-        else {
-            /* Set up linear search table. */
-            (*colorsP)[i] = ppm_parsecolor(str2,
-                                           (pixval) PPM_MAXMAXVAL);
-            (*ptabP)[i] = v;
-        }
-    }
     /* Position to first line of raster (which is the line after
        "static char ...").
     */
     for (;;) {
-        getLine(line, sizeof(line), stream);
-        if (strncmp(line, "static char", 11) == 0)
+        char line[MAX_LINE+1];
+        getLine(line, sizeof(line), ifP);
+        if (strneq(line, "static char", 11))
             break;
     }
 }
@@ -593,29 +821,49 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP,
 
 
 static void
-interpretXpmLine(char   const line[],
-                 int    const chars_per_pixel,
-                 int    const ncolors,
-                 int *  const ptab, 
-                 int ** const cursorP,
-                 int *  const maxCursor) {
+validateRasterPixel(const char * const pixelChars,
+                    unsigned int const charsPerPixel) {
+
+    unsigned int i;
+
+    for (i = 0; i < charsPerPixel; ++i) {
+        if (pixelChars[i] == '\0')
+            pm_error("XPM input file ends in the middle of a string "
+                     "that represents a raster line");
+        else if (pixelChars[i] == '"')
+            pm_error("A string that represents a raster line in the "
+                     "XPM input file is too short to contain all the "
+                     "pixels (%u characters each)",
+                     charsPerPixel);
+    }
+}
+
+
+
+static void
+convertRow(char                  const line[],
+           unsigned int          const width,
+           unsigned int          const charsPerPixel,
+           const ColorNameHash * const colorNameHashP,
+           pixel *               const pixrow,
+           bit *                 const alpharow) {
 /*----------------------------------------------------------------------------
-   Interpret one line of XPM input.  The line is in 'line', and its
-   format is 'chars_per_pixel' characters per pixel.  'ptab' is the
-   color table that applies to the line, which table has 'ncolors'
-   colors.
+   Convert one row from XPM input, which describes one raster line of the
+   image, to PPM.  The XPM line is in 'line', and its format is 'width' pixel,
+   'charsPerPixel' characters per pixel.  *colorNameHashP is the color table
+   that applies to the line.
+
+   Put the PPM pixels in 'pixrow'.
 
-   Put the colormap indexes for the pixels represented in 'line' at
-   *cursorP, lined up in the order they are in 'line', and return
-   *cursorP positioned just after the last one.
+   Also produce PBM row 'alpharow' with the transparency information from the
+   row.
 
    If the line doesn't start with a quote (e.g. it is empty), we issue
    a warning and just treat the line as one that describes no pixels.
 
-   Stop processing the line either at the end of the line or when
-   the output would go beyond maxCursor, whichever comes first.
+   Abort program if there aren't exactly 'width' pixels in the line.
 -----------------------------------------------------------------------------*/
-    char * lineCursor;
+    const char * lineCursor;
 
     lineCursor = strchr(line, '"');  /* position to 1st quote in line */
     if (lineCursor == NULL) {
@@ -626,209 +874,208 @@ interpretXpmLine(char   const line[],
                    "line which is supposed to be a line of raster data: "
                    "'%s'.  Ignoring this line.", line);
     } else {
+        unsigned int col;
+    
         ++lineCursor; /* Skip to first character after quote */
 
         /* Handle pixels until a close quote, eol, or we've returned all
            the pixels Caller wants.
         */
-        while (*lineCursor && *lineCursor != '"' && *cursorP <= maxCursor) {
-            int colorNumber;
-            int i;
-            colorNumber = 0;  /* initial value */
-            for (i = 0; i < chars_per_pixel; ++i)
-                colorNumber = (colorNumber << 8) + *(lineCursor++);
-            if (ptab == NULL)
-                /* colormap is indexed directly by XPM color number */
-                *(*cursorP)++ = colorNumber;
-            else {
-                /* colormap shadows ptab[].  Find this color # in ptab[] */
-                int i;
-                for (i = 0; i < ncolors && ptab[i] != colorNumber; ++i);
-                if (i < ncolors)
-                    *(*cursorP)++ = i;
-                else
-                    pm_error("Color number %d is in raster, but not in "
-                             "colormap.  Line it's in: '%s'",
-                             colorNumber, line);
-            }
+        for (col = 0; col < width; ++col) {
+
+            validateRasterPixel(lineCursor, charsPerPixel);
+
+            pixrow[col] = hash_color(colorNameHashP, lineCursor);
+
+            alpharow[col] = hash_isTransparent(colorNameHashP, lineCursor) ?
+                PBM_BLACK : PBM_WHITE;
+            
+            lineCursor += charsPerPixel;
         }
+        if (*lineCursor != '"')
+            pm_error("A raster line continues past width of image");
     }
 }
 
 
 
 static void
-ReadXPMFile(FILE * const stream, int * const widthP, int * const heightP, 
-            pixel ** const colorsP, int ** const dataP, 
-            int * const transparentP) {
+convertRaster(FILE *                const ifP,
+              unsigned int          const cols,
+              unsigned int          const rows, 
+              unsigned int          const charsPerPixel,
+              const ColorNameHash * const colorNameHashP,
+              FILE *                const imageOutFileP,
+              FILE *                const alphaOutFileP) {
 /*----------------------------------------------------------------------------
-   Read the XPM file from stream 'stream'.
+  Read the XPM raster from *ifP and write the PPM raster to *imageOutFileP
+  and the alpha channel to *alphaOutFileP (where those are, respectively,
+  non-null).
 
-   Return the dimensions of the image as *widthP and *heightP.
-   Return the color map as *colorsP, which is an array of *ncolorsP
-   colors.
+  The dimensions are 'cols' by 'rows' and the color map for the XPM
+  raster is *colorNameHashP.
+-----------------------------------------------------------------------------*/
+    char line[MAX_LINE+1];
+    pixel * pixrow;
+    bit * alpharow;
+    unsigned int row;
 
-   Return the raster in newly malloced storage, an array of *widthP by
-   *heightP integers, each of which is an index into the colormap
-   *colorsP (and therefore less than *ncolorsP).  Return the address
-   of the array as *dataP.
+    pixrow   = ppm_allocrow(cols);
+    alpharow = pbm_allocrow(cols);
 
-   In the colormap, put black for the transparent color, if the XPM 
-   image contains one.
------------------------------------------------------------------------------*/
-    char line[MAX_LINE+1], str1[MAX_LINE+1];
-    int totalpixels;
-    int *cursor;  /* cursor into *dataP */
-    int *maxcursor;  /* value of above cursor for last pixel in image */
-    int *ptab;   /* colormap - malloc'ed */
-    int rc;
-    int ncolors;
-    int chars_per_pixel;
+    for (row = 0; row < rows; ++row) {
+        bool haveLine;
 
-    backup = FALSE;
+        for (haveLine = false; !haveLine; ) {
+            getLine(line, sizeof(line), ifP); 
 
-    /* Read the header line */
-    getLine(line, sizeof(line), stream);
-    backup = TRUE;  /* back up so next read reads this line again */
-    
-    rc = sscanf(line, "/* %s */", str1);
-    if (rc == 1 && strncmp(str1, "XPM", 3) == 0) {
-        /* It's an XPM version 3 file */
-        readXpm3Header(stream, widthP, heightP, &chars_per_pixel,
-                       &ncolors, colorsP, &ptab, transparentP);
-    } else {				/* try as an XPM version 1 file */
-        /* Assume it's an XPM version 1 file */
-        readXpm1Header(stream, widthP, heightP, &chars_per_pixel, 
-                       &ncolors, colorsP, &ptab);
-        *transparentP = -1;  /* No transparency in version 1 */
-    }
-    totalpixels = *widthP * *heightP;
-    MALLOCARRAY(*dataP, totalpixels);
-    if (*dataP == NULL)
-        pm_error("Could not get %d bytes of memory for image", totalpixels);
-    cursor = *dataP;
-    maxcursor = *dataP + totalpixels - 1;
-	getLine(line, sizeof(line), stream); 
-        /* read next line (first line may not always start with comment) */
-    while (cursor <= maxcursor) {
-        if (strncmp(line, "/*", 2) == 0) {
-            /* It's a comment.  Ignore it. */
-        } else {
-            interpretXpmLine(line, chars_per_pixel, 
-                             ncolors, ptab, &cursor, maxcursor);
+            if (strneq(line, "/*", 2)) {
+                /* It's a comment.  Ignore it. */
+            } else
+                haveLine = true;
         }
-        if (cursor <= maxcursor)
-            getLine(line, sizeof(line), stream);
+        convertRow(line, cols, charsPerPixel, colorNameHashP,
+                   pixrow, alpharow);
+
+        if (imageOutFileP)
+            ppm_writeppmrow(imageOutFileP, 
+                            pixrow, cols, PPM_MAXMAXVAL, 0);
+            if (alphaOutFileP)
+                pbm_writepbmrow(alphaOutFileP, alpharow, cols, 0);
     }
-    if (ptab) free(ptab);
+
+    pbm_freerow(alpharow);
+    ppm_freerow(pixrow);
 }
  
 
 
 static void
-writeOutput(FILE * const imageout_file,
-            FILE * const alpha_file,
-            int const cols, int const rows, 
-            pixel * const colors, int * const data,
-            int transparent) {
+readXpmHeader(FILE *           const ifP,
+              unsigned int *   const widthP,
+              unsigned int *   const heightP, 
+              unsigned int *   const charsPerPixelP,
+              ColorNameHash ** const colorNameHashPP) {
 /*----------------------------------------------------------------------------
-   Write the image in 'data' to open PPM file stream 'imageout_file',
-   and the alpha mask for it to open PBM file stream 'alpha_file',
-   except if either is NULL, skip it.
+  Read the XPM header, including color map.
 
-   'data' is an array of cols * rows integers, each one being an index
-   into the colormap 'colors'.
-
-   Where the index 'transparent' occurs in 'data', the pixel is supposed
-   to be transparent.  If 'transparent' < 0, no pixels are transparent.
+  In the colormap, put black for the transparent color, if the XPM image
+  contains one.
 -----------------------------------------------------------------------------*/
-    int row;
-    pixel *pixrow;
-    bit * alpharow;
-
-    if (imageout_file)
-        ppm_writeppminit(imageout_file, cols, rows, PPM_MAXMAXVAL, 0);
-    if (alpha_file)
-        pbm_writepbminit(alpha_file, cols, rows, 0);
-
-    pixrow = ppm_allocrow(cols);
-    alpharow = pbm_allocrow(cols);
+    char line[MAX_LINE+1];
+    char str1[MAX_LINE+1];
+    int rc;
+    unsigned int charsPerPixel;
+    unsigned int width, height;
 
-    for (row = 0; row < rows; ++row ) {
-        int col;
-        int * const datarow = data+(row*cols);
+    backup = FALSE;
 
-        for (col = 0; col < cols; ++col) {
-            pixrow[col] = colors[datarow[col]];
-            if (datarow[col] == transparent)
-                alpharow[col] = PBM_BLACK;
-            else
-                alpharow[col] = PBM_WHITE;
-        }
-        if (imageout_file)
-            ppm_writeppmrow(imageout_file, 
-                            pixrow, cols, (pixval) PPM_MAXMAXVAL, 0);
-        if (alpha_file)
-            pbm_writepbmrow(alpha_file, alpharow, cols, 0);
+    /* Read the header line */
+    getLine(line, sizeof(line), ifP);
+    backup = TRUE;  /* back up so next read reads this line again */
+    
+    rc = sscanf(line, "/* %s */", str1);
+    if (rc == 1 && strneq(str1, "XPM", 3)) {
+        /* It's an XPM version 3 file */
+        readXpm3Header(ifP, &width, &height, &charsPerPixel, colorNameHashPP);
+    } else {
+        /* Assume it's an XPM version 1 file */
+        readXpm1Header(ifP, &width, &height, &charsPerPixel, colorNameHashPP);
     }
-    ppm_freerow(pixrow);
-    pbm_freerow(alpharow);
-
-    if (imageout_file)
-        pm_close(imageout_file);
-    if (alpha_file)
-        pm_close(alpha_file);
-}    
-
+    *widthP         = width;
+    *heightP        = height;
+    *charsPerPixelP = charsPerPixel;
+}
+ 
 
 
 int
 main(int argc, char *argv[]) {
 
-    FILE *ifp;
-    FILE *alpha_file, *imageout_file;
-    pixel *colormap;
-    int cols, rows;
-    int transparent;  /* value of 'data' that means transparent */
-    int *data;
-        /* The image as an array of width * height integers, each one
-           being an index int colormap[].
-        */
+    FILE * ifP;
+    FILE * alphaOutFileP;
+    FILE * imageOutFileP;
+    unsigned int cols, rows;
+    unsigned int charsPerPixel;
+    ColorNameHash * colorNameHashP;
 
-    struct cmdline_info cmdline;
+    struct cmdlineInfo cmdline;
 
     ppm_init(&argc, argv);
 
-    parse_command_line(argc, argv, &cmdline);
+    parseCommandLine(argc, argv, &cmdline);
 
     verbose = cmdline.verbose;
 
     if ( cmdline.input_filespec != NULL ) 
-        ifp = pm_openr( cmdline.input_filespec);
+        ifP = pm_openr( cmdline.input_filespec);
     else
-        ifp = stdin;
+        ifP = stdin;
 
     if (cmdline.alpha_stdout)
-        alpha_file = stdout;
+        alphaOutFileP = stdout;
     else if (cmdline.alpha_filename == NULL) 
-        alpha_file = NULL;
+        alphaOutFileP = NULL;
     else {
-        alpha_file = pm_openw(cmdline.alpha_filename);
+        alphaOutFileP = pm_openw(cmdline.alpha_filename);
     }
 
     if (cmdline.alpha_stdout) 
-        imageout_file = NULL;
+        imageOutFileP = NULL;
     else
-        imageout_file = stdout;
+        imageOutFileP = stdout;
 
-    ReadXPMFile(ifp, &cols, &rows, &colormap, &data, &transparent);
-    
-    pm_close(ifp);
+    readXpmHeader(ifP, &cols, &rows, &charsPerPixel, &colorNameHashP);
 
-    writeOutput(imageout_file, alpha_file, cols, rows, colormap, data,
-                transparent);
+    if (imageOutFileP)
+        ppm_writeppminit(imageOutFileP, cols, rows, PPM_MAXMAXVAL, 0);
+    if (alphaOutFileP)
+        pbm_writepbminit(alphaOutFileP, cols, rows, 0);
 
-    free(colormap);
+
+    convertRaster(ifP, cols, rows, charsPerPixel, colorNameHashP,
+                  imageOutFileP, alphaOutFileP);
+    
+    pm_close(ifP);
+    if (imageOutFileP)
+        pm_close(imageOutFileP);
+    if (alphaOutFileP)
+        pm_close(alphaOutFileP);
+
+    hash_destroy(colorNameHashP);
     
     return 0;
 }
+
+
+
+/*
+**
+** Copyright (C) 1991 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.
+**
+** Upgraded to handle XPM version 3 by
+**   Arnaud Le Hors (lehors@mirsa.inria.fr)
+**   Tue Apr 9 1991
+**
+** Rainer Sinkwitz sinkwitz@ifi.unizh.ch - 21 Nov 91:
+**  - Bug fix, no advance of read ptr, would not read 
+**    colors like "ac c black" because it would find 
+**    the "c" of "ac" and then had problems with "c"
+**    as color.
+**    
+**  - Now understands multiword X11 color names
+**  
+**  - Now reads multiple color keys. Takes the color
+**    of the hightest available key. Lines no longer need
+**    to begin with key 'c'.
+**    
+**  - expanded line buffer to from 500 to 2048 for bigger files
+*/
+
diff --git a/converter/ppm/yuvsplittoppm.c b/converter/ppm/yuvsplittoppm.c
index b3088812..5343a21e 100644
--- a/converter/ppm/yuvsplittoppm.c
+++ b/converter/ppm/yuvsplittoppm.c
@@ -57,7 +57,7 @@ parseCommandLine(int                 argc,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc(100*sizeof(optEntry));
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -71,7 +71,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);
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
@@ -196,9 +196,9 @@ main(int argc, char **argv) {
 
     parseCommandLine(argc, argv, &cmdline);
         
-    asprintfN(&ufname, "%s.U", cmdline.filenameBase);
-    asprintfN(&vfname, "%s.V", cmdline.filenameBase);
-    asprintfN(&yfname, "%s.Y", cmdline.filenameBase);
+    pm_asprintf(&ufname, "%s.U", cmdline.filenameBase);
+    pm_asprintf(&vfname, "%s.V", cmdline.filenameBase);
+    pm_asprintf(&yfname, "%s.Y", cmdline.filenameBase);
 
     uf = pm_openr(ufname);
     vf = pm_openr(vfname);
@@ -241,9 +241,9 @@ main(int argc, char **argv) {
     }
     pm_close(stdout);
 
-    strfree(yfname);
-    strfree(vfname);
-    strfree(ufname);
+    pm_strfree(yfname);
+    pm_strfree(vfname);
+    pm_strfree(ufname);
 
     pm_close(yf);
     pm_close(uf);
diff --git a/converter/ppm/yuvtoppm.c b/converter/ppm/yuvtoppm.c
index 2b44c65f..151ff9f9 100644
--- a/converter/ppm/yuvtoppm.c
+++ b/converter/ppm/yuvtoppm.c
@@ -20,96 +20,201 @@
 ** implied warranty.
 */
 
-#include "ppm.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "ppm.h"
 
-/* x must be signed for the following to work correctly */
-#define limit(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16)
 
-int
-main(argc, argv)
-	char          **argv;
-{
-	FILE           *ifp;
-	pixel          *pixrow;
-	int             argn, rows, cols, row;
-	const char     * const usage = "<width> <height> [yuvfile]";
-    struct yuv {
-        /* This is an element of a YUV file.  It describes two 
-           side-by-side pixels.
-        */
-        unsigned char u;
-        unsigned char y1;
-        unsigned char v;
-        unsigned char y2;
-    } *yuvbuf;
-
-	ppm_init(&argc, argv);
-
-	argn = 1;
-
-	if (argn + 2 > argc)
-		pm_usage(usage);
-
-	cols = atoi(argv[argn++]);
-	rows = atoi(argv[argn++]);
-	if (cols <= 0 || rows <= 0)
-		pm_usage(usage);
-
-	if (argn < argc) {
-		ifp = pm_openr(argv[argn]);
-		++argn;
-	} else
-		ifp = stdin;
-
-	if (argn != argc)
-		pm_usage(usage);
-
-    if (cols % 2 != 0) {
-        pm_error("Number of columns (%d) is odd.  A YUV image must have an "
-                 "even number of columns.", cols);
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    unsigned int cols;
+    unsigned int rows;
+    const char * inputFileName;  /* Name of input file */
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
+
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options */
+    optStruct3 opt;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    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 */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 < 2)
+        pm_error("You need at least two arguments: width and height in "
+                 "pixels.  You specified %u", argc-1);
+    else {
+        int const widthArg  = atoi(argv[1]);
+        int const heightArg = atoi(argv[2]);
+
+        if (widthArg < 0)
+            pm_error("Negative number for width: %d", widthArg);
+        if (heightArg < 0)
+            pm_error("Negative number for height: %d", heightArg);
+
+        cmdlineP->cols = widthArg;
+        cmdlineP->rows = heightArg;
+                 
+        if (cmdlineP->cols % 2 != 0)
+            pm_error("Number of columns (%u) is odd.  "
+                     "A YUV image must have an "
+                     "even number of columns.", cmdlineP->cols);
+
+        if (argc-1 < 3)
+            cmdlineP->inputFileName = "-";
+        else {
+            cmdlineP->inputFileName = argv[3];
+
+            if (argc-1 > 3)
+                pm_error("Too many arguments: %u.  "
+                         "The only possible arguments are "
+                         "width, height, and input file name", argc-1);
+        }
     }
+}
 
-	ppm_writeppminit(stdout, cols, rows, (pixval) 255, 0);
-	pixrow = ppm_allocrow(cols);
-    MALLOCARRAY(yuvbuf, (cols+1)/2);
-    if (yuvbuf == NULL)
-        pm_error("Unable to allocate YUV buffer for %d columns.", cols);
 
-	for (row = 0; row < rows; ++row) {
-		int    col;
 
-		fread(yuvbuf, cols * 2, 1, ifp);
+static int
+limit(int const x) {
 
-		for (col = 0; col < cols; col += 2) {
-            /* Produce two pixels in pixrow[] */
-            int y1, u, v, y2, r, g, b;
+    if (x > 0xffffff)
+        return 0xff;
+    else if (x <= 0xffff)
+        return 0;
+    else
+        return ((x >> 16) & 0xff);
+}
+
+
+
+static int
+nonneg(int const x) {
+/*----------------------------------------------------------------------------
+  Raise 'x' to 0 if negative
+-----------------------------------------------------------------------------*/
+    return x < 0 ? 0 : x;
+}
+
+
+
+struct Yuv {
+/*----------------------------------------------------------------------------
+  This is an element of a YUV file.  It describes two side-by-side pixels.
+
+  This is the actual layout of the data in the file (4 bytes).
+-----------------------------------------------------------------------------*/
+    unsigned char u;
+    unsigned char y1;
+    unsigned char v;
+    unsigned char y2;
+};
 
-			u = yuvbuf[col/2].u-128;
 
-            y1 = yuvbuf[col/2].y1 - 16;
-			if (y1 < 0) y1 = 0;
 
-            v = yuvbuf[col/2].v - 128;
+static void
+readYuv(FILE *       const ifP,
+        struct Yuv * const yuvP) {
 
-            y2 = yuvbuf[col/2].y2 - 16;
-			if (y2 < 0) y2 = 0;
+    size_t readCt;
 
-			r = 104635 * v;
-			g = -25690 * u + -53294 * v;
-			b = 132278 * u;
+    readCt = fread(yuvP, sizeof(*yuvP), 1, ifP);
 
-			y1*=76310; y2*=76310;
+    if (readCt != 1) {
+        if (feof(ifP))
+            pm_error("Premature end of input.");
+        else
+            pm_error("Error reading input.");
+    }
+}
+
+
+
+static void
+yuvtoppm(FILE *       const ifP,
+         unsigned int const cols,
+         unsigned int const rows,
+         FILE *       const ofP) {
+
+    pixval const maxval = 255;
+
+    pixel * pixrow;
+    unsigned int row;
+
+    ppm_writeppminit(ofP, cols, rows, maxval, 0);
+
+    pixrow = ppm_allocrow(cols);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; col += 2) {
+            /* Produce two pixels in pixrow[] */
+            struct Yuv yuv;
+            int     y1, u, v, y2, r, g, b;
+
+            readYuv(ifP, &yuv);
+
+            u = yuv.u - 128;
+            y1 = nonneg (yuv.y1 - 16);
+            v = yuv.v - 128;
+            y2 = nonneg (yuv.y2 - 16);
+
+            r = 104635 * v;
+            g = -25690 * u + -53294 * v;
+            b = 132278 * u;
+
+            y1 *= 76310;
+            y2 *= 76310;
+
+            PPM_ASSIGN(pixrow[col],
+                       limit(r + y1), limit(g + y1), limit(b + y1));
+            PPM_ASSIGN(pixrow[col + 1],
+                       limit(r + y2), limit(g + y2), limit(b + y2));
+        }
+        ppm_writeppmrow(ofP, pixrow, cols, maxval, 0);
+    }
 
-			PPM_ASSIGN(pixrow[col], limit(r+y1), limit(g+y1), limit(b+y1));
-			PPM_ASSIGN(pixrow[col+1], limit(r+y2), limit(g+y2), limit(b+y2));
-		}
-		ppm_writeppmrow(stdout, pixrow, cols, (pixval) 255, 0);
-	}
-    free(yuvbuf);
     ppm_freerow(pixrow);
-	pm_close(ifp);
-	pm_close(stdout);
 
-	exit(0);
+    if (fgetc(ifP) != EOF)
+        pm_message("Extraneous data at end of image.");
+}
+
+
+
+int
+main (int argc, const char ** argv) {
+
+    FILE * ifP;
+    struct CmdlineInfo cmdline;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    yuvtoppm(ifP, cmdline.cols, cmdline.rows, stdout);
+    
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
 }
diff --git a/doc/CONTRIBUTORS b/doc/CONTRIBUTORS
new file mode 100644
index 00000000..d44cfe79
--- /dev/null
+++ b/doc/CONTRIBUTORS
@@ -0,0 +1,37 @@
+As an open source project, Netpbm has been written and otherwise supported by
+myriad people from all over.  Most of them have made isolated contributions
+such as a single program, a single new feature, or a single bug fix.  To save
+clerical effort, we do not list them here and instead refer you to the HISTORY
+file.  This file lists people who have made broad contributions that wouldn't
+be well summarized by the HISTORY file.
+
+Jef Poskanzer had the original idea for what is now Netpbm and wrote dozens of
+programs and the original library code, as well as inventing and designing the
+PNM formats and writing documentation.  Jef's code contributions stopped in
+1991 and because of rewrites, there is not much of Jef's code still remaining,
+with what does remain being mostly obsolete.  Nonetheless, the impact of that
+original code is enormous, as everything in Netpbm today is derived in some
+way from Jef's work.
+
+Bryan Henderson has been maintaining Netpbm since 1999 and has written more
+code, in lines, than anyone else.  Most of that is just rewriting old code in
+a more maintainable style.  But Bryan has also fixed more bugs than anyone,
+since the normal bug reporting procedure is to tell the maintainer, and Bryan
+normally fixes bugs reported that way himself.  Bryan has also added many
+features and whole programs at the suggestion of users.  Bryan has written
+most of the documentation, whether by writing manuals for programs whose
+authors declined to do so or by rewriting manuals to make them more clear when
+users had trouble using Netpbm.
+
+Akira F Urushibata has made myriad contributions since 2004.  Much of the PBM
+processing code is his, as he rewrote existing programs to be much faster, in
+particular by using special bit-processing machine instructions.  He also
+created the entire test framework and regression test suite and runs those
+regression tests and other tests regularly, and often develops fixes for the
+bugs found.  Nobody else has done significant alpha testing of Netpbm except
+where developers have done it to test code they just wrote.  This testing has
+turned up many bugs in code going all the way back to 1989.
+
+Scott Pakin has contributed numerous programs over the years including the
+fairly significant work 'pamstereogram'.
+
diff --git a/doc/COPYRIGHT.PATENT b/doc/COPYRIGHT.PATENT
index fe3c242a..24b1583a 100644
--- a/doc/COPYRIGHT.PATENT
+++ b/doc/COPYRIGHT.PATENT
@@ -34,92 +34,7 @@ all the above to be modified by "to the best of the Netpbm
 maintainer's knowledge."
 
 
-
-PATENTS
--------
-
-These are the patents the Netpbm maintainer knows about that relate to
-Netpbm.  It is basically just information the maintainer has stumbled
-over at some point -- no search has been done.
-
-No licenses have been granted by patent owners to the maintainer of
-Netpbm.  Therefore, if you need a patent to use something in Netpbm,
-you need your own license.
-
-A note about patents in general: A patent gives an inventor the
-exclusive right to make, sell, or use the invention.  If you
-independently invent something without knowing that the patent holder
-already did, that makes no difference -- the patent holder still has
-the exclusive right.  It makes no difference if you give the original
-inventor credit.  The patent applies to a method, not its expression,
-so writing a program from scratch to implement a certain method is
-still a patent infringement.  Infringing a patent is not a crime per
-se, but to the extent that it costs the patent holder money, the
-infringer has to make it up.
-
-The original purpose of patents is probably perverted when patents are
-applied to things you implement in computer programs.  This is one of
-the Free Software Foundation's causes.  See 
-<http://www.gnu.org/philosophy.html#laws>.
-
-
-Unisys owns patents on LZW compression, which is used by Ppmtogif, and
-maybe on LZW decompression, which is used by Giftopnm.  IBM also owns
-a patent that may cover the GIF tools.  Unisys offers a license of the
-patent for trivial use for $5000.  Its U.S. patent (Number 4,558,302)
-EXPIRED June 20, 2003.  In most of Europe, the patent expires June 18,
-2004.  In Japan, it's June 20, 2004 and in Canada, July 7, 2004.
-IBM's U.S. patent expirs August 11, 2006.
-
-Neither company has ever enforced the patent against trivial users of
-it.  <http://news.cnet.com/news/0-1005-200-1713278.html> is an article
-dated April 18, 2000 on the issue.
-http://www.unisys.com/about__unisys/lzw/> is Unisys' view of the
-matter.  For information from another perspective, see
-<http://burnallgifs.org>.
-
-The following Netpbm components may be restricted by these patents:
-Ppmtogif, Giftopnm.
-
-A good substitute for GIF if the patents are a problem is PNG (see
-pngtopnm, pnmtopng), which was developed with a primary purpose of not
-using any patented technology.
-
-You can also use the -nolzw option on ppmtogif to avoid using the LZW
-patent.  The images so generated are larger than traditional
-LZW-compressed GIFs, but any GIF decoder can decode them just the
-same.
-
-I repeat: The Unisys U.S. patent has expired.  It is not an issue
-for any future use of Netpbm.
-
-
-The Pnmtojpeg and possibly Jpegtopnm programs in some cases may use
-the arithmetic coding patents owned by IBM, AT&T, and Mitsubishi.
-There is difference of opinion on whether they do.
-
-Forgent owns a patent it believes covers JPEG compression.  This
-patent was virtually unknown before July 2002, when Forgent began to
-enforce it.  It has successfully enforced it against two companies
-(Sony and an unnamed Japanese digital camera maker), but without court
-ruling.  This patent, U.S. Patent No. 4,698,672, expires in 2006.
-
-Philips and Lucent Technologies also own patents they claim cover
-JPEG.
-
-The following Netpbm components may be restricted by these patents:
-Jpegtopnm, Pnmtojpeg, Ppmtompeg, Tifftopnm, Pnmtotiff.  These all
-do their JPEG work via a JPEG library not distributed with Netpbm.
-Your JPEG-related liability for using Netpbm is limited to your 
-liability for using your JPEG library.
-
-The next best alternative to JPEG is probably PNG and maybe JBIG for
-bilevel (black and white) images.
-
-http://burnalljpegs.org contains information on this issue.
-
-
-The Jbigtopnm and Pnmtojbig programs use arithmetic coding patents and
-other patents covering various aspects of the "front end."
-
-
+Netpbm may practice valid patents, which would mean that you owe someone
+royalties if you use the code.  This is a miniscule risk, though.
+What is known about patents related to Netpbm is in the file
+'doc/patent_summary' in the Netpbm source tree.
diff --git a/doc/HISTORY b/doc/HISTORY
index 8d44c6c6..5f91d170 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -4,7 +4,12 @@ Netpbm.
 CHANGE HISTORY 
 --------------
 
-19.06.28 BJH  Release 10.47.73
+19.06.28 BJH  Release 10.73.28
+
+              pbmtozinc: fix wrong output on big-endian machines.  Broken in
+              Netpbm 10.71 (June 2015).
+
+19.05.04 BJH  Release 10.73.27
 
               pnmtopng: Fix bug: Defaults to no filters.  Should be all
               filters.  Effect is larger PNG output.  Broken after Netpbm
@@ -15,7 +20,22 @@ CHANGE HISTORY
               Broken after Netpbm 10.26 (January 2005) but no later than
               Netpbm 10.35 (August 2006).
 
-19.03.30 BJH  Release 10.47.72
+19.03.10 BJH  Release 10.73.26
+
+              pstopnm: Fix bug: -textalphabits has no effect.  Always broken.
+              (-textalphabits was new in Netpbm 10.53 (December 2010)).
+
+              pamtopng: Fix sBit chunk, bit shift value for 1-, 2-, and 4-bit-
+              per-sample images.  Always broken (Pamtopng was new in Netpbm
+              10.71 (June 2015)).
+
+              pamtopng: Fix buffer overrun.  Always broken (Pamtopng was new
+              in Netpbm 10.71 (June 2015)).
+
+19.02.09 BJH  Release 10.73.25
+
+              pnmtopng: fix bug: -interlace ignored.  Broken in 10.55
+              (June 2011).
 
               pamstretch: Reject very large scale factors instead of producing
               incorrect output.
@@ -27,18 +47,25 @@ CHANGE HISTORY
               ppmdraw: Fix bug: 'setlinetype nodiag' says invalid type.
               Always broken.  (Ppmdraw was new in Netpbm 10.29 (August 2005)).
 
+18.12.17 BJH  Release 10.73.24
+
               bmptopnm: Fix wrong output for non-colormapped OS2 BMP.  Broken
               in Netpbm 10.18 (September 2003).
 
-18.12.05 BJH  Release 10.47.71
-
-              picttoppm: accept rectangle specifications in input that have
-              the corners in any order, not just upper left, then lower right.
+18.11.05 BJH  Release 10.73.23
 
               bmptopnm: Fix array bounds violation when index value in raster
               is too big.  Broken after Netpbm 10.10 (October 2002) but before
               10.19 (November 2003).
 
+              Merge build: fix so legacy program names 'pnmtopnm', 'ppmnorm',
+              and 'ppmtotga' work again.
+
+18.11.02 BJH  Release 10.73.22
+
+              picttoppm: accept rectangle specifications in input that have
+              the corners in any order, not just upper left, then lower right.
+
               libnetpbm: Fix invalid memory reference in color name processing
               when trivial memory allocation fails.
 
@@ -53,10 +80,7 @@ CHANGE HISTORY
               pnmtojbig: fix incorrect handling of -x option.  Always broken
               (pnmtojbig was new in Netpbm 9.2 (May 2000)).
 
-              Merge build: fix so legacy program names 'pnmtopnm', 'ppmnorm',
-              and 'ppmtotga' work again.
-
-18.09.29 BJH  Release 10.47.70
+18.09.29 BJH  Release 10.73.21
 
               pstopnm: Fix divide-by-zero crash when Postscript input says
               the image has zero size.
@@ -71,14 +95,20 @@ CHANGE HISTORY
               pbmmask: Fix invalid memory reference with zero-dimension
               input image.  Broken in primordial Netpbm, ca 1989.
 
-18.06.27 BJH  Release 10.47.69
+18.06.27 BJH  Release 10.73.20
+
+              Pngtopam: Fix bogus warning of non-square pixels when image does
+              not contain pixel resolution information.  Introduced in Netpbm
+              10.48 (September 2009)
+
+18.04.28 BJH  Release 10.73.19
 
               ilbmtoppm: Fix bug: may fail with bogus error message about an
               invalid CLUT chunk if image has a CLUT chunk.  Introduced after
               Netpbm 10.26 (January 2005) and at or before Netpbm 10.35
               (August 2006).
 
-18.03.25 BJH  Release 10.47.68
+18.02.17 BJH  Release 10.73.18
 
               g3topbm: Fix bug - produces invalid empty PBM image if input
               image is empty.
@@ -86,7 +116,10 @@ CHANGE HISTORY
               mrftopbm: Fix bug - wrong error messages or output when input
               invalidly short.
 
-17.12.30 BJH  Release 10.47.67
+17.10.28 BJH  Release 10.73.17
+
+              sldtoppm: -lib and -dir don't work - always says slide not
+              found.  Broken in Netpbm 10.63 (June 2013).
 
               sldtoppm: fix bug: says AutoCAD slide file isn't an AutoCAD
               slide file.  Broken after Netpbm 10.26 (January 2005), but no
@@ -95,20 +128,36 @@ CHANGE HISTORY
               sldtoppm: fix bug: wild memory accesses, weird messages when
               invalid input file has unterminated strings.
               
-17.09.28 BJH  Release 10.47.66
+17.09.28 BJH  Release 10.73.16
 
               ppmbrighten: fix bug: red pixels change hue.  Introduced in
               after Netpbm 10.11 (October 2002) and before Netpbm 10.18
               (September 2003).
 
+17.09.13 BJH  Release 10.73.15
+
               palmtopnm: fix crash if invalid input contains color index that
               is not in the palette.  Always broken (palmtopnm was new in
               Netpbm 9.10 (October 2001)).
 
+              pnmtopalm: fix incorrect output with certain input files and
+              -packbits_compression.  Always broken.  -packbits_compression
+              was new in Netpbm 10.27 (March 2005).
+
+              pamtopdbimg: Fix incorrect output. Always broken (pamtopdbimg
+              was new in Netpbm 10.52.00 (October 2010)).
+
+17.08.11 BJH  Release 10.73.14
+
               libnetpbm: font facilities: fix invalid memory reference with
               certain font files.
 
-17.06.28 BJH  Release 10.47.65
+17.06.30 BJH  Release 10.73.13
+
+              ppmcolormask: fix incorrect output when input maxval is not 255.
+              Always broken (ppmcolormask was new in Netpbm 9.0, April 2000).
+
+17.06.28 BJH  Release 10.73.12
 
               pamgauss: Fix skewed output with even dimension.  Always broken
               (Pamgauss was added in Netpbm 10.23 (July 2004).
@@ -117,11 +166,12 @@ CHANGE HISTORY
               number for width.  Always broken.  (bmptopnm was new, as
               bmptoppm, in original Netpbm, 1992).
 
-              pnmtojpeg: fix array bounds violation in argument list.  Always
-              broken (pnmtojpeg was new to Netpbm in Netpbm 8.2 (March 2000).
+17.05.27 BJH  Release 10.73.11
 
-              Build: correct link order in Jpeg2000 converter builds to
-              correct undefined symbol reference at link time.
+              pamcrater: fix incorrect output with non-square image.
+              Introduced in Netpbm 10.69 (December 2014).
+
+17.04.30 BJH  Release 10.73.10
 
               libnetpbm: fix bug: pm_system_XXX closes Standard Input if you
               supply a Standard Output accepter but not a Standard Input
@@ -138,20 +188,23 @@ CHANGE HISTORY
               exists (so don't require ppmdcfont to exist).  Broken around
               Netpbm 10.35 (2006).
 
-17.03.28 BJH  Release 10.47.64
+17.04.15 BJH  Release 10.73.09
 
-              tifftonm: Fix incorrect PBM output with two-color paletted TIFF
-              image.  Broken in primordial Netpbm, ca 1990.
+              pamcomp: fix incorrect output with -mixtransparency.
+              Always broken.  (-mixtransparency was new in Netpbm 10.56,
+              September 2011).
 
-              giftopnm: Fix buffer overflow/crash with invalid GIF input.
-              Broken since primordial Netpbm.
+              pamcomp: remove debug trace message with -mixtransparency.
+              Always broken.  (-mixtransparency was new in Netpbm 10.56,
+              September 2011).
 
-              bmptopnm: Fix buffer overflow/crash with negative height or
-              width in OS/2 BMP.  Broken since primordial Netpbm.
+              pnmtojpeg: fix array bounds violation in argument list.  Always
+              broken (pnmtojpeg was new to Netpbm in Netpbm 8.2 (March 2000).
 
-              tifftopnm: Fix memory corruption when image is more pixels
-              than can be represented as a C unsigned integer.  Broken in
-              Netpbm 10.11 (October 2002).
+17.03.28 BJH  Release 10.73.08
+
+              tifftonm: Fix incorrect PBM output with two-color paletted TIFF
+              image.  Broken in primordial Netpbm, ca 1990.
 
               tifftopnmcmyk: Default rows per strip to the TIFF library
               default instead of whatever yields 8K strips.
@@ -160,34 +213,58 @@ CHANGE HISTORY
               -rowsperstrip.  Always broken.  (Tifftopnmcmyk was new in Netpbm
               8.2 (March 2000).
 
+              libnetpbm: ppmd_fill_path: remove debug trace.  Always broken
+              (ppmd_fill_path was new in Netpbm 10.34 (June 2006).
+
+16.01.29 BJH  Release 10.73.07
+
+              tifftopnm: Fix memory corruption when image is more pixels
+              than can be represented as a C unsigned integer.  Broken in
+              Netpbm 10.11 (October 2002).
+
               svgtopam: Fix crash when out of memory.  Always broken
               (svgtopam was new in Netpbm 10.33 (March 2006)).
 
-              libnetpbm: ppmd_fill_path: remove debug trace.  Always broken
-              (ppmd_fill_path was new in Netpbm 10.34 (June 2006).
+16.12.25 BJH  Release 10.73.06
+
+              pbmtoascii: fix bogus assertion failure.  Introduced in 
+              Netpbm 10.51 (June 2010) and visible only with a custom
+              build with assertion checking.
+
+16.12.01 BJH  Release 10.73.05
+
+              pnmpad: fix bug: incorrect output width.  Introduced in
+              Netpbm 10.72 (July 2015).
 
-16.09.26 BJH  Release 10.47.63
+16.09.02 BJH  Release 10.73.05
 
-              Build: Add warning when libpng versions is later than 1.4, since
-              it is incompatible with this release of Netpbm.
+              pnmquantall: Fix failure when temporary file location is
+              not the same filesystem as the output file.
+
+              pnmquantall: Fix incorrect handling of when the Pnmremap or
+              the final rename fails.
+
+16.08.13 BJH  Release 10.73.04
 
               giftopnm: Fix bug: crash on little-endian computers that can't
-              tolerate unaligned memory access.  Thanks Ignatios Souvatzis
+              toleration unaligned memory access.  Thanks Ignatios Souvatzis
               (is@netbsd.org).  Broken in Netpbm 10.47 (June 2009).
 
-16.06.26 BJH  Release 10.47.62
+16.06.26 BJH  Release 10.73.03
 
               pamarith: fix incorrect output when maxvals differ, for
               -add, -multiply, -mean, -min, -max.  Broken in Netpbm 10.41
               (December 2007).
 
+16.06.12 BJH  Release 10.73.02
+
               pbmtextps: Abort with error instead of generating single space
               when user supplies no text.
 
               pbmtextps: Fix bug: input text or font name with Postscript
               control characters messes up the Postscript program.
 
-16.05.09 BJH  Release 10.47.61
+16.05.09 BJH  Release 10.73.01
 
               bmptopnm: fail properly with Version 4, 5 Windows BMP.
 
@@ -200,14 +277,13 @@ CHANGE HISTORY
               pbmreduce: fix undefined behavior when scale factor argument is
               too big.  Always present (pbmreduce was new in September 1989).
 
+              cameratopam: fix invalid memory reference; effect unknown.
+              Introduced in Netpbm 10.68 (September 2014).
+
               Install on Windows: fix backward compatibility symlinks for
               pnmtoplainpnm, pnmquantall.
 
-16.03.27 BJH  Release 10.47.60
-
-              (no changes - don't know why this release exists).
-
-15.12.25 BJH  Release 10.47.59
+15.12.26 BJH  Release 10.73.00
 
               anytopnm: use --mime-type option instead of --mime on newer
               'file' program (on which --mime has a new meaning so that
@@ -216,17 +292,45 @@ CHANGE HISTORY
               anytopnm: recognize "Netpbm PAM" non-mime output from 'file'
               program as indicating PAM.
 
-              pnmtops: fix bug: always things -level=N is -level=1.
+              pnmtops: fix bug: always thinks -level=N is -level=1.
               Introduced after Netpbm 10.26 (January 2005) but before 10.35
               (August 2006).  Manifests only with recent compilers.
 
               pnmpaste: fix bug with PBM: incorrect output.  Introduced in
               Netpbm 10.44 (September 2008).
 
+              Build: fix undefined reference to parse_printf_format etc.  on
+              system that doesn't have that facility.  Broken in Netpbm 10.69
+              (December 2014).
+
               Build: fix superfluous error message when USER environment
               variable contains whitespace.  Broken since primordial Netpbm.
 
-15.08.15 BJH  Release 10.47.58
+              Windows build: fix bug: make clean doesn't clean icon/netpbm.o.
+              Broken since Netpbm 10.67 (June 2014).
+
+              Test: improved pnmpaste test.
+
+15.09.26 BJH  Release 10.72.00
+
+              Add pamunlookup .
+
+              pamtopng: Implement -itxt .
+
+              pamlookup: Add -byplane .
+
+              pbmtoescp2: Add -stripeheight .
+
+              phmtoescp2: Add -resolution .
+
+              pbmtoescp2: Add -formfeed .
+
+              pbmtoescp2: Add -raw .
+
+              pbmtoescp2: Add -resolution=720 .
+
+              pbmtoescp2: Pad output horizontally to a multiple of 8 columns
+              and vertically to a whole stripe to prevent image loss.
 
               fitstopnm: Add message saying you're probably making a mistake
               if you have a 3-D image and don't specify -image.  The third
@@ -235,36 +339,59 @@ CHANGE HISTORY
               fitstopnm: Fix -min and -max.  Broken in Netpbm 10.39 (June
               2007).
 
-              Pnmtopclxl: fix wild memory access when out of memory.  Always
+              pnmtopclxl: fix buffer overrun causing unpredictable behavior.
+              (Introduced in Netpbm 10.54 (March 2011).
+
+              pnmtopclxl: fix wild memory access when out of memory.  Always
               broken (Pnmtopclxl was new in Netpbm 10.6 (July 2002)).
 
-              Pnmtopclxl: fix wild memory access with pathologically large and
+              pnmtopclxl: fix wild memory access with pathologically large and
               uncompressible image.  Always broken (Pnmtopclxl was new in
               Netpbm 10.6 (July 2002)).
 
-              Pnmtopclxl: fix silent output corruption when a file write
+              pnmtopclxl: fix silent output corruption when a file write
               fails.  Always broken (Pnmtopclxl was new in Netpbm 10.6 (July
               2002)).
 
+              escp2topbm: Fix -plain.  Always broken (escp2topbm was new in
+              Netpbm 10.18 (September 2003)).
+
+              pnmpad: Add -mheight, -mwidth.
+
               ppmtoilbm: Fix failure with -hamforce and -nocompression.
               Broken in Netpbm 9.12 (March 2001).
 
-              Build: fix compile error in sbigtopnm introduced in 10.47.57
+              makeman: fix Python syntax error.  Introduced in Netpbm 10.70
               (June 2015).
 
-              Build: fix link errors in pbmtomacp, ppmtoyuvsplit introduced
-              in 10.47.57 (June 2015).
+15.06.28 BJH  Release 10.71.00
 
-15.06.28 BJH  Release 10.47.57
+              Add pamtopng.  Thanks Willem vanSchaik (willem@schaik.com).
 
-              palmtopnm: Fix distorted output with PackBits compressed input.
-              Always broken (Ability to convert PackBits input was new in
-              Netpbm 10.27 (March 2005).
+              pnmtopng: Add -srgbintent.
+
+              pamstereogram: Add -xbegin.  Change default to render from
+              center outwards intead of from right to left, thus making the
+              center of the image the crispest part.  Thanks Scott Pakin
+              (scott@pakin.org).
+
+              pamstereogram: Allow -xshift and -yshift to be negative.  Thanks
+              Scott Pakin (scott@pakin.org).
+
+              pnmpsnr: Add -rgb.
+
+              ppmtoicr: remove -rle option.  Actually, it never worked
+              because of a bug.  Now it isn't expected to.  Ppmtoicr was
+              new in 1991.
 
               pnmtopalm: Fix arithmetic overflow with ridiculously large
               image.  Introduced after Netpbm 10.26 (January 2005) but before
               Netpbm 10.35 (August 2006).
 
+              palmtopnm: Fix distorted output with PackBits compressed input.
+              Always broken (Ability to convert PackBits input was new in
+              Netpbm 10.27 (March 2005).
+
               pbmtoepson: fix -protocol option - never works and sometimes
               crashes program.  Always broken (-protocol was new in Netpbm
               10.23 (July 2004).
@@ -279,6 +406,9 @@ CHANGE HISTORY
               sbigtopgm: fix detection of camera type.  Always broken
               (sbigtopgm was new in Netpbm 8.3 (March 2000)).
 
+              sbigtopgm: fix recognition of compressed image.  Broken in
+              Netpbm 10.70 (March 2015).
+
               pbmtogo: Fix bug: garbage first row.  Broken at least since
               November 1989.
 
@@ -288,21 +418,18 @@ CHANGE HISTORY
               pbmtoescp2: Fix bug: overrun on certain input.  Always broken
               (pbmtoescp2 was new in Netpbm 10.18 (September 2003)).
 
-              Build: improve text of pointer man pages.
-
               escp2topbm: Fix buffer overrun on certain input.  Always broken
               (escp2topbm was new in Netpbm 10.18 (September 2003)).
 
-15.05.24 BJH  Release 10.47.56
-
               libnetpbm: pm_stripeq: fix bug: wild pointer access when
               comparator is shorter than comparand.  Doesn't affect function,
               but could cause crash or privacy exposure.  Affects reading of a
               PAM file by any program.  Introduced in one of Netpbm 10.27
               (March 2005) through 10.35 (August 2006).
 
-              pbmtog3: Fix buffer overrun.  Introduced in Netpbm 10.23
-              (July 2004).
+              pnmconvol: Fix bug: wrong output for pixels that convolve to
+              negative values (should be clipped to zero).  Introduced in
+              Netpbm 10.68 (September 2014).
 
               pbmtog3: Fix buffer overrun.  Introduced in Netpbm 10.23
               (July 2004).
@@ -333,8 +460,13 @@ CHANGE HISTORY
               than 640 pixels.  Always broken (pbmtopi3 was new in September
               1991).
 
+              st4topgm: Fix bug: with no argument, uses file named "'" instead
+              of Standard Input.  Always present (st45topgm was new in Netpbm
+              10.70 (March 2015).
+
               pbmtomgr: Fix incorrect output when input is too large (must be
-              at most 4095 pixels high or wide).
+              at most 4095 pixels high or wide).  Always broken.  (pbmtomgr
+              was new in 1989).
 
               pbmtomacp: fix wild pointer dereference with -b larger than
               image height.  Always broken.  (pbmtomacp was new in X.V11R3
@@ -343,46 +475,140 @@ CHANGE HISTORY
               ppmtorgb3: Fix buffer overflow with long input file name.
               Always present.  (ppmtorgb3 was new in X.V11R4 (November 1989).
 
-              pbmtoatk: Fix crash with very long input file name argument.
-              Always broken (pbmtoatk was new in 1991).
+              ppmtoarbtxt: fix bug: wrong output when high numbers represent
+              darker.  Broken in Netpbm 10.69 (November 2014).
+
+              ppmtoarbtxt: better rounding in sample values.
+
+              libnetpbm: Remove bitio.h as an external interface.
+
+              test: replace some GNU-only code with more portable code that
+              works on OS X.  Thanks Ryan Schmidt <ryandesign@macports.org>.
+
+              makeman: deal properly with backlash in source.  Thanks Willem
+              van Schaik <willem@schaik.com>.  But something was wrong with
+              this change and it caused the program always to fail, so
+              we reversed this change in 10.72.
+
+              Build: don't build and install libjbig and libjasper if we
+              are using external versions of them instead.
+
+              Build: various cross-compile fixes, especially for MinGW.
+
+              Build: work around bug in GCC < 4.2 related to SSE2 builtins
+              that causes compile of pamflip to fail.
 
               Build: fix 'make package' where config.mk sets a subdirectory
               other than 'man' for the manual.
 
-15.03.29 BJH  Release 10.47.55
+              Build: improve pointer man page text.
 
-              pamtosvg: fix use of unset variable; probably results in a
-              crash.  Always present (pamtosvg was new in Netpbm 10.33 (March
-              2006).
+15.03.29 BJH  Release 10.70.00
+
+              Add st4topgm, pgmtost4.
+
+              Add pgmtosbig.  Mainly a test tool for sbigtopgm.
+
+              Add yuy2topam.  Thanks Michael Haardt.
+
+              tifftopnm: allow input file to be nonseekable.
+              Thanks Ludolf Holzheid <ludolf.holzheid@gmx.de>.
+
+              pnmhisteq: add -noblack and -nowhite.  Idea from Andrew Brooks
+              <arb@sat.dundee.ac.uk>.
+
+              pgmmorphconv: add -gradient.  Thanks Michael Haardt
+              <michael@moria.de>.
 
-15.01.25 BJH  Release 10.47.54
+              giftopnm: Fix bug: crashes if purported GIF has neither a global
+              color map nor a local one.
+
+              pgmmorphconv: fix bug: always produces PGM Plain format.  Always
+              present (progam was added to Netpbm in Release 10.0 (June 2002)).
+
+              pamtilt: fix bug: unconditional crash.  Broken in Netpbm 10.63
+              (June 2013).
 
               pnmgamma -srgbtobt709, -bt709tosrgb: fix bug; incorrect output
               nearly always.  Always broken (These options were new in
               Netpbm 10.32 (February 2006)).
 
+              pamtosvg: fix use of unset variable; probably results in a
+              crash.  Always present (pamtosvg was new in Netpbm 10.33 (March
+              2006)).
+
+              cameratopam: fix bug: variable used before set; unknown impact.
+              Introduced in Netpbm 10.66 (March 2014).
+
+              On Windows, don't leave temporary files around (previous code
+              did so because unlink of an open file fails in Windows; new
+              code deletes temporary files via atexit).  Thanks
+              Ludolf Holzheid <ludolf.holzheid@gmx.de>.
+
+              Libnetpbm: fix external header file pm.h so it does not include
+              internal header file pm_c_util.h.  Introduced in Netpbm
+              10.69 (December 2014).
+
               build: fix incompatible type compilation error in giftopnm.
-              Broken in Netpbm 10.38 (March 2007) (but obviously manifests
+              Introduced in Netpbm 10.38 (March 2007) (but obviously manifests
               only in recent build environments).
 
+              build: fix compile failure in wordint_access_be.h with Bigendian
+              target platforms.  Introduced in Netpbm 10.63 (June 2013).
+
+              build: fix compile failure in pbmtomacp, ppmtoacad, pgmabel:
+              TRUE redefined.  Introduced in Netpbm 10.69 (December 2014).
+
+14.12.25 BJH  Release 10.69.00
+
+              pnmnorm: add -bsingle, -wsingle.
+
+              ppmtoarbtxt: Do some validation of format strings.  Thanks
+              Akira F Urushibata <afu@wta.att.ne.jp>.
+
+              pamcrater: Add -verbose.
+
+              ppmtoarbtxt: Fail if a #() escape sequence runs off end of
+              file or is too long to process; before, the program would
+              treat the text from # to EOF or where the buffer filled up
+              as literal text, even ignoring any #() within.
+
+              NetBSD: show actual numbers in messages instead of "f" or
+              no information, by using NetBSD's vasprintf.
+
+              Make %g in messages display the actual number instead of "g" in
+              messages where platform doesn't have vasprintf.  (But scores of
+              %f are still left).
+
+              anytopnm: convert all images in a multi-image GIF instead of
+              just the first.
+              
+              Improve "bad magic number" message from pbmXXX, and pgmXXX, and
+              pnmXXX programs.
+
               Fix bogus message from ppmXXX programs when the input is not
               (per the magic number) a Netpbm image.  Introduced after
               Netpbm 10.26 (January 2005) but before Netpbm 10.35 (August
               2006).
 
-              Install: make backward compatibility link ppmtotga -> pamtotga .
-
-14.11.23 BJH  Release 10.47.53
+              ppmtoarbtxt: Fix some undefined behavior when program limits
+              are exceeded (i.e. buffer overruns).
 
               pambackground: fix bug: segfault or incorrect results in most
               cases.  Thanks Ludolf Holzheid (ludolf.holzheid@gmx.de).
               Introduced in Netpbm 10.37 (December 2006).
 
-14.11.01 BJH  Release 10.47.52
+              Windows build: fix universal build failure with "No rule to make
+              ...icon.netpbm.oLINKERISCOMPILER...".  Broken in Netpbm 10.67
+              (June 2014).
+
+14.09.26 BJH  Release 10.68.00
+
+              Split pgmcrater into pamcrater and pamshadedrelief.
 
-              Fix 'make package': missing pkgconfig_template file.
+              pnmconvol: add -bias .
 
-14.09.07 BJH  Release 10.47.51
+              Remove pnmcomp, install a pnmcomp symlink for pamcomp.
 
               Fix incorrect option parsing when there are multiple common
               options (e.g. -plain -quiet).  Always broken.  (Possibility of
@@ -393,18 +619,88 @@ CHANGE HISTORY
               cameratopam: fix buffer overflow.  Always present.  (cameratopam
               was new in Netpbm 10.28 (June 2005)).
 
+              cameratopam: fix incorrect output introduced in Netpbm 10.51
+              (June 2010).
+
+              ppmtopict: Fix unconditional crash.  Introduced in 
+              Netpbm 10.52 (September 2010).
+
+              pcdovtoppm: Fix crash due to invalid operator == on some
+              systems.  Always broken (pcdovtoppm was new sometime between
+              Netpbm 9.25 (March 2002) and Netpbm 10.11 (October 2002)).
+
+              Build: change _XOPEN_SOURCE from 600 back to 500 in 7 files.  It
+              was changed from 500 to 600 in Subversion revision 1731 in
+              Netpbm 10.60 (September 2012) because that made a similar
+              version of Netpbm compile on Mac OSX.  Without it, strdup did
+              not get defined.  But this is apparently a bug in Mac OSX, since
+              X/Open 500 does have strdup.  Furthermore, many other Netpbm
+              files use strdup and apparently compile OK on Mac OSX without
+              600.  Finally, we see today that Illumos system header files
+              deliberately kill the compilation if the compiler is pre-C99 and
+              _XOPEN_SOURCE is 600.  So we go back to 500 and if the problem
+              on Mac OSX gets reported again, we will look more deeply.
+
+              Build: change _XOPEN_SOURCE from 600 back to 500 in
+              jpeg2ktopam.c and pamtojpeg2k.c.  It was changed from 500 to 600
+              in Netpbm 10.41 (December 2007), reportedly to get int_fast32_t,
+              etc. defined on AIX, but other files that use int_fast32_t
+              don't have it today, so that must be wrong.  See above for the
+              drawback of 600.
+
+              Build: fix undefined symbols in fiasco converters with
+              static libraries.
+
+              Build: provide means of setting the default search path for
+              rgb.txt color database via config.mk.
+
+              Build: fix bug which prevents JBIG converters from building with
+              internal JBIG library.  Introduced in 10.67.00.
+
+              Build: fix bug which makes build of Ppmsvga fail (which is
+              attempted only on a system with libvga).  Introduced in Netpbm
+              10.63 (June 2013)
+
               Build: fix build failure in an environment that does not have
               __inline .  Introduced some time between Netpbm 10.26
               (January 2005) and Netpbm 10.35 (August 2006).
 
-              Build: Fix failure to compile lib/libsystem.c because of
-              nonexistent signal classes on some systems.
+              Build: fix build failure on SCO OpenServer due to SIGURG not
+              existing.  Broken in Netpbm 10.49 (December 2009).
+
+              Build: Declare _XOPEN_SOURCE >= 500 in source files that use
+              strdup.
+
+              Build: fix compile failure due to use of reserved word
+              'stdout'.
+
+              Build with 'installosf': Fix crash due to invalid operator ==
+              on some systems.
+
+14.06.29 BJH  Release 10.67.00
+
+              sgitopnm: add ability to convert 2-channel SGI image.
+              Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
+
+              sgitopnm: add ability to work with non-seekable input (e.g. a
+              pipe).  Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
-14.04.30 BJH  Release 10.47.50
+              pamtotiff: add -output, use Standard Output normally (before, it
+              had to be seekable.  Also, you could do an append operation to
+              Standard Output; now you have to use -output for that).  Thanks
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pamsharpness: put primary output on Standard Output instead of
               on Standard Error as a Netpbm message.
 
+              pamflip: fix bug when built with WANT_SSE=n: column-for-row
+              transformations of PBM produce garbage output.  Introduced in
+              10.65 (December 2013).
+
+              sgitopnm: fix bug: no output if input is RLE compressed.  Broken
+              in Netpbm 10.53 (December 2010).  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
+
               jpegtopnm -dumpexif: fix incorrect display of resolution.
               Always broken.  (-dumpexif was new in Netpbm 9.18 September
               2001).
@@ -417,71 +713,214 @@ CHANGE HISTORY
               Always broken.  (-dumpexif was new in Netpbm 9.18 September
               2001).
 
-14.03.29 BJH  Release 10.47.49
+              Windows build: include an icon in every executable.  The icon
+              was designed by Ron Vantreese (ait_frog-netpbm@yahoo.com).
+
+              Build: fix bug in which null value is not taken to mean
+              "in the system search path" for JBIGHDR_DIR and JASPERHDR_DIR.
+
+              Build: Fix inconsistent use of upper and lower case Y and N in 
+              make variables, causing static library not to get built.
+              Introduced in 10.66.
+
+              Build: fix dependencies in .deb package so they work with
+              Debian 6 at least.  Always broken (.deb capability was new in
+              10.66).
+
+14.03.30 BJH  Release 10.66.00
+
+              Add pamvalidate.  Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
+
+              Add pamfix: Does what pamfixtrunc did, plus repairs excessive
+              sample values.
+
+              pamfixtrunc: implement as call to new pamfix.
+
+              pgmhist, ppmhist: Add -forensic: Analyze invalid >maxval pixels.
+              Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
+
+              pgmramp: add -diagonal.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
+
+              libnetpbm: Read functions validate that sample values do not
+              exceed maxval.  Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
+
+              libnetpbm: Validate image dimensions are small enough that you
+              can allocate a row buffer.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
+
+              pgmhist: fix incorrect report of quantiles or crash due to array
+              bounds violation in some builds.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.  Always broken.  Quantile reporting was
+              new in Netpbm 10.61 (December 2012).
+
+              pgmhist: fix buffer overrun with -median.  Always broken.
+              -median was new in Netpbm 10.61 (December 2012).
+
+              pnmmargin: fix for size 0 and superfluous "unexpected operator" 
+              message with size != 0.  Introduced in 10.42.
+
+              pstopnm: fix wrong interpretations of -xsize and -ysize when
+              rendering image in landscape (rotated).  This can appear as
+              stretching and squashing.  Probably always broken.
+
+              pstopnm: fix wrong orientation sometimes when you specify
+              both -xsize and -ysize.  Introduced in 10.65.
+
+              pgmramp: fix bogus output with really large images.  Thanks
+              Akira F Urushibata <afu@wta.att.ne.jp>.  Always broken.
 
               ppmrelief: fix out-of-bound values in output.  Always broken.
-              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              ppmrelief was new in primordial Netpbm in 1989.
+              Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
               ppmrelief: fix crash when input image is too small.  Always
-              broken.  Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              broken.  ppmrelief was new in primordial Netpbm in 1989.  Thanks
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pgmtexture: fix buffer overflow.  Always broken.  (Program
               was added in primordial Netpbm in 1991).
 
               pamdeinterlace: fix incorrect output with -takeodd and image has
               only one row.  Always broken (pamdeinterlace was introduced in
-              Netpbm 9.21 (January 2001)).  Thanks Prophet of the Way
+              Netpbm 9.21 (January 2001)).  Thanks Akira F Urushibata
               <afu@wta.att.ne.jp>.
 
+              configure: warn if user says JPEG library is in the linker's
+              default search path, but it isn't.
+
+              build/install: add tools for creating a Debian package.
+
               make package: Include template for pkgconfig file.
 
+              make package: Include a man/web directory with .url files for
+              each manual page.
+
+              test: Add -portrait to invocations of pstopnm in order to get
+              proper round trips.
+
               Windows build: fix Ppmtompeg build failure in non-Cygwin build
-              due to missing sys/utsname.h.  (But other things still fail).
+              due to missing sys/utsname.h.
 
               Windows build: fix missing .exe on copies of programs under
               their old names.
 
-13.12.15 BJH  Release 10.47.48
+13.12.26 BJH  Release 10.65.00
+
+              pamfunc: add -changemaxval.
+
+              pgmkernel: add -maxval.
+
+              Recognize SIGPWR on systems that have it in messages
+              about signal received.
+
+              pstopnm: More rational default for landscape/portrait choice.
+              In particular, if the image or page is square, image will always
+              be in portrait (not rotated).
+
+              brushtopbm: check for read errors, extraneous data after apparent
+              end of image.
+
+              pnmtops: Fix spurious blank line in asciihex encoding of the
+              image raster.  Probably harmless.  Introduced in 10.56
+              (September 2011).
+
+              pnmtops: Fix crash with 12 bits per sample.  Introduced in 10.53
+              (December 2010).  Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
+
+              pnmtops: Fix bug: wrong output with -ascii85.  Introduced in
+              10.63 (June 2013).
+
+              pnmtops: Fix bug: wrong output with -rle.  Introduced in
+              10.63 (June 2013).
+
+              pnmtops: Fix bug: program hangs if it inherits lots of open
+              files.  Introduced in 10.56 (September 2011).
+
+              pnmtops: Fix bug: fails with message about waitpid() failing
+              if invoked with SIGCHLD ignored.  Introduced in 10.56
+              (September 2011).
 
               pbmtoepsi: fix handling of all-white image.  Always broken.
-              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pbmtoepsi: fix excessively long raster line.  Always broken.
-              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pnmshear: fix incorrect determination of background color.
               Always broken.
 
               ppmpat: fix crash with -squig with aspect ratio < 1:25 or
-              > 25:1. Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              > 25:1. Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
               Always broken.
 
-13.12.03 BJH  Release 10.47.47
+              pgmkernel: fix some pixels 1 less than they should be.
+
+              pamgauss: Fix typo in message.
 
               Fix wild pointer dereference when memory allocation for a string
               fails.  Broken since 10.36 (September 2006).
 
-13.09.26 BJH  Release 10.47.46
+              Build for big-endian machines: fix syntax error so it compiles.
+              Introduced in 10.63 (June 2013).
+
+              Fix compile failure on system such as OpenBSD that don't have
+              SIGWINCH and SIGIO.  Broken since 10.49 (December 2009).
+
+              Build: Use SSE2 vector instructions when compiling with Clang,
+              as done already with GCC.
+
+              Build: Use <emmintrin.h> interface for SSE intrinsics
+              instead of GCC-specific versions.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
+
+              Build on system without vasprintf (not GNU libc): fix compiler
+              warning.
+
+              Apple build: use vasprintf.
 
-              Fixes for Mingw build with MSYS shell.
+13.09.28 BJH  Release 10.64.00
 
-              libnetpbm, pnmpsnr, ppmcie on Mac OS X: fix bogus printing of
-              floating point numbers.
+              pngtopam: fix bug: ignores -gamma.  Introduced in 10.48
+              (September 2009).
 
-13.06.27 BJH  Release 10.47.45
+              libnetpbm, pnmpsnr, ppmcie on systems that don't use GNU C
+              library: fix bogus printing of floating point numbers.
+
+              test: fix ppmhist lack of sorting.
+
+13.06.29 BJH  Release 10.63.00
+
+              Add pamtowinicon, winicontopam.  Thanks Ludolf Holzheid
+              (lholzheid@bihl-wiedemann.de).
+
+              pgmnoise: add -maxval, speed up.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
+
+              Perl programs: make them shell programs that reinvoke themselves
+              as perl so we can get the Perl interpreter from the PATH.
+
+              yuvtoppm: check for inconsistencies between specified
+              width and height and size of input.
+
+              411toppm: check for inconsistencies between specified
+              width and height and size of input.
+
+              Ignore -plain when program generates PAM.  Before, programs
+              failed if the user specified -plain to a program that generates
+              a PAM image.
 
               giftopnm: fix bug: erroneously claims GIF ends prematurely.
               Broken in Netpbm 10.38 (March 2007).  This affects all GIFs, but
               the problem does not manifest when Netpbm was built with Gcc
               from 2007 and later.
 
-              ppmforge. fix crash when -mesh is 1 or less.  Always broken
-              (program was added in Pbmplus (October 1991).  Thanks Prophet of
-              the Way <afu@wta.att.ne.jp>.
+              pnmtops: Fix bug: only first image gets converted.  Broken in
+              Netpbm 10.56 (September 2011).
 
-              ppmforge: fix array bounds violation.  Always broken (program
-              was added in Pbmplus (October 1991).  Thanks Prophet of the Way
-              <afu@wta.att.ne.jp>.
+              pnmtopng: fix bug: incorrect output when output should have
+              an alpha mask.  Broken in Netpbm 10.55 (June 2011).  Thanks
+              Ludolf Holzheid (lholzheid@bihl-wiedemann.de).
 
               pnmtopng: fix bug: output bigger than it needs to be when the
               input is a color format image that contains only gray.  Broken
@@ -493,6 +932,17 @@ CHANGE HISTORY
               -norandom and floyd-steinberg dithering.  Always broken.
               (-norandom was introduced in Netpbm 10.39 (June 2007)).
 
+              pamtilt: fix bug: incorrect output or invalid memory access
+              crash.  Always broken (program was new in Netpbm 10.30
+              (October 2005)).
+
+              pnmpsnr: fix bug: says types aren't the same when they are.
+              Introduced in Netpbm 10.61 (December 2012).
+              
+              ppmtowinicon: fix bug: sometimes creates image truncated in the
+              middle of the AND mask.  Always broken (program was new in
+              Netpbm 9.3 (June 2000)).
+
               ppmtoxpm: fix bug: ignores inability to open the specified color
               dictionary file (-rgb) and just doesn't use color names.
               Introduced in Netpbm 10.15 (April 2003).
@@ -502,25 +952,87 @@ CHANGE HISTORY
               because the color dictionary file was not openable.  ppmtoxpm
               suffers from this.  Broken in 10.15 (April 2003).
 
-13.05.03 BJH  Release 10.47.44
+              libnetpbm: fix bug: pnm_readpaminit and pnm_writepaminit set
+              'opacity_plane' member of struct pam incorrectly.  No Netpbm
+              programs are affected.  Always broken (member was added in
+              Netpbm 10.56 (September 2011)).
 
-              ppmtowinicon: fix bug: sometimes creates image truncated in the
-              middle of the AND mask.  Always broken (program was new in
-              Netpbm 9.3 (June 2000)).
+              sparc64 pbmtog3: fix bug that causes crash due to unaligned
+              memory access.
 
-              pamtilt: fix bug: incorrect output or invalid memory access
-              crash.  Always broken (program was new in Neptbm 10.30
-              (October 2005)).
+              ppmforge. fix crash when -mesh is 1 or less.  Always broken
+              (program was added in Pbmplus (October 1991).  Thanks 
+              Akira F Urushibata <afu@wta.att.ne.jp>.
+
+              ppmforge: fix array bounds violation.  Always broken (program
+              was added in Pbmplus (October 1991).  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
+
+              install: fix Perl warning in installnetpbm.  Broken in 
+              Netpbm 10.61.
+
+              build: Use <stdbool.h> when available.
+
+              build: fix problem with creating lib/util that already exists.
+              Broken in Netpbm 10.62.
+
+13.03.28 BJH  Release 10.62.00
+
+              pnmtorast: set don't care bytes to zero to make output
+              repeatable.
+
+              rasttopnm: add -dumpheader, dumpcolormap.
+
+              pamstereogram: change -guidesize default from 10 to 20
+              (relevant since Netpbm 10.61).
+
+              rasttopnm: fix bug: incorrect output due to used-before-set
+              variable.  Introduced in Netpbm 10.56 (September 2011).
+
+              pamstereogram: fix bug: doesn't reject negative guidesize.
+              Broken since Netpbm 10.61.
+
+              pamstereogram: fix bug: garbage in -verbose listing.  Broken
+              since Netpbm 10.61
+
+              Windows MinGW build: various fixes.
+
+12.12.30 BJH  Release 10.61.00
+
+              pgmhist: Add -machine option.
 
-13.02.20 BJH  Release 10.47.43
+              pgmhist: Add -median, -quartile, and -decile options.
+
+              pamstereogram: Add -guidetop and -guidebottom options to replace
+              trick where negative -guidesize means top, positive means
+              bottom, absent means none.
+
+              pamtojpeg2k: default to no compression ratio constraint and
+              allow compression ratios less than 1.  Because of compression
+              metadata, small images do require loss of quality in order to
+              get down to a compression ratio of 1.
+
+              pamstereogram: -smoothing smooths images even without -texfile.
+              Thanks Scott Pakin (scott@pakin.org).
+
+              pnmcat: set don't care bits in packed PBM output to zero so
+              they are predictable.
+
+              libpam, pamcomp: fix bug: treats tuple type GRAYSCALE_ALPHA like
+              GRAYSCALE on 32-bit machine.  Actually, looks only at first 4
+              characters (8 characters on machine with 64 bit addressess) of
+              the tuple type.  Broken since Netpbm 10.56 (September 2011).
 
               pngtopam -alphapam with grayscale input: fix bug: generates
               invalid output: tuple type GRAYSCALE_ALPHA, but depth 1.  Depth
               should be 2.  Always broken (pngtopam was created in 
               Netpbm 10.44 (September 2008)).
               
-              ppmpat: Fix bug: wrong output with -poles.  Broken in 10.47
-              (June 2009).
+              pamtotiff: fix bug: XRESOLUTION, YRESOLUTION, and RESOLUTIONUNIT
+              not allowed in -tags.  Broken at least since Netpbm 10.35.
+
+              pnmpsnr: fix crash when invoked (invalidly) with only one
+              argument.  Always broken.
 
               leaftoppm: fix incorrect determination of whether input is
               Interleaf on 64 bit systems.  Always broken.
@@ -528,56 +1040,93 @@ CHANGE HISTORY
               cmuwmtopbm: fix incorrect determination of whether input is
               a CMU window manager bitmap on 64 bit systems.  Always broken.
 
-12.12.04 BJH  Release 10.47.42
+              pnmmontage: fix totally wrong output.  Broken in Netpbm 10.44
+              (August 2008).
 
-              pamtotiff: fix bug: XRESOLUTION, YRESOLUTION, and RESOLUTIONUNIT
-              not allowed in -tags.  Broken at least since 10.35.
+              pnmmontage: fix random stuff placed in unoccupied space in the
+              output image.  Always broken (program was new in Netpbm 9.10
+              (January 2001).
 
-              pnmpsnr: fix crash when invoked (invalidly) with only one
-              argument.  Always broken.
+              pbmpscale, pgmhist, pampick, pamtompfont: fix wild pointer in
+              command line parsing.  Bug has always been there in pampick,
+              pamtompfont, since Netpbm 10.50 (March 2010) in pbmpscale, since
+              10.44 (September 2008) in pgmhist.
+
+              xbmtopbm: fix incorrect output, memory leak.  Thanks Akira F
+              Urushibata <afu@wta.att.ne.jp>.
+
+              sunicontopnm: Fix incorrect output for depth 8.  Always broken
+              (depth = 8 capability was added in Netpbm 10.53 (December 2010).
+
+              pamgauss: Fix bug: erroneously says -maxval is too big on 64 bit
+              system.  Always broken (Pamgauss was added in Netpbm 10.23 (July
+              2004).
 
-              pgmhist, pampick, pamtompfont: fix wild pointer in command line
-              parsing.  Bug has always been there in pampick, pamtompfont,
-              since 10.44 (September 2008) in pgmhist.
+              ppmpat: Fix bug: wrong output with -poles.  Broken in Netpbm
+              10.47 (June 2009).
 
-              Xbmtopbm: fix incorrect output, memory leak.  Thanks Prophet of
-              the Way <afu@wta.att.ne.jp>.
+              Add tests.  Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
-12.10.03 BJH  Release 10.47.41
+12.09.30 BJH  Release 10.60.00
 
-              pamgauss: Fix bug: erroneously says -maxval is too big.
-              Always broken (Pamgauss was added in 10.23 (July 2004)).
+              xpmtoppm: major speedup for 3-character-per pixel files,
+              memory reduction for all files: use hash table instead of
+              linear search or direct index, go row by row.
 
-12.08.20 BJH  Release 10.47.40
+              xpmtoppm: fix bogus "color number too large" failure.  Broken
+              in Netpbm 10.49 (December 2009).
+
+              pnm_hashtuple: slight performance improvement from new hash
+              function.  Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
+
+              ppmtospu: wild memory accesses.  Always broken (program added in
+              Netpbm 10.58 (March 2012).
+
+              pamtosrf: fix storage corruption.  Always broken (program added
+              in Netpbm 10.55 (June 2011).
+
+              Build: change _XOPEN_SOURCE 500 to 600 because on a Mac OSX
+              Netpbm 10.8 system, this is necessary to get 'strdup' into
+              <strings.h>.
 
               Build: rename getline() in xpmtoppm.c to avoid collision
               with some libc.  Always broken.
 
-12.07.04 BJH  Release 10.47.39
+              Build: Don't expect GCC facilities to exist when compiler is
+              Clang.  (Note that Clang identifies itself as GCC).
 
-              ppmtobmp: fix failure with "internal error" message on all
-              uses.  Broken in 10.47.23.
+12.06.28 BJH  Release 10.59.00
 
-12.06.21 BJH  Release 10.47.38
+              pamtogif: Make data blocks 255 bytes instead of 254.  255 is
+              the maximum allowed by GIF.  254 was intended to not trigger
+              potential bugs in programs that read the file.
 
-              configure: work around Perl bug that causes 'configure' to
-              falsely conclude Svgalib is installed.
+              ppmdraw: Add 'filledcircle' command class.  Thanks
+              Elijah Griffin <eli@panix.com>.
 
-              Build: move -lm to end of -l's.  Broken at least since 10.35.
+              pamstereogram: Add -planes .  Thanks Scott Pakin
+              (scott@pakin.org).
 
-12.02.15 BJH  Release 10.47.37
+              pamstereogram: improve verbose output.  Thanks Scott Pakin
+              (scott@pakin.org).
 
-              Fix compile error from 10.47.36.
+              pamstereogram: fix crash introduced in Netpbm 10.54 (March 2011).
 
-12.02.15 BJH  Release 10.47.36
+              pamstereogram: fix crash introduced in Netpbm 10.53 (December
+              2010).
 
-              pm_make_tmpfile_fd: fix improper handling when unable to get
-              memory to construct file name.
+              Build: fix bug: declines to build pnmtops.  Broken in Netpbm
+              10.58.
 
-              Windows/Mingw: fix bug: temporary file creation fails
-              consistently.  Broken in 10.34.
+              Build: move -lm to end of -l's.  Broken at least since Netpbm
+              10.35.
+
+12.03.29 BJH  Release 10.58.00
 
-12.01.14 BJH  Release 10.47.35
+              Add ppmtospu, 22 years late.
+
+              pngtopam: fix incorrect output when PNG has 16 bits per pixel.
+              Broken in Netpbm 10.56.
 
               pgmtexture: fix integer overflow in difference variance.
               Always broken.
@@ -585,103 +1134,278 @@ CHANGE HISTORY
               pgmtexture: fix array bounds violations in various calculations.
               Always broken.
 
-11.12.12 BJH  Release 10.47.34
+              xpmtoppm: Make it work on XPM with zero characters per pixel.
+
+              pm_make_tmpfile_fd: fix improper handling when unable to get
+              memory to construct file name.
+
+              ppmquantall: replace with pnmquantall and change from Bash
+              to Perl.
+
+              Windows/Mingw: fix bug: temporary file creation fails
+              consistently.  Broken in Netpbm 10.34.
+
+              Windows/Mingw: set binary mode on files.
+
+              Windows: compute program name correctly.
+
+              configure: work around Perl bug that causes 'configure' to
+              falsely conclude Svgalib is installed.
+
+              Build: Use rand/srand instead of random/srandom because the
+              latter are not present in Mingw builds.
+
+              Build: fix bug finding X libraries for merge buildl
+
+              Build: redo conditional compilation of Windows to use
+              MSVCRT, based on _WIN32, instead of WIN32.
+
+              Build, Windows: various fixes.
+
+              Build: use Pkgconfig to find libxml2 if it works (fall back
+              to xml2-config).
+
+11.12.30 BJH  Release 10.57.00
+
+              pnmnorm: add -midvalue, -middle .
+
+              pngtopam: fix crash with invalid tIME chunk.  Always broken.
+
+              pamarith: fix wrong result with -multiply.  Broken in Neptbm
+              10.41.
 
               pamscale: fix all black output with resampling.  Always broken.
 
-11.11.22 BJH  Release 10.47.33
+              Build: don't use <alloca.h>.
 
-              pngtopam, pngtopnm: fix crash with invalid tIME chunk.  Always
-              broken.
+11.09.28 BJH  Release 10.56.00
 
-11.11.08 BJH  Release 10.47.32
+              Add pamexec.  Thanks Michael Pot <fmw@actrix.co.nz>.
 
-              pamarith: fix wrong result with -multiply.  Broken in 10.41.
+              pbmclean: add -extended.  Idea from kugland@gmail.com.
 
-11.09.25 BJH  Release 10.47.31
+              rasttopnm: add -index.
 
-              bmptopnm: Fail properly with BMP that indicates an illegal bits
-              per pixel.
+              pamcomp: Retain opacity information from underlying image.
 
-              pnmtops: fix message: says "from top edge" where it means
-              "from bottom edge."
+              pnmtops: Add PBM fast path.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
+
+              libnetpbm and most programs that use color maps: speedup with
+              new color hash function.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
+
+              pnmquant: use File::Temp::tempfile() instead of local
+              approximation if it is available.
+
+              pnmquant: work with older Perl that doesn't have 3-argument open.
+              Thanks Slaven Rezic <srezic@iconmobile.com>.
 
-11.08.22 BJH  Release 10.47.30
+              pamscale: Issue error message instead of ignoring extraneous
+              arguments.
 
+              libnetpbm, many programs: fix bug in pm_allocarray(): returns
+              NULL when can't get memory.  Calling programs thus crash on
+              very large images.  Introduced in Netpbm 10.51.
+              
               pnmtopng: fix bug: with -alpha specifying a mask which contains
               no fully transparent area, output PNG is fully opaque.
-              Introduced in 10.29.
+              Introduced in Netpbm 10.29.
 
-              pnmquant: work with older Perl that doesn't have 3-argument open.
+              pnmtops: fix message: says "from top edge" where it means
+              "from bottom edge."
+
+              ppmcie: fix bug: fails with "X coordinate out of range" error.
+              Introduced in Neptbm 10.51.
+
+              bmptopnm: Fail properly with BMP that indicates an illegal bits
+              per pixel.
+
+              Build: To find libpng, use the Pkgconfig database entry instead
+              of libpng-config if it is available.
+
+              Build: pnmtops: Instead of omitting pnmtops from the build when
+              there is no libz, just omit ability to create flate-compressed
+              output from pnmtops.
+
+11.06.29 BJH  Release 10.55.00
 
-11.07.10 BJH  Release 10.47.29
+              Add pamtosrf, srftopam.  Thanks Mike Frysinger
+              (vapier@gentoo.org).
 
               pgmtexture: fix wrong sum variance result.  Wrong since the
               beginning.  Thanks Francois P. S. Luus <fpsluus@gmail.com>.
 
-11.06.25 BJH  Release 10.47.28
-
-              pnmpsnr: fix bug: says PGM images differ when they don't
-              and vice versa.
+              pamrubber: fix crash.  Introduced in Netpbm 10.54.
 
               libnetpbm: pm_system(): fix bug - program always takes
               Standard Input from invoker's Standard Input when you don't
-              supply a Standard Output accepter.
+              supply a Standard Output accepter.  Introduced in Netpbm 10.40.
 
-11.03.27 BJH  Release 10.47.27
+              ppmtobmp: fix bug: crash ("internal error") or bogus failure or
+              incorrect output on image without palette (e.g. black and
+              white).  Introduced in Netpbm 10.54.
 
-              g3topbm: correct error message: codes go up to 13 bits.
+              pnmtops: fix floating point exception or bogus width/height
+              too large error.  Introduced in Netpbm 10.53.
 
-11.01.31 BJH  Release 10.47.26
+              pnmcat: Fix garbage output when multiple input image are from
+              Standard Input (now it just fails gracefully).
 
-              asciitopgm: fix bug: memory corruption on too-long lines.
+              Build: Use 'pkg-config' to find X libraries if possible (This
+              works with modern Xorg installations).
 
-              asciitopgm: fix bug: improper handling of blank lines.
-              
-              Build: compiles with libpng 1.4.  (This was done in 10.47.04
-              too, but apparently didn't work).
+              Build: pnmtopng, pamrgbatopng compile with newer libpng,
+              as they no longer refer to private members of the pnginfo
+              structure.
+
+              Build: fix undefined "strsol" and "vsnprintfN" failure on
+              systems without vasprintf().  Introduced in Netpbm 10.53.
+
+              Build: fix compiler warning in pbmtocmuwm.
 
-11.01.15 BJH  Release 10.47.25
+              configure: fix selection of mingw compiler.  Introduced in
+              Netpbm 10.48.
+
+11.03.30 BJH  Release 10.54.00
+
+              Add pamrubber.  Thanks Willem van Schaik <willem@schaik.com>.
+
+              Add ppmtoapplevol.  Thanks Matthew Garrett
+              <mjg59@srcf.ucam.org>.
+
+              Add pamwipeout.  Thanks Willem van Schaik <willem@schaik.com>.
+
+              pngtopam: add -byrow.
+
+              pnmtopclxl: add -embedded.
+
+              pgmtoascii: Truncates instead of undefined behavior if input
+              image is wider than specified width.
+
+              Add libpamd (PAM version of classic libppmd drawing routines).
+
+              Rename pbmtoicon to pbmtosunicon, to go with change
+              of icontopbm to sunicontopnm in Netpbm 10.53.
+
+              g3topbm: correct error message: codes go up to 13 bits.
+
+              pamstereogram: fix crash when not doing texture.
+
+              pgmtoascii: Fix numerous output bugs.
 
               pngtopam: fix bug: -verbose reports history chunk present when
               it's really a palette.
 
-10.12.30 BJH  Release 10.47.24
+              pnmpsnr: fix bug: says PGM images differ when they don't
+              and vice versa.
+
+10.12.30 BJH  Release 10.53.00
 
-              configure: don't default to /usr/X11R6/lib/libX11.so just because
-              /usr/X11R6 exists.  /usr/X11R6/lib must exist.
+              Add pammosaicknit.  Thanks Scott Pakin.
 
-10.12.10 BJH  Release 10.47.23
+              pstopnm: Add -textalphabits, default Ghostscript TextAlphaBits
+              to 4.
+
+              pngtopam: include tuple type in output.  Thanks
+              Thomas Henlich <thenlich@users.sourceforge.net>.
+
+              sunicontopnm: Understands Depth=8.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
+
+              sunicontopnm: Validate header.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
 
               bmptopnm: Don't crash on invalid zero value of image height in
               the BMP header of a compressed file.  Always broken.
               
               bmptopnm: don't crash on large invalid value of 'colorsused' in
-              the BMP header.
-
-              pngtopam: include tuple type in output.  Thanks
-              Thomas Henlich <thenlich@users.sourceforge.net>.
-
-10.11.01 BJH  Release 10.47.22
+              the BMP header.  Introduced in Netpbm 9.11.
 
-              pnmtops: Fix incorrect output with -flate.  Thanks Prophet of
-              the Way <afu@wta.att.ne.jp>.
+              pm_mallocarray2: fix wild pointers when image is too big to fit
+              in a single chunk of malloc memory.  Affects many Netpbm
+              programs.  Introduced in Netpbm 10.51.00.
 
               ilbmtoppm: Don't crash on image that has a transparent color
               index, but no color map.
 
-10.10.16 BJH  Release 10.47.21
+              sunicontopnm: fix for 32 bit items in input.  Thanks Akira F
+              Urushibata <afu@wta.att.ne.jp>.
 
-              configure: fix crash when libpng test compile fails.
+              sunicontopnm: fix arithmetic overflows.  Thanks Akira F
+              Urushibata <afu@wta.att.ne.jp>.
 
-10.09.30 BJH  Release 10.47.20
+              icontopbm: rename to sunicontopnm.
+
+              pamstereogram: Add mapped texture stereogram option.
+              Thanks Scott Pakin.
+
+              pamstereogram: slight change to dpi and eye separation defaults.
+              invert near/far dark/light association.  Thanks Scott Pakin.
+
+              pnmtops: Fix incorrect output with -flate.  Thanks Akira F
+              Urushibata <afu@wta.att.ne.jp>.
+
+              sgitopnm: Fix arithmetic overflow on -channel option.  Thanks
+              Akira F Urushibata <afu@wta.att.ne.jp>.
+
+              sgitopnm: Fix crash with -channel on verbatim SGI image.  Thanks
+              Akira F Urushibata <afu@wta.att.ne.jp>.
+
+              fitstopnm: Deal properly with NaN in input image.
 
               pm_floatFromBigendFloat, pm_doubleFromBigendFloat, fitstopnm:
-              fix corrupted output.  Broken in 10.46.
+              fix corrupted output.  Broken in Netpbm 10.46.
+
+              pamtopdbimg: fix corrupted output image.  Thanks Scott Pakin.
+
+              pdbimgtopam: fix corrupted output image.  Thanks Scott Pakin.
+
+              pnmtops: fix arithmetic overflows.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
+
+              Rename all external symbols that don't have a Netpbm prefix
+              to start with "pm_": shhopt, nstring, nsleep.  Eliminate
+              createBlackTuple -- pnmCreateBlackTuple already existed.
+
+              libnetpbm: Change shared library major number to 11 because
+              of above renaming.
+
+              build: include -lm in build of pamtopdbimg, pdbimgtopam.
+
+              configure: Make warnings stand out more.
+
+              configure: Fix crash when libpng test compile fails.
+
+10.09.26 BJH  Release 10.52.00
+
+              Add pamtopdbimg, pdbimgtopam.
+
+              Add pamrecolor.  Thanks Scott Pakin.
+
+              anytopnm: Use 'pngtopam' shell command instead of 'pngtopnm'.
+
+              pnmtopng:  -libversion doesn't report level of linked libz.
+              It was a modularity violation and caused build failures on
+              Mac OS X, because pnmtopng per se doesn't know about libz --
+              it's used by libpng.
+
+              libnetpbm: add pm_readbiglong2, pm_readbiglong2u,
+              pm_readlittlelong2, pm_readlittlelong2u: These use the proper
+              32 bit integer types instead of "long".  (But the old ones
+              still work in legacy code because long is always at least 32
+              bits).
+
+              pnmconvol: fix reversed sense of -nooffset.  Introduced in
+              Netpbm 10.49.
 
               ppmtompeg: fix crash with free of unallocated memory.
+              Broken after Netpbm 10.18, not later than 10.26.
 
-10.08.28 BJH  Release 10.47.19
+              Build: fix parallel make - multiple simultaneous attempts to
+              build lib/util/importinc.
+
+              Build: don't fail due to SIGRTMIN, SIGRTMAX being undefined.
 
               Build: don't expect snprintf() to exist.
 
@@ -690,115 +1414,179 @@ CHANGE HISTORY
               Build: fix PNGVER.  Thanks Matthew Fischer
               <futhark@users.sourceforge.net>.
 
-10.07.27 BJH  Release 10.47.18
+10.06.27 BJH  Release 10.51.00
 
-              Pnmtopng:  -libversion doesn't report level of linked libz.
-              It was a modularity violation and caused build failures on
-              Mac OS X, because Pnmtopng per se doesn't know about libz --
-              it's used by libpng.
+              Add ppmtoascii.  Thanks "Frank Ch. Eigler" <fche@elastic.org>.
 
-10.07.06 BJH  Release 10.47.17
+              pnmtops: Add -bitsperpixel option.
 
-              Build: don't fail due to SIGRTMIN, SIGRTMAX being undefined.
+              pamx: Make exit status 0 instead of 10 when window manager
+              requests termination.
 
-10.06.17 BJH  Release 10.47.16
+              pnmsmooth: Respect -plain.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
+
+              pnmsmooth: Don't display pnmconvol messages (i.e. run
+              pnmconvol with -quiet).
+
+              pamflip: speedup for PBM.  Use SSE2 and skip some idempotent
+              pixel movement.  Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
+
+              anytopnm: recognize "PC bitmap" in 'file' response as BMP.
+              (in addition to existing "PC bitmap data").
+
+              libnetpbm, various PBM programs: Use SSE insted of MMX.  Thanks
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pbmtext/libpbmfont: Fix wild pointer; probably asymptomatic.
-              Introduced in 10.39.
+              Introduced in Netpbm 10.39.
 
               pbmtext/libpbmfont: Fix some error messages for bad fonts.
 
               pbmtext/libpbmfont: fix crash with a BDF font with negative
-              left or bottom border.  Introduced in 10.39.
+              left or bottom border.  Introduced in Netpbm 10.39.
+
+              pamarith: fix memory leak.  Introduced in Netpbm 10.41.
 
-10.06.03 BJH  Release 10.47.15
+              pnm_bytespersample(): fix bogus assertion on 64 bit system.
+
+              pnmtops: fix bug: 12 bits per sample output when 8 would do.
+              Introduced in Netpbm 10.40.
+
+              palmtopnm: fix for pixel size 16.  Thanks Paul Bolle
+              <pebolle@tiscali.nl>.
+
+              pnmsmooth: Fail politely when convolution matrix is so
+              large as to bust the system's program parameter size limit
+              on the invocation of pnmconvol.
+
+              avstopam: fix incorrect output.
+
+              pnmsmooth: fix arithmetic overflow with absurdly large
+              convolution matrix dimensions.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
 
-              libnetpbm line drawing: fix bogus assertion, introduced in 10.47.
+              pnmsmooth: fix wild pointer: causes wrong arguments to
+              'pnmconvol'.  Introduced in Netpbm 10.50.  Thanks Akira F
+              Urushibata <afu@wta.att.ne.jp>.
+
+              pamscale: fix -reduce.  Introduced in Netpbm 10.27 (March 2005).
+
+              pampaintspill: fix incorrect output.
 
               libnetpbm text drawing: fix incorrect output in all cases.
-              Introduced in 10.47.
+              Introduced in Netpbm 10.47.
+
+              libnetpbm line drawing: fix bogus assertion, introduced in
+              Netpbm 10.47.
 
               build: fix incorrect determination of when vasprintf() exists
               in libc.
 
-10.05.20 BJH  Release 10.47.14
+              configure: don't default to /usr/X11R6/lib/libX11.so just because
+              /usr/X11R6 exists.  /usr/X11R6/lib must exist.
 
-              pamarith: fix memory leak.
+10.03.27 BJH  Release 10.50.00
 
-10.05.04 BJH  Release 10.47.13
+              Add pamtoavs, avstopam.  Thanks Scott Pakin.
 
-              pnmtops: fix bug: 12 bits per sample output when 8 would do.
-              Introduced in 10.40.
+              Add pampaintspill.
 
-10.04.20 BJH  Release 10.47.12
+              pnmconvol: Add -normalize .
 
-              palmtopnm: fix for pixel size 16.  Thanks Paul Bolle
-              <pebolle@tiscali.nl>.
+              pm_system(): Close extraneous file descriptors that, among
+              other things, prevent child from seeing EOF.
 
-              pamscale: fix -reduce.  Introduced in 10.27.
+              libnetpbm: Add PNM_GETR(), PNM_GETG(), PNM_GETB().  Same
+              as PPM_GETR(), etc.
 
-              pbmtext: fix crash when BDF font file contains spurious
-              blank line.  Ignore such blank lines.
+              libnetpbm: Add ppm_luminosity().  Same as PPM_LUMIN, but
+              returns pixval.
 
-10.03.17 BJH  Release 10.47.11
+              pnmhisteq: Equalize based on luminosity alone, rather than a
+              strange combination of luminosity and HSV value.
 
-              ppmtoilbm: fix arithmetic overflow with image dimension
-              represented as 16 bit integer.
+              pamenlarge: Make special fast path for scale factors up to 10
+              (2, 3, and 5 already existed).  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
 
-              pbmpage: fix garbage output.
+              pamflip: Speed up for most images.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
 
-              Build: don't fail due to SIGPWR being undefined.
+              ybmtopbm: Assume YBM format has raster in natural order
+              instead of byte-reversed.  This is what pbmtoybm creates,
+              and is most logical.  We don't know if there are any existing
+              YBM images or generators of them other than pbmtoybm.
+
+              pbmpscale: speedup.
+
+              pbmclean: speedup.
+
+              pbmtext: fix crash when BDF font file contains spurious
+              blank line.  Ignore such blank lines.
+              
+              pbmpscale: fix arithmetic overflow on output image dimensions.
 
-10.02.23 BJH  Release 10.47.10
+              pbmtogem, pbmtoybm, pgmtolispm, ppmtoilbm, pnmtosgi: fix
+              arithmetic overflow with image dimension represented as
+              16 bit integer.
+
+              pbmpage: fix garbage output.
 
               pnmhistmap: Fix crash with -width.  Always broken.
 
-              pm_system(): Close extraneous file descriptors that, among
-              other things, prevent child from seeing EOF.
+              libppmd/ppmpat: fix wild pointer in ppmd_fill_drawprocp();
+              broken in Netpbm 10.47.
 
-10.01.25 BJH  Release 10.47.09
+              palmtopnm: fix incorrect "PALM_DIRECT_COLOR_FLAG is not valid
+              for version 3 encoding type" failure.  Thanks Paul Bolle
+              <pebolle@tiscali.nl>.
 
               pamtosvg: fix bug: occasional crash with out of range error.
+              Introduced in Netpbm 10.42.
 
-10.01.11 BJH  Release 10.47.08
+              palmtopnm: fix incorrect output with version 3 direct color.
+              Thanks Paul Bolle <pebolle@tiscali.nl>.
 
-              libppmd/ppmpat: fix wild pointer in ppmd_fill_drawprocp();
-              broken in 10.47.00.
+              Build: don't fail due to SIGPWR being undefined.
 
-              Restore ability of Pnmconvol convolution matrix to be a
-              pseudo-plain-PNM with samples that exceed the maxval.  Lost in
-              10.30 due to addition of maxval-checking code to libnetpbm.
+09.12.30 BJH  Release 10.49.00
 
-              pnmtofiasco: fix bug: doesn't work with Standard Input.
+              Add pnmmercator.
 
-              palmtopnm: fix incorrect "PALM_DIRECT_COLOR_FLAG is not valid
-              for version 3 encoding type" failure.
+              pnmconvol: Add -matrix, -matrixfile.
 
-              palmtopnm: fix incorrect output with version 3 direct color.
+              pambayer: add -nointerpolate.
+
+              jpeg2ktopam: Work with JP2 input as well as JPC.
 
-09.12.29 BJH  Release 10.47.07
+              pamscale: Work on multi image stream.
+
+              ppmrainbow: Make new directory for temporary files.
+              Prevents interference by other user who shares the main
+              temporary file directory.
 
               libnetpbm: When reading plain format PNM with PAM routines,
               validate pixel against maxval (necessary for integer non-overrun
               guarantees).
 
-              xpmtoppm: fix wild pointer with color index > 127.
+              pnmsmooth: change -dump option to output a new pnmconvol
+              -matrix option instead of a PGM file (because pnmsmooth now
+              uses -matrix).
+
+              pnmtofiasco: fix bug: doesn't work with Standard Input.
 
               pnmsmooth: fix incorrect call to pm_system_lp() that makes
               it never work (but it wouldn't anyway because pm_system_lp()
               was broken -- see below).
 
-              pm_system*: fix various bugs that make it never work.
+              pm_system*: fix various bugs making it never work.
 
-09.12.10 BJH  Release 10.47.06
-
-              pamtosvg: fix compile failure from 10.47.05.
+              xpmtoppm: fix wild pointer with color index > 127.
 
-09.12.10 BJH  Release 10.47.05
-
-              ppmrainbow: Make new directory for temporary files.
-              Prevents interference by other user who shares the main
-              temporary file directory.
+              xpmtoppm: fix wild pointer when the input has a color index
+              value that is too large to be consistent with the number of
+              colors and bytes per pixel.
 
               pamtouil: fix crash when -name option doesn't contain an
               underscore.
@@ -807,39 +1595,60 @@ CHANGE HISTORY
 
               pnmtofiasco, fiascotopnm: fix bug on bigendian machine.
 
-09.09.18 BJH  Release 10.47.04
+              pngtopam: use png_create_read_struct() instead of
+              png_create_write_struct().  Broken since Netpbm 10.48.
 
-              pambayer: fix unconditional crash/failure when you aren't using
-              Standard Input.
+              configure: advise if adding -lz -lm fixes libpng link problem.
 
-              Build: use AR from config.mk instead of "ar" to build static
-              libraries: libnetpbm, librle, libjasper.
+              makeman: some fixes or enhancements.
 
-              Build: compiles with libpng 1.4 beta.
+09.09.27 BJH  Release 10.48.00
 
-              Build: don't use "uint".
+              ppmsvgalib: Wait to initialize Svgalib to prevent it from
+              interfering with error messages early code might issue, and
+              leaving the console in an undesirable state if the failures
+              cause the program to abort early.
 
-              Package: fix installation of pnmplain on Windows (.exe suffix).
+              tifftopnm: wait for Pamflip processes to terminate before
+              exiting.
 
-09.09.03 BJH  Release 10.47.03
+              Remove pngtopnm from the package.  Pngtopnm is now an alias
+              for Pngtopam.
 
-              Build: fix failure to recognize static library in omitting
-              -R from link.
+              pngtopam, pnmtopng: Compiles with libpng 1.4 beta.
 
-09.08.17 BJH  Release 10.47.02
+              pamtotiff: do miniswhite properly with 8 and 16 bit samples.
 
-              tifftopnm: wait for Pamflip processes to terminate before
-              exiting.
+              pamsumm: fix syntax error message.
+
+              pambayer: fix unconditional crash/failure when you aren't using
+              Standard Input.
+
+              Add pbmtocis, cistopbm.  Thanks John Elliott
+              <jce@seasip.demon.co.uk>.
 
-              Build: Compiles with libpng 1.4 beta.
+              Build: tifftopnm builds on systems without fork().
 
               Build: work with JPEG library Version 7.
 
-09.07.23 BJH  Release 10.47.01
+              Build: Configure recognizes libvga.a installed without
+              libvga.so and offers that as default.
+
+              Build: Configure recognizes the Mingw-64 compiler.
 
               Build: fix failure of a merge build on a system that doesn't
               have the PNG library.
 
+              Build: fix failure to recognize static library in omitting
+              -R from link.
+
+              Build: use AR from config.mk instead of "ar" to build static
+              libraries: libnetpbm, librle, libjasper.
+
+              Build: don't use "uint".
+
+              Package: fix installation of pnmplain on Windows (.exe suffix).
+
 09.06.27 BJH  Release 10.47.00
 
               Add pamsistoaglyph.  Thanks Scott Pakin.
@@ -878,7 +1687,7 @@ CHANGE HISTORY
               problems.
 
               ilbmtoppm: fix some bug in interpreting ILBM input.
-              (due to wrong pm_uintFromBigend16()).  From 10.46.
+              (due to wrong pm_uintFromBigend16()).  From Netpbm 10.46.
 
               ximtoppm: fix crash in command line processing.
 
@@ -889,7 +1698,7 @@ CHANGE HISTORY
               ppmquantall: don't use 'set' to set Bourne shell variable.
 
               pnmtile: fix reference to arbitrary storage in option
-              processing.  Introduced in 10.42.
+              processing.  Introduced in Netpbm 10.42.
 
               pamstereogram: fix tuple type in output file (and crash
               with -verbose) with -patfile .
@@ -929,23 +1738,23 @@ CHANGE HISTORY
               the background color (i.e. the color of added margins).
 
               pnmscale: finally make it just an alias of pamscale, which
-              obsoleted it in 10.20.
+              obsoleted it in Netpbm 10.20.
               
               pnmcut: finally make it just an alias of pamcut, which
-              obsoleted it in 9.20.
+              obsoleted it in Netpbm 9.20.
 
               tifftopnm: fix orientation problem on big-endian machines.
-              Introduced in 10.42.
+              Introduced in Netpbm 10.42.
 
               pnmcrop: various fixes.
 
-              g3topbm: fix array bound violation.  Introduced in 10.32.
+              g3topbm: fix array bound violation.  Introduced in Netpbm 10.32.
 
               pnmcat: fix array bound violation with PBM top/bottom
-              concatenation.  Introduced in 10.44.
+              concatenation.  Introduced in Netpbm 10.44.
 
               ilbmtoppm: Fix array bound violation with compressed ILBM.
-              Introduced in 10.18.
+              Introduced in Netpbm 10.18.
 
               fitstopnm: fix garbage output when input is little endian
               floating point FITS.
@@ -956,7 +1765,7 @@ CHANGE HISTORY
               picttoppm: Improve error/informational messages.
 
               picttoppm: Don't fail if 'fontdir' file doesn't exist.  Bug
-              from 10.44.
+              from Netpbm 10.44.
 
               ppmtopict: Use two-byte length field when image width > 200
               instead of > 250.  Former is what Picttoppm has been assuming
@@ -965,11 +1774,11 @@ CHANGE HISTORY
 
               ilbmtoppm: fix bug: appends color map PPM to output if input
               has color map; fails if input doesn't have color map.  Broken
-              in 10.18.
+              in Netpbm 10.18.
 
               leaftoppm: fix bug: uses red channel as all three channels;
-              (produces grayscale output).  Broken between 10.19 and 10.26,
-              inclusive.
+              (produces grayscale output).  Broken between Netpbm 10.19 and
+              10.26, inclusive.
 
               pbmtomrf, mrftopbm: fix crashes, incorrect output in all
               cases.  Broken forever.
@@ -1010,7 +1819,7 @@ CHANGE HISTORY
 
               Build: Move LDFLAGS later so that a -L in LDFLAGS doesn't
               interfere with finding the built libnetpbm.  (The common
-              link rule is already this way in 10.44.00; all the other
+              link rule is already this way in Netpbm 10.44.00; all the other
               link rules are now the same).
 
               Build: Rename Makefile.common, Makefile.config, to common.mk,
@@ -1028,7 +1837,7 @@ CHANGE HISTORY
               computing means.
 
               pamcut, pnmcat, pnmpaste, pnmpad, g3topbm: Add fast PBM
-              path.  Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              path.  Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pnmpaste: fail if user specified stdin for both images.
 
@@ -1043,10 +1852,10 @@ CHANGE HISTORY
 
               pamcut: don't crash when cutting a region entirely to
               the left or right of the input image, with -pad.  Thanks
-              Prophet of the Way <afu@wta.att.ne.jp>.
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pamcut: don't crash when left > right or top > bottom with
-              -pad.  Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              -pad.  Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pgmhist: arbitrary output when total pixels doesn't fit in an
               integer.
@@ -1056,13 +1865,13 @@ CHANGE HISTORY
 
               pamtosvg: remove "needed exchange" debug trace.
 
-              Add pbmminkowski (source code has been in package since 10.12
-              but not documented or built by default).
+              Add pbmminkowski (source code has been in package since Netpbm
+              10.12 but not documented or built by default).
 
               pnmmontage: don't corrupt file names when they contain
               colons.
 
-              pamflip: fix bug: garbage output for PBMs, since 10.42.
+              pamflip: fix bug: garbage output for PBMs, since Netpbm 10.42.
 
               pngtopnm: fix gamma correction.
 
@@ -1105,7 +1914,7 @@ CHANGE HISTORY
               pamtohtmltbl: fix output error: contains holes due to
               uninitialized memory.
 
-              xvminitoppm: fix.  Totally broken since 10.34.
+              xvminitoppm: fix.  Totally broken since Netpbm 10.34.
 
               pnmtopclxl: fix crash with Standard Input input.
 
@@ -1121,7 +1930,7 @@ CHANGE HISTORY
               height, width.
 
               Add back ppmd_fill_init() for backward compatibility;
-              removed in 10.29.
+              removed in Netpbm 10.29.
 
               Build: make it work with Gcc 4.3 and -sse.
 
@@ -1137,9 +1946,11 @@ CHANGE HISTORY
               tag).
 
               pbmtext: fail cleanly if -width, -space, or -lspace is
-              too large for computation.  Thanks Prophet of the Way
+              too large for computation.  Thanks Akira F Urushibata
               <afu@wta.att.ne.jp>.
 
+              pnmmargin: don't crash with zero margin request.
+
               pnmtile: deal with zero width/height.
 
               pbmtext: fix negative -space.
@@ -1176,8 +1987,8 @@ CHANGE HISTORY
               
 07.12.27 BJH  Release 10.41.00
 
-              pamenlarge: much faster for PBM.  Thanks Prophet of the
-              Way <afu@wta.att.ne.jp>.
+              pamenlarge: much faster for PBM.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
 
               pamenlarge: remove arithmetic overflow.
 
@@ -1239,14 +2050,14 @@ CHANGE HISTORY
               <pebolle@tiscali.nl>.
 
               pnmmargin: add -plain option.    Thanks
-              Prophet of the Way <afu@wta.att.ne.jp>.
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pnmgamma: improve error messages.
 
               pamstack: accept Standard Input properly.
 
               pnmmargin: recognize invalid options better.  Thanks
-              Prophet of the Way <afu@wta.att.ne.jp>.
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
               anytopnm, pnmmargin, pamstretch-gen, ppmquantall: fix
               small temporary file security exposure.
@@ -1339,7 +2150,7 @@ CHANGE HISTORY
               Add pamfixtrunc.
 
               pamtogif: Add -aspect.  Thanks
-              Prophet of the Way <afu@wta.att.ne.jp>.
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pamditherbw: Add -atkinson.
 
@@ -1365,11 +2176,11 @@ CHANGE HISTORY
               misinterpretations.
 
               pamtogif: Speed up for monochrome images.  Thanks
-              Prophet of the Way <afu@wta.att.ne.jp>.
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pamtogif: Speed up for small images by using smaller
               hash table (so smaller memory footprint).  Thanks
-              Prophet of the Way <afu@wta.att.ne.jp>.
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
               libnetpbm: add pm_drain().
               
@@ -1443,21 +2254,21 @@ CHANGE HISTORY
               -interlace.
 
               pbmtoxbm: add -name option.
-              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
               ppmshift, ppmspread, ppmforge, pgmnoise, pgmcrater: better
               randomization; won't produce the same image if you run it
               twice within the same second.
 
               pbmtoxbm: Use packed PBM functions for efficiency.
-              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
               
               xbmtopbm: Use packed PBM functions for efficiency.
-              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
               cmuwmtopbm, mgrtopbm, pbmtocmuwm, pbmtoicon, pbmtomgr:
               Use packed PBM functions for efficiency.
-              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
               libnetpbm: add pnm_colorname().
 
@@ -1481,10 +2292,10 @@ CHANGE HISTORY
               rgb:0/0/0 style color name.
 
               pbmtoxbm, pbmtox10bm: merge.
-              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pbmtox10bm: Fix generation of name in XBM file.
-              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pbmtextps: Fix buffer overrun -- typical symptom: extra
               text generated.
@@ -1724,7 +2535,7 @@ CHANGE HISTORY
               Add pgmmake.
 
               bmptopnm: Understands RLE4/RLE8 compressed BMP.  Thanks
-              Prophet of the Way <afu@wta.att.ne.jp>.
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pnmgamma: Add -bt709tosrgb -srgbtobt709, -bt709tolinear,
               -lineartobt709, -gamma, -rgamma, -ggamma, -bgamma (gammma
@@ -1768,11 +2579,11 @@ CHANGE HISTORY
               being verbose.
 
               ppmtobmp, bmptopnm: major speed improvement for PBM.  Thanks
-              Prophet of the Way <afu@wta.att.ne.jp>.
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pbmtog3: Use new GCC features instead of assembly language
               for superspeed operations.  Thanks
-              Prophet of the Way <afu@wta.att.ne.jp>.
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pm_make_tmpfile(): Use TEMP and TMP environment variables if
               TMPDIR not set.
@@ -2090,7 +2901,7 @@ CHANGE HISTORY
               has pointer size != word size and uses the general case code.
 
               libnetpbm: fix basic pm_readlittleshort() bug introduced
-              in 10.27.  Affects mdatopbm, rawtopgm, lispmtopgm,
+              in 10.27 (March 2005).  Affects mdatopbm, rawtopgm, lispmtopgm,
               pcxtoppm, winicontoppm, bmptopnm, sirtopnm, xwdtopnm,
               cameratopam.
 
@@ -2135,8 +2946,9 @@ CHANGE HISTORY
               ppmtompeg: fix insecure temp file.  Thanks Alexey
               Tourbin <at@altlinux.ru>.
 
-              libnetpbm: fix basic readlittlelong() bug introduced in 10.27.
-              Affects xwdtopnm, bmptopnm, winicontoppm, lispmtopgm.
+              libnetpbm: fix basic readlittlelong() bug introduced in 10.27
+              (March 2005).  Affects xwdtopnm, bmptopnm, winicontoppm,
+              lispmtopgm.
 
               pbmtext/libnetpbm: fix crash with -builtin=fixed.
 
@@ -2429,37 +3241,34 @@ CHANGE HISTORY
 
               pamtopnm: accept extra planes (ignore them).
 
-              pamcut: major speedup.  Thanks Prophet of the Way
-              <afu@wta.att.ne.jp> (Akira Urushibata ("Douso")).
+              pamcut: major speedup.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>
 
               libnetpbm: Add pnm_getopacity().
 
               libnetpbm: Add pnm_applyopacityrown(), pnm_unapplyopacityrown().
 
               libnetpbm: "pam" read and write routines much more
-              efficient.  Thanks Prophet of the Way
-              <afu@wta.att.ne.jp> (Akira Urushibata ("Douso")).
+              efficient.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>
 
               Add pnm_formatpamrow(), pnm_allocrowimage(),
-              pnm_freerowimage().  Thanks Thanks Prophet of the Way
-              <afu@wta.att.ne.jp> (Akira Urushibata ("Douso")).
+              pnm_freerowimage().  Thanks Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>
 
               pnm_readpamrow(): Add option to have "tuplerow" argument
               NULL and not get the row's contents.  Thanks Thanks
-              Prophet of the Way <afu@wta.att.ne.jp> (Akira Urushibata
-              ("Douso")).
+              Akira F Urushibata <afu@wta.att.ne.jp>
 
-              Add pnm_writepamrowmult().  Thanks Thanks Prophet of the
-              Way <afu@wta.att.ne.jp> (Akira Urushibata ("Douso")).
+              Add pnm_writepamrowmult().  Thanks Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
 
               libnetpbm: format plain format images more nicely.
-              Thanks Thanks Prophet of the Way <afu@wta.att.ne.jp>
-              (Akira Urushibata ("Douso")).  
+              Thanks Thanks Akira F Urushibata <afu@wta.att.ne.jp>
 
               pamcut: fix bug: Blows up instead of failing cleanly
               when you try to cut beyond the bounds of the image.
-              Thanks Thanks Prophet of the Way <afu@wta.att.ne.jp>
-              (Akira Urushibata ("Douso")).
+              Thanks Thanks Akira F Urushibata <afu@wta.att.ne.jp>
 
               fix bug: MMX/SSE fast PBM writing (with gcc -msse) all wrong.
 
@@ -2469,7 +3278,7 @@ CHANGE HISTORY
               to enlarge.
 
               Speed up pbm_writepbmrow() (and all PBM output programs)
-              by going a byte at a time.  Thanks Prophet of the Way
+              by going a byte at a time.  Thanks Akira F Urushibata
               <afu@wta.att.ne.jp>.
 
               pamperspective: fix bug that can cause memory corruption
@@ -2500,11 +3309,11 @@ CHANGE HISTORY
               <esr@thyrsus.com>.
 
               pamflip: Large speed, memory improvements for
-              non-diagonal flips.  Thanks Prophet of the Way
+              non-diagonal flips.  Thanks Akira F Urushibata
               <afu@wta.att.ne.jp>.
 
               jbigtopnm, pnmtojbig: Use packed PBM functions to speed up
-              greatly.  Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              greatly.  Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
               g3topbm: Don't fail on premature EOF; produce partial
               output instead.
@@ -2571,13 +3380,13 @@ CHANGE HISTORY
               maketuplergb(), makerowrgb(), makearrayrgb().
 
               pnminvert: much faster for PBMs.  Thanks 
-              Prophet of the Way <afu@wta.att.ne.jp>.
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
-              pbmmake: use packed bits to speed up.  Thanks Prophet of
-              the Way <afu@wta.att.ne.jp> (Akira Urushibata ("Douso")).
+              pbmmake: use packed bits to speed up.  Thanks
+              Akira F Urushibata <afu@wta.att.ne.jp>.
 
-              pbmtog3: speedups.  Thanks Prophet of the Way
-              <afu@wta.att.ne.jp> (Akira Urushibata ("Douso")).
+              pbmtog3: speedups.  Thanks Akira F Urushibata
+              <afu@wta.att.ne.jp>.
 
               Fix gamma value (from .45 to 2.2) in pm_gamma709(), 
               pm_ungamma709().
@@ -2708,7 +3517,7 @@ CHANGE HISTORY
               Thanks David Jones <drj@pobox.com>.
 
               pbmtog3: fix buffer overrun when image > 1728 columns.
-              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              Thanks Akira F Urushibata <afu@wta.att.ne.jp>.
 
               pnmsvgalib: Correct error message - too wide -> too tall.
 
@@ -2819,7 +3628,7 @@ CHANGE HISTORY
               rgb.txt: move D65 to the end, so "white" is preferred.
 
               xwdtopnm: change interpretation of bitmap_pad and bitmap_unit
-              to accomodate Xfree86 direct color 24 bit xwd.
+              to accommodate Xfree86 direct color 24 bit xwd.
 
               pbmtextps: fix bug where intermediate file gets truncated.
 
@@ -2984,7 +3793,7 @@ CHANGE HISTORY
 
               pnmtojpeg: Add -density option.
 
-              pamtotga: Add alpha capability, via "RGBA" tuple type.
+              pamtotga: Fix bugs with images with alpha planes.
 
               libnetpbm: Add ppm_readcolornamefile().
 
@@ -3318,9 +4127,17 @@ CHANGE HISTORY
               pnmmontage: add -data option.  Thanks Ben
               <bem@mad.scientist.com>.
 
-              pnmtotga: put "image ID" in TGA output.
+              pamtotga: Add alpha capability, via "RGBA" tuple type.
+
+              pamtotga: put "image ID" in TGA output.
+
+              ppmtotga: take PAM input; rename dot 'pamtotga'.  Former name
+              was a misnomer anyway; it was always a PNM program because it
+              distinguished between PGM and PPM input.
+
+              pamtotga: Add alpha capability, via "RGBA" tuple type.
 
-              ppmtotga: Correct name to pnmtotga.
+              pamtotga: put "image ID" in TGA output.
 
               pnmcomp:  Add -opacity option.
 
@@ -3389,7 +4206,7 @@ CHANGE HISTORY
 
               pnmtopng: fix -hist option.
 
-              Cygwin build accomodations.  Thanks Charles Wilson 
+              Cygwin build accommodations.  Thanks Charles Wilson 
               <cwilson@ece.gatech.edu>.
 
               Fix ppmtompeg build failure when JPEGLIB = NONE
@@ -3649,7 +4466,8 @@ CHANGE HISTORY
 
               pnmtops: Accept maxval > 255 input.
          
-              Rename pnminterp to the more informative pnmstretch.
+              Rename pnminterp to the more informative pnmstretch and
+              pnminterp-gen to pamstretch-gen.
 
               pnmstretch: convert from pnm to pam - pamstretch.
 
@@ -3868,7 +4686,7 @@ CHANGE HISTORY
               this.  pnm_readpnmrow() changed to do same.  external data
               symbols like this do not work with Mingw.
          
-              various changes to accomodate Mingw (native Windows)
+              various changes to accommodate Mingw (native Windows)
               and DLLs with Cygwin (Windows).
 
               eyuvtoppm: rewrite.  Uses Netpbm libraries now.
@@ -4240,7 +5058,7 @@ CHANGE HISTORY
 
               stamp-date doesn't rely on whoami.
 
-              Make file fixes to accomodate more install programs.
+              Make file fixes to accommodate more install programs.
          
               Replace tmpnam() with mkstemp().
 
@@ -4837,7 +5655,7 @@ Changes since the comp.sources.misc distribution of 31oct88:
     Moved to the new PNM package: pbmcrop pbmcut pbmenlarge pbminvert.
     Consolidated into a single pnmflip tool: pbmfliplr pbmfliptb pbmtrnspos.
     Consolidated into a single pnmcat tool: pbmcatlr pbmcattb.
-    Added compataliases script for upward compatability with changed tools.
+    Added compataliases script for upward compatibility with changed tools.
     Removed xxxtopbm.
     Added a -headersize flag to macptopbm, to help get around annoying
       problems in MacPaint file format.
diff --git a/doc/INSTALL b/doc/INSTALL
index ec2cfbed..f0b3e87b 100644
--- a/doc/INSTALL
+++ b/doc/INSTALL
@@ -20,10 +20,16 @@ finally
 
 to install it into your system.  
 
-If you're building Latest Netpbm (as opposed to Stable Netpbm), there
-are probably known bugs.  Some may even prevent Netpbm from building.
-These are listed in the release notes for the release on Sourceforge.
-Check it out.
+If you have a Debian-like system, that uses Dpkg for package management,
+it's better to create a Debian package file and install it as follows in
+place of the 'installnetpbm' step above.
+
+    make deb
+    dpkg --install netpbm-sf-*.deb
+
+More information on building and installing on Debian are in the file
+buildtools/debian/README in the source tree.
+
 
 The 'configure' program is not GNU Autoconf -- it is a simple program
 specific to Netpbm that prompts you for information about your system.
@@ -52,6 +58,57 @@ and it will attempt to build whatever it hasn't successfully built
 yet.
 
 
+CHECKING THE BUILD
+------------------
+
+The package comes with a test facility, which you can run after packaging,
+against the package you created.  The typical sequence is
+
+    make
+
+    make package
+
+    make check
+
+    ./installnetpbm
+
+To capture all the messages from "make check" do:
+
+    make check > test.log  2>&1
+
+The test facility was new in Netpbm 10.61 (December 2012).
+
+For further information on the tests read the document TESTS.
+
+
+OVERRIDING INTERNAL LIBRARIES
+-----------------------------
+
+There are a few esoteric libraries that are distributed separately from Netpbm
+but of which Netpbm contains a copy too, for convenience.  The build normally
+uses the internal Netpbm copy, but you can configure the build to use a
+version of the library that is installed on your system.
+
+For the JBIG library, set the JBIGLIB and JBIGHDR_DIR variables in
+config.mk to the appropriate values.
+
+For the Utah Raster Toolkit (aka URT aka RLE) library, set the URTLIB and
+URTHDR_DIR vairables in config.mk to the appropriate values.
+
+The appropriate value for the xxxLIB variable is the full file name of the
+link library (e.g. "/usr/local/lib/jbigkit/libjbig.so") or just the file name
+part of it (e.g. "libjbig.so") if the library is in your linker's default
+search path.
+
+The appropriate value for the xxxHDR_DIR variable is the full directory name
+of the directory that contains the interface header files for the library
+identified by the xxxLIB variable (E.g. "usr/local/include/jbigkit") or a null
+string if those header files are in your compiler's default search path.
+
+If you use the 'configure' program, be sure to edit config.mk _after_ you
+run 'configure', since 'configure' generates config.mk.
+
+
 AUTOMATING THE BUILD
 --------------------
 
@@ -133,7 +190,7 @@ Search for your platform name (Solaris, SunOS, NetBSD, Cygwin, BeOS,
 and Tru64 are among those mentioned) to see recommended settings for
 your platform.
 
-If your system is even too exotic to accomodate with settings in
+If your system is even too exotic to accommodate with settings in
 config.mk, you may need to modify things in the main make files
 or pm_config.h.in.
 
@@ -141,6 +198,32 @@ If you figure out how to install on other platforms, contact the
 Netpbm maintainer to have the 'configure' program or these
 instructions improved for the next person.
 
+If you want to use a compiler other than your system default, set
+the CC value in config.mk to the shell command name for your compiler,
+e.g.
+
+  CC=clang-3
+
+You can also override it on the Make command line, which is especially
+useful if you want to build some parts with one compiler and other parts
+with another compiler:
+
+  $ make pamflip CC=clang-3
+
+To get appropriate defaults when you run 'configure', you can also set what
+compiler Configure assumes you will be using to build, with an environment
+variable:
+
+  $ CC=clang-3 ./configure
+
+Likewise, you can add compiler flags with a CFLAGS make variable, either set
+in config.mk or on the make command line and you can get better get more
+appropriate defaults from Configure if you pass the same CFLAGS to Configure
+via environment variable.  LDFLAGS (linker options) and CPPFLAGS (C
+preprocessor options) are similar.
+
+  $ make CFLAGS=-O0
+
 
 CUSTOM INSTALLATION
 -------------------
@@ -259,10 +342,11 @@ and which one is default is controlled by the DEFAULT_TARGET make
 variable in config.mk, and its value is one of the choices you
 make via the Configure dialog.
 
-The standard build is the conventional one.  The merge build is a way
-to reduce disk space and other resource usage in some configurations.
-These are rare configurations, mostly antique ones.  The advent of
-shared libraries largely obsoleted the merge build.
+The standard build is the conventional one.  The merge build is a way to
+reduce disk space and other resource usage in some configurations.  These are
+rare configurations, mostly antique ones.  The advent of shared libraries
+largely obsoleted the merge build.  In some configurations, Netpbm uses _more_
+resources with the merge build.
 
 In the standard build, hundreds of separate programs get built: ppmtogif,
 pamcomp, etc.
@@ -288,3 +372,61 @@ DOCUMENTATION
 
 Documentation is not packaged with the program source code.  See the
 file doc/USERDOC for information on installing documentation.
+
+
+COMMON PROBLEMS
+---------------
+
+Improper -config files
+----------------------
+
+The most common problem building Netpbm is one of improperly installed
+prerequisite libraries, such as Libpng.  Such a library is designed to be
+installed along with a -config program (e.g. libpng-config) that tells
+builders of dependent packages (such as Netpbm) how to use it.  When the
+-config program is wrong, you get Netpbm build failures with messages about
+undefined references.
+
+The exact nature of the problems with -config programs can be quite
+involved, especially since there is no guarantee that a -config
+program can do what's required of it in every situation.  But I'll
+explain the basic problem.  For simplicity, I'll talk specifically
+about Libpng, but the principles apply to any library that has a -config
+program.
+
+The point of libpng-config is to describe how Libpng is installed on your
+particular system.  You have choices of where to install the various parts
+and what prerequisites to build into them, and libpng-config is how you
+communicate those choices to the Netpbm make files.
+
+Libpng's builder automatically creates a libpng-config program for you,
+but you should not think of it as part of Libpng.  It's really a
+configuration file -- something that tells how your particular system
+is put together.  The Libpng builder is not smart enough to know exactly
+what to put in libpng-config; it just builds one that works for most
+people.  The local system administrator is actually responsible for
+the contents of libpng-config.
+
+One rather complex way in which the libpng-config that the Libpng builder
+builds can be wrong is that it often indicates that to link to the
+Libpng library, you need a "-L /usr/lib" option (or something like that
+-- an option that adds to the linker's search path a directory that is
+already in it).  This is usually unnecessary because the directory is
+already in the search path, and often breaks things because it puts
+the directory too early in the search path.  If your libpng-config says to
+link with -L /usr/lib, you should normally edit it to remove that.
+
+As an example of how -L /usr/lib breaks things, here is a problem that is
+often reported: The user has Netpbm installed on his system, but wants to
+build a new one to replace it, or to use for a particular project instead of
+the system version.  But the build of the new version fails with undefined
+references to symbol "pm_foo".  pm_foo is a new symbol - it was added to
+Netpbm in a recent release.  The version of Netpbm installed on the system is
+too old to have it.  The make file obviously specifies the path to the current
+libraries that the user just built in the link library search order, but the
+link is picking up the old system version instead.  Why?  Because the link
+options say to search /usr/lib _before_ the local build directory.  And they
+do that because libpng-config erroneously says that you need a -L /usr/lib
+link option to find the Libpng library.
+
+
diff --git a/doc/Netpbm.programming b/doc/Netpbm.programming
index 14de8b08..c4d38ed4 100644
--- a/doc/Netpbm.programming
+++ b/doc/Netpbm.programming
@@ -48,23 +48,31 @@ DEVELOPMENT PROCESS
 -------------------
 
 Just email your code to the Netpbm maintainer.  Base it on the most recent
-Latest Netpbm release if possible.  It's a good idea to ask the maintainer
-before starting a big change to a file to make sure it doesn't conflict
-with some other work that has been done since the last release.
+Netpbm Development release if possible.
 
 The preferred form for changes to existing files is a unified diff patch --
-the conventional Unix means of communicating code changes.
+the conventional Unix means of communicating code changes.  A Subversion
+'svn diff' creates that easily.
 
-You should update or create documentation too.  The source files for
-the documentation are the HTML files at
-http://netpbm.sourceforge.net.doc/ .  But if you don't, the Netpbm
-maintainer will do the update before releasing your code.
+You should update or create documentation too.  But if you don't, the Netpbm
+maintainer will do the update before releasing your code.  The source files
+for the documentation are the HTML files in the 'userguide' directory of the
+Netpbm Subversion repository: https://svn.code.sf.net/p/netpbm/code/userguide.
+The identical files are at http://netpbm.sourceforge.net/doc/ .
 
 There are some automated tests in the package - shell scripts in files
 named such as "pbmtog3.test".  You can use those to verify your
 changes.  You should also add to these tests and create new ones.  But
 most developers don't.
 
+As you code and test, do 'make dep' to create the header file dependency files
+(depend.mk).  Do it after 'configure' and every time you change the
+dependencies.  If you don't, 'make' won't know to recompile things when you
+change the header files.  For the convenience of users who are just building,
+and not changing anything, the make files do work without a 'make dep', but do
+so by automatically generating empty depend.mk files.  This is not what you
+want as a developer.
+
 
 CODING GUIDELINES
 -----------------
@@ -88,40 +96,14 @@ before you read these.
   but don't depend very much on these; use the library functions
   instead to read and write the image files.
 
-* Your new program must belong to one of the four Netpbm classes,
-  which are loosely based on the Netpbm formats.  They are defined as
-  follows:
-
-  pbm: These programs take PBM (bitmap - pixels are black or white)
-       files as input or output or both.  They never have PGM or PPM 
-       primary input or output.  They use the libpbm Netpbm
-       library.  They have "pbm" in their names.
-
-  pgm: These programs take PBM or PGM (grayscale) files as input, or
-       produce PGM files as output, or both.  They treat PBM input
-       as if it were the equivalent PGM input.  They never produce PBM
-       primary output and never have PPM primary input or output.
-       They use the libpbm and libpgm Netpbm libraries.  They have
-       "pgm" in their names.
-
-  ppm: These programs take PBM or PGM or PPM (color) files as input,
-       or produce PPM files as output, or both.  They treat PBM and
-       PGM input as if it were the equivalent PPM input.  They never
-       produce PBM or PGM primary output.  They use the libpbm,
-       libpgm, and libppm Netpbm libraries.  They have "ppm" in their
-       names.
-
-  pnm: These are the most general programs.  They handle all four
-       Netpbm formats (PBM, PGM, PPM, and PAM).  They use all Netpbm
-       formats as input or output or both.  They recognize the
-       difference between PBM, PGM, and PPM input, so a PBM input
-       might produce a different result than the equivalent PGM input.
-       These programs use the libpbm, libpgm, libppm, and libpnm
-       Netpbm libraries.  They have "pnm" or "pam" in their names.
-
-  Decide which one of these classes your program belongs to.  Your choice
-  determines the proper naming of the program and which set of library
-  subroutines you should use.
+* You should try to use the "pam" library functions, which are newer than the
+  "pbm", "pgm", and "pnm" ones.  The "pam" functions read and write all four
+  Netpbm formats and make code easier to write and to read.
+
+* A new program should generate PAM output.  Note that any program that uses
+  the modern Netpbm library can read PAM even if it was designed for the older
+  formats.  For other programs, one can convert PAM to the older formats
+  with 'pamtopnm'.
 
 * If your program involves transparency (alpha masks), you have two
   alternatives.  The older method is to use a separate PGM file for the
@@ -137,10 +119,7 @@ before you read these.
   cause problems to other programs when you do a "merge build" of 
   Netpbm.
 
-* Declare main() with return type 'int', not 'void'.  Some systems won't
-  compile void main().
-
-* Always start the code in main() with a call to p?m_init().
+* Always start the code in main() with a call to pm_proginit().
 
 * Use shhopt for option processing.  i.e. call optParseOptions3().
   This is really easy if you just copy the parseCommandLine() function
@@ -193,8 +172,7 @@ before you read these.
   makes samples fit in a single byte and at one time was the maximum 
   possible maxval in the format.
 
-  Note that the Pnmdepth program converts an image from one maxval to
-  another.
+  Note that the Pamdepth program converts an image from one maxval to another.
 
 * Don't include extra function.  If you can already do something by 
   piping the input or output of your program through another Netpbm
@@ -203,11 +181,14 @@ before you read these.
 
   Similarly, if your program does two things which would be useful
   separately, write two programs and advise users to pipe them
-  together.  Or add a third program that simply calls the first two.
+  together.  Or add a third program that simply runs the first two.
 
 * Your program should, if appropriate, allow the user to use Standard
   Input and Output for images.  This is because Netpbm users commonly
-  use pipes.
+  use pipes.  As an alternative to Standard Input, the user should be able
+  to name the input file as a non-option program argument.  But the user
+  should not be able to name the output file if Standard Output is
+  feasible.  That's just the Netpbm tradition.
 
 * Don't forget to write a proper html documentation page.  Get an
   example to use as a template from
@@ -223,6 +204,37 @@ before you read these.
   the other hand, look the same for everyone.  Modern editors let you
   compose code just as easily with spaces as with tabs.
 
+* Limit your C code to original ANSI C.  Not C99.  Not C89.  Not C++.  Don't
+  use // for comments or put declarations in the interior of a block.
+  Interpreted languages are OK, with Perl being preferred.
+
+* Make output invariant with input.  The program should produce the same output
+  when fed the same input.  This helps with testing and also reduces the chance
+  of bugs being encountered randomly.
+
+  This is an issue when there are "don't care" bits in the output.  You should
+  normally make those zero.
+
+  The valgrind utility helps you identify instances of undefined bits and
+  bytes getting to output.
+
+  A common place to find "don't care" bits is padding at the end of a raster
+  row.  You can use pbm_cleanrowend_packed() to clear padding bits when you
+  use pbm_writepbmrow_packed().
+
+  Some programs are designed to produce random output.  For those, include a
+  -seed option to specify the seed of the random number generator so that a
+  tester can make the output predictable.
+
+
+* Never assume that input will be valid; make sure your program can
+  handle corrupt input gracefully.
+
+  Watch out for arithmetic overflows caused by large numbers in the input.
+
+  Be careful when writing decoders.  Make sure that corruptions in the
+  input image do not cause buffer overruns.
+
 
 
 
@@ -232,6 +244,9 @@ DISCONTINUED CODING GUIDELINES
 Here are some things you will see in old Netpbm programs, but they are
 obsolete and you shouldn't propagate them into a new program:
 
+* Use of pbm_init(), pgm_init(), ppm_init(), and pnm_init().  We use
+  pm_proginit() now.
+
 * Tolerating non-standard C libraries.  You may assume all users have
   ANSI and POSIX compliant C libraries.  E.g. use strrchr() and forget
   about rindex().
@@ -317,9 +332,11 @@ code.  Modular and structured above all.
   Especially never modify the argument of a function.  All function arguments
   should be "const".
 
-  Don't use initializers except for constants.  If you're going to set 
-  a variable twice, do it out in the open; don't hide one in the declaration
-  of the variable.
+  Don't use initializers except for with constants.  The first value a
+  variable has is a property of the algorithm that uses the variable, not of
+  the variable.  A reader expects to see the whole algorithm in one place, as
+  opposed to having part of it hidden in the declaration of the algorithm
+  variables.
 
 * Avoid global variables.  Sometimes, a value is truly global and
   passing it as a parameter just muddies the code.  But most of the
@@ -328,6 +345,10 @@ code.  Modular and structured above all.
 
   Declare a variable in the most local scope possible.
 
+  Global constants are OK.
+
+* Do not use static variables, except for global constants.
+
 * Keep subroutines small.  Generally under 50 lines.  But if the
   routine is a long sequence of simple, similar things, it's OK for it
   run on ad infinitem.
@@ -335,18 +356,22 @@ code.  Modular and structured above all.
 * Use the type "bool" for boolean variables.  "bool" is defined in Netpbm
   headers.  Use TRUE and FALSE as its values.
 
-* Do not say "if (a)" when you mean "if (a != 0)".  Use "if (a)" only if
-  a is a boolean variable.  Or where it's defined so that a zero value 
-  means ("doesn't exist").
+* Do not say "if (a)" when you mean "if (a != 0)".  Use "if (a)" only if a is
+  a boolean variable.  Or where it's defined so that zero is a special value
+  that means "doesn't exist".
+
+* Do multiword variable names in camel case: "multiWordName".  Underscores
+  waste valuable screen real estate.
 
-* Do multiword variable names like this:  "multiWordName".  Underscores waste
-  valuable screen real estate.
+* If a variable's value is a pointer to something, it's name should reflect
+  that, as opposed to lying and saying the value is the thing pointed to.  A
+  "P" on the end is the conventional way to say "pointer to."  E.g. if a
+  variable's value is a pointer to a color value, name it "colorP", not
+  "color".
 
-* If a variable's value is the address of something (a pointer to something),
-  it's name should reflect that, as opposed to lying and saying the value is
-  the thing pointed to.  A "P" on the end is the conventional way to say
-  "address of."  E.g. if a variable's value is the address of a color value,
-  name it "colorP", not "color".
+* A variable that represents the number of widgets should be "widgetCt",
+  ("widget count"), not "widgets".  The latter should represent the actual
+  widgets instead.
 
 * Put "const" as close as possible to the thing that is constant.
   "int const width", not "const int width".  When pointers to
@@ -355,4 +380,46 @@ code.  Modular and structured above all.
 * Free something in the same subroutine that allocates it.  Have exactly 
   one free per allocate (this comes naturally if you eliminate gotos).
 
+* Dynamically allocate buffers, for example raster buffers, rather than use
+  automatic variables.  Accidentally overrunning an automatic variable,
+  typically stored on the stack, is much more dangerous than overrunning a
+  variable stored in the heap.
+
+* Use pm_asprintf() to compose strings, instead of sprintf(), strcat(), and
+  strcpy().  pm_asprintf() is essentially the same as GNU asprintf(), i.e.
+  sprintf(), except it dynamically allocates the result memory.  This
+  effortlessly makes it impossible to overrun the result buffer.  Use
+  pm_strfree() to free the result memory.  You usually need not worry about
+  the pathological case that there is no memory available for the result,
+  because in that case, pm_asprintf() returns a constant string "OUT OF MEMORY"
+  and in most cases, that won't cause a disaster - just incorrect behavior that
+  is reasonable in the face of such a pathological situation.
+
+* Do not use the "register" qualifier of a variable.
+
+
+MISCELLANEOUS
+-------------
+
+Object code immutability across compilations
+--------------------------------------------
+
+In a normal build process the same source code always produces the same object
+code.  (Of course this depends on your compiler and its settings: if your
+compiler writes time stamps into the objects, they will naturally be different
+with each compilation.)
+
+The file lib/compile.h has a time stamp which gets written into libnetpbm.a
+and libnetpbm.so .  This will affect individual binary executables if they are
+statically linked.
+
+If compile.h does not exist it will be created by the program
+buildtools/stamp-date.  Once created, compile.h will stay unchanged until you
+do "make clean".
+
+If you make alterations to source which are entirely cosmetic (for example,
+changes to comments or amount of indentation) and need to confirm that the
+actual program logic is not affected, compare the object files.  If you need
+to compare library files (or for some reason, statically linked binaries) make
+sure that the same compile.h is used throughout.
 
diff --git a/doc/TESTS b/doc/TESTS
new file mode 100644
index 00000000..65f91d26
--- /dev/null
+++ b/doc/TESTS
@@ -0,0 +1,362 @@
+Contents
+========
+
+1. Running the tests
+  1.1 Standard test procedure
+  1.2 Summary report
+  1.3 Prerequisites
+  1.4 Repeatability
+  1.5 Execution time
+  1.6 Testing package in designated directory
+  1.7 Pre-packaging check
+  1.8 Post-install check
+  1.9 Skipping test items
+  1.10 Valgrind
+  
+2. Troubleshooting
+  2.1 Missing programs
+  2.2 Broken programs
+  2.3 Color name file
+  2.4 Multiple versions
+  2.5 System dependent issues
+
+3. Reporting test failures
+
+------------------------------------------------------------------------------
+
+1. Running the tests
+====================
+
+1.1 Standard test procedure
+===========================
+
+The recommended method of running the tests is after packaging:
+
+   make
+   make package
+   make check
+
+To capture the output do:
+
+   make check 2>&1 | less
+
+Or:
+
+   make check > check.log  2>&1
+
+
+
+1.2 Summary report
+==================
+
+Like most other test routines, these produce much output.  A summary will
+appear at the end:
+
+  Test summary:
+  ==================
+  SUCCESS 83
+  FAILURE 1
+  TOTAL TESTABLE 84
+  ==================
+  All tests done.
+  Sat, 08 Jun 2013 09:30:33 +0000
+  make: *** [check] Error 1
+
+
+
+1.3 Prerequisites
+=================
+
+The tests require the Bash command shell.  The script Execute-Tests has
+some expressions unique to bash.  Quite old versions work, at least back
+to bash v. 2.05b.
+
+The tests also use the following utilities:
+
+ - sh
+ - awk
+ - perl
+
+ - cat
+ - cksum
+ - cmp
+ - cp
+ - cut
+ - date
+ - dirname
+ - egrep
+ - fgrep
+ - grep
+ - file
+ - head
+ - mkdir
+ - mktemp
+ - rm
+ - sed
+ - seq
+ - tee
+ - tr
+ - uniq
+
+
+
+1.4 Repeatability
+=================
+
+The current version of the test package produces identical results if
+you run "make check" repeatedly.  The tests contain no random elements;
+some Netpbm programs use randomness internally, but the tests seed
+their random number generators with fixed values so that they too have
+repeatable results.
+
+
+
+1.5 Execution time
+==================
+
+Currently "make check" takes no more time to execute than "make package",
+and much less than "make".
+
+
+
+1.6 Testing package in designated directory
+===========================================
+
+If you specify the package directory when you do "make package",
+you must do the same with "make check":
+
+   make
+   make package pkgdir=/tmp/package
+   make check pkgdir=/tmp/package
+
+
+
+1.7 Pre-packaging check
+=======================
+
+You can run the tests to check executables after compilation, before
+packaging.  This feature is intended for developers.
+
+   make check-tree
+
+This test method is incompatible with merge build.
+
+Currently this test method reports several errors when Netpbm is compiled in a
+separate build directory.
+
+
+
+1.8 Post-install check
+======================
+
+You can run the tests after installation.  Run this way, the tests are of
+programs in the default search path.
+
+  make check-install
+
+Make sure to set RGBDEF if the color dictionary file rgb.txt is in
+a non-standard location.  This must be an absolute path.  
+
+  RGBDEF=/etc/colors/rgb.txt make check-install
+
+
+
+1.9 Skipping test items
+=======================
+
+The file test/Test-Order is a list of tests which are run.  If you want to
+skip any test, remove the line or comment it out with a "#".
+
+The variable "target", a comma-separated list of Netpbm programs
+provides another way to run only select tests.  For example to run
+only the test scripts which examine giftopnm and pamtogif, do:
+
+  make check target=giftopnm,pamtogif
+
+
+
+1.10 Valgrind
+============
+
+You can run the whole test under Valgrind.  This is an advanced feature
+intended for programmers who work on Netpbm code.
+
+To turn on Valgrind, set VALGRIND_TESTS to "on":
+
+  make check VALGRIND_TESTS="on"
+
+Valgrind version 3.6.0 or newer is required.  For information on
+valgrind, visit http://www.valgrind.org/ .
+
+Valgrind results are output to files, one per process in the directory
+/tmp/netpbm-test/valgrind .  The file name consists of the test script
+name, process ID and the suffix ".vout", e.g.: "ppmmake.18420.vout" .
+
+Valgrind errors are not reported in the summary report and do not
+influence the success/failure count in any way.  The following awk
+one-liner will report ".vout" files with a positive error count in
+the ERROR SUMMARY line:
+
+  awk '/ERROR SUMMARY/ && $4>0 {print FILENAME}' \
+       /tmp/netpbm-test/valgrind/*.vout  
+
+
+You can add or alter valgrind options by editing this line in
+test/Execute-Tests:
+
+  vg_command_base="valgrind --trace-children=yes"
+
+To run "valgrind --track-origins=yes", you must make two changes in
+config.mk:
+
+  - Add -g to CFLAGS
+  - Turn stripping off: STRIPFLAG =
+
+Valgrind significantly increases execution time.  If ordinary
+"make check" requires 10 seconds, "make check VALGRIND_TESTS=on"
+will require roughly 12 minutes, maybe more.  You should consider
+either setting "target=..." or paring down the items in Test-Order.
+In the latter case, you probably don't need to run "all-in-place.test"
+and "legacy-names.test".
+
+The option "--trace-children-skip" is used to prevent valgrind from
+stepping into child processes that are not relevant.  This option
+appears in valgrind v. 3.6.0.  If you wish to conduct valgrind tests
+with an older version, comment out the line in Execute-Tests with
+"--trace-children-skip".
+
+
+
+2. Troubleshooting
+==================
+
+2.1 Missing programs
+====================
+
+The first two tests run, "all-in-place.test" and "legacy-names.test"
+detect missing programs.
+
+If you work around a build glitch with "make --keep-going" you
+will get a few errors here.
+
+A wholesale failure with "all-in-place.test" indicates a systemic
+problem, such as a misconfigured dynamic library or a directory-wide
+permission issue.  This kind of failure is known to happen when N is
+set too high with "make -jN" (overambitious parallel make.) 
+
+The current test routines assume a typical build configuration - they are
+not aware of the actual configuration you chose.  If a choice you make
+during configure causes "make" to skip compilation of certain programs,
+the test routines won't know and will report failures.
+
+For details read 'Netpbm Library Prerequisites':
+http://netpbm.sourceforge.net/prereq.html .
+
+
+
+2.2 Broken programs
+===================
+
+Broken programs will invariably lead to failures.  Certain programs
+(for instance, image generators 'pbmmake' and 'pgmmake') are used in
+numerous test scripts.  Problems in them will lead to multiple failures.
+
+To aid you in this situation each test routine lists the necessary programs
+near the top.
+
+Each test routine comes in two parts, a ".test" file which is a
+shell script and a ".ok" file which denotes its proper output.
+When a test fails, a ".out" file will be produced in the
+/tmp/netpbm-test/ directory.  By comparing ".ok" and ".out" you
+can tell exactly what went wrong.  Often one does not need to
+go this far; the error messages tell enough.
+
+
+
+2.3 Color dictionary file
+=========================
+
+If you get the following error message, it indicates a problem with
+the color dictionary file rgb.txt.
+
+  ppmmake: can't open color names dictionary file from the path '/usr/share/
+  netpbm/rgb.txt:/usr/lib/X11/rgb.txt:/usr/share/X11/rgb.txt:/usr/X11R6/lib/
+  X11/rgb.txt' and Environment variable RGBDEF not set.  Set RGBDEF to the
+  pathname of your rgb.txt file or don't use color names.
+
+This is highly unlikely to occur with "make check" right after packaging,
+but may appear in the post-installation check "make check-install".
+
+To check manually after installation, execute the following and see
+whether the proper output or the error message appears:
+
+   ppmmake red 1 1 -plain
+
+Proper output:
+
+   P3
+   1 1
+   255
+   255 0 0
+
+The simple remedy is properly setting the environment value RGBDEF to
+the location of rgb.txt.
+
+RGBDEF must be an absolute path.  The following will not work:
+
+  RGBDEF=./lib/rgb.txt make check-install
+
+If you want to hardcode the path, modify RGB_DB_PATH in pm_config.h
+and run "make" again.  Note that running "configure" will cause
+pm_config.h to be overwritten; changes by editing will be lost.
+
+
+
+2.4 Multiple versions
+=====================
+
+If multiple versions of Netpbm executables are installed on your
+system, you should do a post-installation check to ensure that
+the newly built version is in place and in working condition.
+
+The test routines can test binaries other than the intended
+target, for example pre-compiled binaries distributed in .rpm
+or .deb format.  If the default binary search path gives priority
+to a directory that contains programs from such a source, you should
+expect multiple failures due to missing features, etc. with
+"make check-install".
+
+Netpbm distributed with Debian or Ubuntu is called "Netpbm-Free" and
+is based on a fork which broke off in 2002.  There are many differences.
+Many tests will fail.  However, the test framework itself is valid for
+these distributions.  The following procedure will allow you to run
+the tests on installed Netpbm programs, regardless of the version:
+
+   ./configure           # accept the defaults
+   make check-install
+
+
+2.5 System dependent issues
+===========================
+
+The tests have worked on x86 and x86_64 GNU/Linux systems, with several
+versions of GCC and Clang.  Reports from users of other systems including Mac
+OS, Sun SPARC and BSD and compilers other than GCC are highly welcome.
+
+Floating point math precision seems to be an issue.  Some discrepancies
+have been observed between x86 32 bit and 64 bit; the tests are written to
+work around them as much as possible.  The use of the "--fast-math"
+flag by default may also be a factor.
+
+The current test framework checks whether the random number generator
+is the one from glibc and skips certain tests if a different one is
+detected.
+
+
+
+3. Reporting test failures
+==========================
+
+When reporting problems with the tests, please give both
+the output of "make check" and the contents of the "netpbm-test"
+directory.
diff --git a/doc/USERDOC b/doc/USERDOC
index d1da64e1..9d883849 100644
--- a/doc/USERDOC
+++ b/doc/USERDOC
@@ -39,6 +39,7 @@ requests (with an Internal Server Error indication and a message saying there
 had been too many requests) before all the files could be fetched.
 
 
+
 GETTING COMMAND HELP WITH A "MAN" COMMAND
 -----------------------------------------
 
@@ -119,9 +120,11 @@ Also, these methods require manual effort, and technical
 understanding, on your part to set up.  Setting it up is too complex
 for an automated process to do it for you with any significant
 integrity.  The examples are guidelines and you shouldn't expect them
-to work literally in your situtation.
+to work literally in your situation.
 
-Here is an example of making troff pages:
+Here is an old example of making troff pages, which doesn't actually
+work anymore, but might be helpful.  You'll probably want to use a Subversion
+export of the manual instead of a Wget download (see above).
 
   mkdir netpbmdoc
   cd netpbmdoc
@@ -129,7 +132,6 @@ Here is an example of making troff pages:
   cd netpbm.sourceforge.net/doc
   make MAKEMAN=/usr/src/netpbm/buildtools/makeman \
     -f /usr/src/netpbm/buildtools/manpage.mk manpages
-  make -f /usr/src/netpbm/buildtools/manpage.mk manpages
   make -f /usr/src/netpbm/buildtools/manpage.mk installman
   cd ../../..
   rm -r netpbmdoc
diff --git a/doc/patent_summary b/doc/patent_summary
new file mode 100644
index 00000000..b9623d09
--- /dev/null
+++ b/doc/patent_summary
@@ -0,0 +1,100 @@
+These are the patents the Netpbm maintainer knows about that relate to
+Netpbm.  It is basically just information the maintainer has stumbled
+over at some point -- no search has been done.
+
+No licenses have been granted by patent owners to the maintainer of
+Netpbm.  Therefore, if you need a patent to use something in Netpbm,
+you need your own license.
+
+A note about patents in general: A patent gives an inventor the
+exclusive right to make, sell, or use the invention.  If you
+independently invent something without knowing that the patent holder
+already did, that makes no difference -- the patent holder still has
+the exclusive right.  It makes no difference if you give the original
+inventor credit.  The patent applies to a method, not its expression,
+so writing a program from scratch to implement a certain method is
+still a patent infringement.  Infringing a patent is not a crime per
+se, but to the extent that it costs the patent holder money, the
+infringer has to make it up.
+
+The original purpose of patents is probably perverted when patents are
+applied to things you implement in computer programs.  This is one of
+the Free Software Foundation's causes.  See 
+<http://www.gnu.org/philosophy.html#laws>.
+
+The Jbigtopnm and Pnmtojbig programs use arithmetic coding patents and
+other patents covering various aspects of the "front end."
+
+
+JPEG patents
+------------
+
+The Pnmtojpeg and possibly Jpegtopnm programs in some cases may use
+the arithmetic coding patents owned by IBM, AT&T, and Mitsubishi.
+There is difference of opinion on whether they do.
+
+Forgent owns a patent it believes covers JPEG compression.  This
+patent was virtually unknown before July 2002, when Forgent began to
+enforce it.  It has successfully enforced it against two companies
+(Sony and an unnamed Japanese digital camera maker), but without court
+ruling.  This patent, U.S. Patent No. 4,698,672, expires in 2006.
+
+Philips and Lucent Technologies also own patents they claim cover
+JPEG.
+
+The following Netpbm components may be restricted by these patents:
+Jpegtopnm, Pnmtojpeg, Ppmtompeg, Tifftopnm, Pnmtotiff.  These all
+do their JPEG work via a JPEG library not distributed with Netpbm.
+Your JPEG-related liability for using Netpbm is limited to your 
+liability for using your JPEG library.
+
+Note that it is possible to use Ppmtompeg without involving JPEG and to
+build it without the ability to involve JPEG.
+
+The next best alternative to JPEG is probably PNG and maybe JBIG for
+bilevel (black and white) images.
+
+http://burnalljpegs.org contains information on this issue.
+
+
+MPEG patents
+------------
+
+The original University of California distribution of the Ppmtompeg code
+contains this statement in a README file:
+
+  ... patents are held by several companies on various aspects of the MPEG
+  video standard.  Companies or individuals who want to develop commercial
+  products that include this code must acquire licenses from these companies.
+  For information on licensing, see Appendix F in the standard.
+
+
+Expired LZW patents
+-------------------
+
+Unisys owns patents on LZW compression, which is used by
+Ppmtogif, and maybe on LZW decompression, which is used by Giftopnm.  IBM also
+owns a patent that may cover the GIF tools.  Unisys offers a license of the
+patent for trivial use for $5000.  Its U.S. patent (Number 4,558,302) expired
+June 20, 2003.  In most of Europe, the patent expired June 18, 2004.  In
+Japan, it was June 20, 2004 and in Canada, July 7, 2004.  IBM's U.S. patent
+expired August 11, 2006.
+
+Neither company has ever enforced the patent against trivial users of
+it.  <http://news.cnet.com/news/0-1005-200-1713278.html> is an article
+dated April 18, 2000 on the issue.
+http://www.unisys.com/about__unisys/lzw/> is Unisys' view of the
+matter.  For information from another perspective, see
+<http://burnallgifs.org>.
+
+The following Netpbm components may be restricted by these patents:
+Ppmtogif, Giftopnm.
+
+A good substitute for GIF if the patents are a problem is PNG (see
+pngtopnm, pnmtopng), which was developed with a primary purpose of not
+using any patented technology.
+
+You can also use the -nolzw option on ppmtogif to avoid using the LZW
+patent.  The images so generated are larger than traditional
+LZW-compressed GIFs, but any GIF decoder can decode them just the
+same.
diff --git a/editor/Makefile b/editor/Makefile
index d5d7633f..39329f00 100644
--- a/editor/Makefile
+++ b/editor/Makefile
@@ -7,7 +7,7 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 include $(BUILDDIR)/config.mk
 
-SUBDIRS = specialty
+SUBDIRS = pamflip specialty
 
 # We tend to separate out the build targets so that we don't have
 # any more dependencies for a given target than it really needs.
@@ -19,13 +19,14 @@ SUBDIRS = specialty
 PORTBINARIES = pamaddnoise pambackground pamcomp pamcut \
 	       pamdice pamditherbw pamedge \
 	       pamenlarge \
-	       pamflip pamfunc pammasksharpen \
-	       pamperspective \
+	       pamfunc pammasksharpen \
+	       pamperspective pamrecolor pamrubber \
 	       pamscale pamsistoaglyph pamstretch pamthreshold pamundice \
+	       pamwipeout \
 	       pbmclean pbmmask pbmpscale pbmreduce \
 	       pgmdeshadow pgmenhance \
 	       pgmmedian \
-	       pnmalias pnmcat pnmcomp pnmconvol pnmcrop \
+	       pnmalias pnmcat pnmconvol pnmcrop \
 	       pnmgamma \
 	       pnmhisteq pnminvert pnmmontage \
 	       pnmnlfilt pnmnorm pnmpad pnmpaste \
@@ -42,10 +43,9 @@ PORTBINARIES = pamaddnoise pambackground pamcomp pamcut \
 NOMERGEBINARIES = 
 MERGEBINARIES = $(PORTBINARIES)
 
-
 BINARIES = $(MERGEBINARIES) $(NOMERGEBINARIES)
-SCRIPTS = pnmflip ppmfade ppmquant ppmquantall ppmshadow \
-	  pamstretch-gen pnmmargin pnmquant 
+SCRIPTS = pnmflip ppmfade ppmquant ppmshadow \
+	  pamstretch-gen pnmmargin pnmquant pnmquantall 
 
 OBJECTS = $(BINARIES:%=%.o)
 
@@ -90,3 +90,11 @@ install.bin.local: $(PKGDIR)/bin
 	cd $(PKGDIR)/bin ; \
 	rm -f pnmscale$(EXE) ; \
 	$(SYMLINK) pamscale$(EXE) pnmscale$(EXE)
+# In March 2012, pnmquantall replaced ppmquantall
+	cd $(PKGDIR)/bin ; \
+	rm -f ppmquantall$(EXE) ; \
+	$(SYMLINK) pnmquantall$(EXE) ppmquantall$(EXE)
+# In August 2014, pamcomp replaced pnmcomp
+	cd $(PKGDIR)/bin ; \
+	rm -f pnmcomp$(EXE) ; \
+	$(SYMLINK) pamcomp$(EXE) pnmcomp$(EXE)
diff --git a/editor/pambackground.c b/editor/pambackground.c
index 1aa53177..b4941042 100644
--- a/editor/pambackground.c
+++ b/editor/pambackground.c
@@ -37,7 +37,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 < 1)
@@ -182,7 +182,7 @@ determineBackgroundColor(struct pam * const pamP,
             pnm_colorname(pamP, *bgColorP, hexokTrue);
         pm_message("Background color is %s", colorname);
 
-        strfree(colorname);
+        pm_strfree(colorname);
     }
 
     pnm_freepamtuple(lr);
diff --git a/editor/pamcomp.c b/editor/pamcomp.c
index e89509cc..b76eb8c7 100644
--- a/editor/pamcomp.c
+++ b/editor/pamcomp.c
@@ -39,13 +39,30 @@ enum vertPos {ABOVE, TOP, MIDDLE, BOTTOM, BELOW};
 
 
 enum sampleScale {INTENSITY_SAMPLE, GAMMA_SAMPLE};
-/* This indicates a scale for a PAM sample value.  INTENSITY_SAMPLE means
-   the value is proportional to light intensity; GAMMA_SAMPLE means the 
-   value is gamma-adjusted as defined in the PGM/PPM spec.  In both
-   scales, the values are continuous and normalized to the range 0..1.
+    /* This indicates a scale for a PAM sample value.  INTENSITY_SAMPLE means
+       the value is proportional to light intensity; GAMMA_SAMPLE means the 
+       value is gamma-adjusted as defined in the PGM/PPM spec.  In both
+       scales, the values are continuous and normalized to the range 0..1.
+       
+       This scale has no meaning if the PAM is not a visual image.  
+    */
 
-   This scale has no meaning if the PAM is not a visual image.  
-*/
+enum alphaMix {AM_KEEPUNDER, AM_OVERLAY};
+    /* This is a way to combine the alpha channels (transparency/opacity)
+       of the underlay and overlay images to form the alpha channel of the
+       composed result.
+
+       AM_KEEPUNDER means the alpha for the composed result is identical
+       to that of the underlay image.  I.e. the overlay merely modifies the
+       color.
+
+       AM_OVERLAY means the result is as if the underlay and overlay images
+       are plastic slides and they are taped together to form a composed slide.
+       So for one thing, the transparency of the composed image is the product
+       of the transparencies of the component images.  But the color is also
+       different, because the transparency of the underlaying image affects
+       its contribution to the composition.
+    */
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -61,6 +78,7 @@ struct cmdlineInfo {
     enum horizPos align;
     enum vertPos valign;
     unsigned int linear;
+    unsigned int mixtransparency;
 };
 
 
@@ -80,7 +98,7 @@ parseCommandLine(int                        argc,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -93,28 +111,30 @@ parseCommandLine(int                        argc,
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "invert",     OPT_FLAG,   NULL,                  
+    OPTENT3(0, "invert",             OPT_FLAG,   NULL,                  
             &cmdlineP->alphaInvert,       0);
-    OPTENT3(0, "xoff",       OPT_INT,    &cmdlineP->xoff,       
+    OPTENT3(0, "xoff",               OPT_INT,    &cmdlineP->xoff,       
             &xoffSpec,                    0);
-    OPTENT3(0, "yoff",       OPT_INT,    &cmdlineP->yoff,       
+    OPTENT3(0, "yoff",               OPT_INT,    &cmdlineP->yoff,       
             &yoffSpec,                    0);
-    OPTENT3(0, "opacity",    OPT_FLOAT, &cmdlineP->opacity,
+    OPTENT3(0, "opacity",            OPT_FLOAT,  &cmdlineP->opacity,
             &opacitySpec,                 0);
-    OPTENT3(0, "alpha",      OPT_STRING, &cmdlineP->alphaFilespec,
+    OPTENT3(0, "alpha",              OPT_STRING, &cmdlineP->alphaFilespec,
             &alphaSpec,                   0);
-    OPTENT3(0, "align",      OPT_STRING, &align,
+    OPTENT3(0, "align",              OPT_STRING, &align,
             &alignSpec,                   0);
-    OPTENT3(0, "valign",     OPT_STRING, &valign,
+    OPTENT3(0, "valign",             OPT_STRING, &valign,
             &valignSpec,                  0);
-    OPTENT3(0, "linear",     OPT_FLAG,    NULL,       
+    OPTENT3(0, "linear",             OPT_FLAG,   NULL,       
             &cmdlineP->linear,            0);
+    OPTENT3(0, "mixtransparency",    OPT_FLAG,   NULL,       
+            &cmdlineP->mixtransparency,   0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
@@ -217,32 +237,65 @@ commonFormat(int const formatA,
 
 
 
-static void
+typedef enum { TT_BLACKANDWHITE, TT_GRAYSCALE, TT_RGB } BaseTupletype;
+
+
+
+static BaseTupletype
 commonTupletype(const char * const tupletypeA, 
-                const char * const tupletypeB, 
-                char *       const tupletypeOut,
-                unsigned int const size) {
-
-    if (strncmp(tupletypeA, "RGB", 3) == 0 ||
-        strncmp(tupletypeB, "RGB", 3) == 0)
-        strncpy(tupletypeOut, "RGB", size);
-    else if (strncmp(tupletypeA, "GRAYSCALE", 9) == 0 ||
-        strncmp(tupletypeB, "GRAYSCALE", 9) == 0)
-        strncpy(tupletypeOut, "GRAYSCALE", size);
-    else if (strncmp(tupletypeA, "BLACKANDWHITE", 13) == 0 ||
-        strncmp(tupletypeB, "BLACKANDWHITE", 13) == 0)
-        strncpy(tupletypeOut, "BLACKANDWHITE", size);
+                const char * const tupletypeB) {
+
+    if (strneq(tupletypeA, "RGB", 3) ||
+        strneq(tupletypeB, "RGB", 3))
+        return TT_RGB;
+    else if (strneq(tupletypeA, "GRAYSCALE", 9) ||
+             strneq(tupletypeB, "GRAYSCALE", 9))
+        return TT_GRAYSCALE;
+    else if (strneq(tupletypeA, "BLACKANDWHITE", 13) ||
+             strneq(tupletypeB, "BLACKANDWHITE", 13))
+        return TT_BLACKANDWHITE;
     else
         /* Results are undefined for this case, so we do a hail Mary. */
-        strncpy(tupletypeOut, tupletypeA, size);
+        return TT_RGB;
 }
 
 
 
 static void
-determineOutputType(struct pam * const composedPamP,
-                    struct pam * const underlayPamP,
-                    struct pam * const overlayPamP) {
+determineOutputTupleType(BaseTupletype const baseTupletype,
+                         bool          const underlayHaveOpacity,
+                         char *        const tupleType,
+                         size_t        const size) {
+
+    char buffer[80];
+
+    switch (baseTupletype) {
+    case TT_BLACKANDWHITE:
+        STRSCPY(buffer, "RGB");
+        break;
+    case TT_GRAYSCALE:
+        STRSCPY(buffer, "GRAYSCALE");
+        break;
+    case TT_RGB:
+        STRSCPY(buffer, "RGB");
+        break;
+    }
+
+    if (underlayHaveOpacity)
+        STRSCAT(buffer, "_ALPHA");
+
+    strncpy(tupleType, buffer, size);
+}
+
+
+
+static void
+determineOutputType(const struct pam * const underlayPamP,
+                    const struct pam * const overlayPamP,
+                    struct pam *       const composedPamP) {
+
+    BaseTupletype const baseTupletype =
+        commonTupletype(underlayPamP->tuple_type, overlayPamP->tuple_type);
 
     composedPamP->height = underlayPamP->height;
     composedPamP->width  = underlayPamP->width;
@@ -250,22 +303,22 @@ determineOutputType(struct pam * const composedPamP,
     composedPamP->format = commonFormat(underlayPamP->format, 
                                         overlayPamP->format);
     composedPamP->plainformat = FALSE;
-    commonTupletype(underlayPamP->tuple_type, overlayPamP->tuple_type,
-                    composedPamP->tuple_type, 
-                    sizeof(composedPamP->tuple_type));
 
     composedPamP->maxval = pm_lcm(underlayPamP->maxval, overlayPamP->maxval, 
                                   1, PNM_OVERALLMAXVAL);
 
-    if (strcmp(composedPamP->tuple_type, "RGB") == 0)
-        composedPamP->depth = 3;
-    else if (strcmp(composedPamP->tuple_type, "GRAYSCALE") == 0)
-        composedPamP->depth = 1;
-    else if (strcmp(composedPamP->tuple_type, "BLACKANDWHITE") == 0)
-        composedPamP->depth = 1;
-    else
-        /* Results are undefined for this case, so we just do something safe */
-        composedPamP->depth = MIN(underlayPamP->depth, overlayPamP->depth);
+    composedPamP->visual = true;
+    composedPamP->color_depth = (baseTupletype == TT_RGB ? 3 : 1);
+    composedPamP->have_opacity = underlayPamP->have_opacity;
+    composedPamP->opacity_plane = (baseTupletype == TT_RGB ? 3 : 1);
+
+    composedPamP->depth =
+        (baseTupletype == TT_RGB ? 3 : 1) +
+        (underlayPamP->have_opacity ? 1 : 0);
+
+    determineOutputTupleType(baseTupletype, underlayPamP->have_opacity,
+                             composedPamP->tuple_type,
+                             sizeof(composedPamP->tuple_type));
 }
 
 
@@ -374,16 +427,86 @@ computeOverlayPosition(int                const underCols,
 
 
 
+/*----------------------------------------------------------------------------
+   COMPOSING TRANSPARENT PIXELS - AM_OVERLAY
+
+   There are various interpretations of composing transparent pixels, which
+   you can see in the definition of enum alphaMix.
+
+   For AM_OVERLAY, which means the result is as if the underlaying and
+   overlaying images are plastic slides and we taped them together to make a
+   composed plastic slide, the calculations go as follows.
+
+   U means color of underlay pixel
+   O means color of overlay pixel
+   C means color of composed pixel
+   X is a variable, standing for U, O, or C
+   X_T means transparency of the X pixel
+   X_O means opacity of the X pixel
+
+   Opacity and transparency are fractions in [0,1] and are complements:
+
+     X_T = 1 - X_O
+
+   We consider a further composed image, where in the future someone projects
+   another image (the background image) through our composed slide.  The
+   opacity and color of our composed image must be such that the light
+   emerging (the result image) is the same as if it travelled serially through
+   the underlaying and overlaying slide.
+
+   The transparency of each slide is the fraction of light that gets
+   through that slide, so the transparency of the composed slide is the
+   product of the underlay and overlay transparencies.
+
+       C_T = U_T * O_T
+
+   The composed transparency determines the distribution of background and
+   composed image in the result.  We have to choose the composed color such
+   that the result of combining it thus with the background image is the same
+   as the result of combining the background image with the underlay image
+   (distributed according to the underlay transparency), then combining that
+   with the overlay image (distributed according to the overlay transparency).
+
+       C = (O_T * U_O * U + O_O * O) / C_O   if C_O != 0
+
+   But this is meaningless if both components are fully transparent, which
+   means composed image opacity (C_O) is zero.  In that case, the composed
+   color is irrelevant to the result image and we can choose whatever color
+   is most likely to be convenient in applications that don't use the
+   transparency of the composed image.  We choose the pure underlay color.
+
+       C = U   if C_O = 0
+
+   function composeComponents() computes the above formula.
+
+-----------------------------------------------------------------------------*/
+
+
+
 static sample
 composeComponents(sample           const compA, 
                   sample           const compB,
                   float            const distrib,
+                  float            const bFactor,
+                  float            const composedFactor,
                   sample           const maxval,
                   enum sampleScale const sampleScale) {
 /*----------------------------------------------------------------------------
-  Compose a single component of each of two pixels, with 'distrib' being
+  Compose a single color component of each of two pixels, with 'distrib' being
   the fraction of 'compA' in the result, 1-distrib the fraction of 'compB'.
-  
+
+  Note that this function is not useful for an opacity component.
+
+  'sampleScale' tells in what domain the 'distrib' fraction applies:
+  brightness or light intensity (gamma-adjusted or not).
+
+  'bFactor' is a factor in [0,1] to apply to 'compB' first.
+
+  'composedFactor' is a factor to apply to the result.
+
+  See above for explanation of why 'bFactor' and 'composedFactor' are
+  useful.
+
   The inputs and result are based on a maxval of 'maxval'.
   
   Note that while 'distrib' in the straightforward case is always in
@@ -392,21 +515,23 @@ composeComponents(sample           const compA,
 -----------------------------------------------------------------------------*/
     sample retval;
 
-    if (fabs(1.0-distrib) < .001)
+    if (fabs(distrib) > .999 && bFactor > .999 && composedFactor > .999)
         /* Fast path for common case */
         retval = compA;
     else {
         if (sampleScale == INTENSITY_SAMPLE) {
             sample const mix = 
-                ROUNDU(compA * distrib + compB * (1.0 - distrib));
+                ROUNDU(compA * distrib + compB * bFactor *(1.0 - distrib));
             retval = MIN(maxval, MAX(0, mix));
         } else {
             float const compANormalized = (float)compA/maxval;
             float const compBNormalized = (float)compB/maxval;
             float const compALinear = pm_ungamma709(compANormalized);
             float const compBLinear = pm_ungamma709(compBNormalized);
+            float const compBLinearAdj = compBLinear * bFactor;
             float const mix = 
-                compALinear * distrib + compBLinear * (1.0 - distrib);
+                compALinear * distrib + compBLinearAdj * (1.0 - distrib)
+                * composedFactor;
             sample const sampleValue = ROUNDU(pm_gamma709(mix) * maxval);
             retval = MIN(maxval, MAX(0, sampleValue));
         }
@@ -416,6 +541,38 @@ composeComponents(sample           const compA,
 
 
 
+static sample
+composedOpacity(tuple         const uTuple,
+                struct pam *  const uPamP,
+                tuple         const oTuple,
+                struct pam *  const oPamP,
+                struct pam *  const cPamP,
+                enum alphaMix const alphaMix) {
+
+    sample retval;
+
+    assert(uPamP->have_opacity);
+
+    switch (alphaMix) {
+    case AM_OVERLAY: {
+        /* output transparency is product of two input transparencies */
+        float const underlayTrans =
+            1.0 - ((float)uTuple[uPamP->opacity_plane]/uPamP->maxval);
+        float const overlayTrans =
+            1.0 - ((float)oTuple[oPamP->opacity_plane]/oPamP->maxval);
+        float const composedTrans = underlayTrans * overlayTrans;
+        sample const sampleValue =  (1.0 - composedTrans) * cPamP->maxval;
+        retval = MIN(cPamP->maxval, MAX(0, sampleValue));
+    } break;
+    case AM_KEEPUNDER:
+        retval = uTuple[uPamP->opacity_plane];
+        break;
+    }
+    return retval;
+}
+
+
+
 static void
 overlayPixel(tuple            const overlayTuple,
              struct pam *     const overlayPamP,
@@ -423,20 +580,54 @@ overlayPixel(tuple            const overlayTuple,
              struct pam *     const underlayPamP,
              tuplen           const alphaTuplen,
              bool             const invertAlpha,
-             bool             const overlayHasOpacity,
-             unsigned int     const opacityPlane,
              tuple            const composedTuple,
              struct pam *     const composedPamP,
              float            const masterOpacity,
-             enum sampleScale const sampleScale) {
+             enum sampleScale const sampleScale,
+             enum alphaMix    const alphaMix) {
+/*----------------------------------------------------------------------------
+   Generate the result of overlaying one pixel with another, taking opacity
+   into account, viz overlaying 'underlayTuple' with 'overlayTuple'.
+   'overlayPamP' and 'underlayPamP', respectively, describe those tuples.
+
+   We always assume the underlay pixel is opaque.
+
+   We use the following declarations of the opacity of the overlay pixel in
+   deciding how much of the underlay pixel should show through.  The product
+   of all the indicated opacity factors is the overall opacity factor, where
+   an opacity factor is a real number from 0 to 1 and 1 means none of the
+   underlay pixel shows through and 0 means the overlay pixel is invisible and
+   the underlay pixel shows through in full force.
 
+     if *overlayPamP may indicate that 'overlayTuple' has an opacity component.
+
+     'alphaTuplen' is a normalized tuple whose first sample is the opacity
+     factor, except that iff 'invertAlpha' is true, it is a transparency
+     factor instead (opacity = 1 - transparency).
+
+     'masterOpacity' is a direct opacity factor
+
+   'sampleScale' tells whether the samples in the tuples are proportional
+   to brightness or light intensity (gamma-adjusted or not).  Opacity
+   factors apply to brightness (.5 means half the brightness of the result
+   comes from the underlay pixel, half comes from the overlay).
+
+   'alphaMix' tells how to determine the opacity of the result pixel.
+
+   Return the result as 'composedTuple', which has the form described by
+   'composedPamP'.
+-----------------------------------------------------------------------------*/
     float overlayWeight;
+    float underlayWeight;
+        /* Part of formula for AM_OVERLAY -- see explanation above */
+    float composedWeight;
+        /* Part of formula for AM_OVERLAY -- see explanation above */
 
     overlayWeight = masterOpacity;  /* initial value */
     
-    if (overlayHasOpacity)
+    if (overlayPamP->have_opacity)
         overlayWeight *= (float)
-            overlayTuple[opacityPlane] / overlayPamP->maxval;
+            overlayTuple[overlayPamP->opacity_plane] / overlayPamP->maxval;
     
     if (alphaTuplen) {
         float const alphaval = 
@@ -444,34 +635,73 @@ overlayPixel(tuple            const overlayTuple,
         overlayWeight *= alphaval;
     }
 
+    if (underlayPamP->have_opacity && alphaMix == AM_OVERLAY) {
+        struct pam * const uPamP = underlayPamP;
+        struct pam * const oPamP = overlayPamP;
+        sample const uOpacity = underlayTuple[uPamP->opacity_plane];
+        sample const oOpacity = overlayTuple[oPamP->opacity_plane];
+        sample const uMaxval = uPamP->maxval;
+        sample const oMaxval = oPamP->maxval;
+        float  const uOpacityN = uOpacity / uMaxval;
+        float  const oOpacityN = oOpacity / oMaxval;
+        float  const composedTrans = (1.0 - uOpacityN) * (1.0 * oOpacityN);
+        
+        if (composedTrans > .999) {
+            underlayWeight = 1.0;
+            composedWeight = 1.0;
+        } else {
+            underlayWeight = uOpacityN;
+            composedWeight = 1.0 / (1.0 - composedTrans);
+        }
+    } else {
+        underlayWeight = 1.0;
+        composedWeight = 1.0;
+    }
     {
         unsigned int plane;
         
-        for (plane = 0; plane < composedPamP->depth; ++plane)
+        for (plane = 0; plane < composedPamP->color_depth; ++plane)
             composedTuple[plane] = 
                 composeComponents(overlayTuple[plane], underlayTuple[plane], 
-                                  overlayWeight,
+                                  overlayWeight, underlayWeight,
+                                  composedWeight,
                                   composedPamP->maxval,
                                   sampleScale);
+
+        if (composedPamP->have_opacity)
+            composedTuple[composedPamP->opacity_plane] =
+                composedOpacity(underlayTuple, underlayPamP,
+                                overlayTuple, overlayPamP,
+                                composedPamP, alphaMix);
     }
 }
 
 
 
 static void
-adaptRowToOutputFormat(struct pam * const inpamP,
-                       tuple *      const tuplerow,
-                       struct pam * const outpamP) {
+adaptRowFormat(struct pam * const inpamP,
+               struct pam * const outpamP,
+               tuple *      const tuplerow) {
 /*----------------------------------------------------------------------------
    Convert the row in 'tuplerow', which is in a format described by
    *inpamP, to the format described by *outpamP.
 
    'tuplerow' must have enough allocated depth to do this.
 -----------------------------------------------------------------------------*/
+    assert(outpamP->visual);
+    assert(inpamP->visual);
+
     pnm_scaletuplerow(inpamP, tuplerow, tuplerow, outpamP->maxval);
 
-    if (strncmp(outpamP->tuple_type, "RGB", 3) == 0)
-        pnm_makerowrgb(inpamP, tuplerow);
+    if (outpamP->color_depth == 3) {
+        if (outpamP->have_opacity)
+            pnm_makerowrgba(inpamP, tuplerow);
+        else
+            pnm_makerowrgb(inpamP, tuplerow);
+    } else {
+        if (outpamP->have_opacity)
+            pnm_addopacityrow(inpamP, tuplerow);
+    }
 }
 
 
@@ -482,16 +712,22 @@ composeRow(int              const originleft,
            struct pam *     const overlayPamP,
            bool             const invertAlpha,
            float            const masterOpacity,
-           bool             const overlayHasOpacity,
-           unsigned int     const opacityPlane,
            struct pam *     const composedPamP,
            enum sampleScale const sampleScale,
+           enum alphaMix    const alphaMix,
            const tuple *    const underlayTuplerow,
            const tuple *    const overlayTuplerow,
            const tuplen *   const alphaTuplerown,
            tuple *          const composedTuplerow) {
+/*----------------------------------------------------------------------------
+   Create a row of tuples ('composedTupleRow') which is the composition of
+   row 'overlayTupleRow' laid over row 'underlayTupleRow', starting at
+   column 'originLeft'.
 
+   *underlayPamP and *overlayPamP describe the respective tuple rows.
+-----------------------------------------------------------------------------*/
     unsigned int col;
+
     for (col = 0; col < composedPamP->width; ++col) {
         int const ovlcol = col - originleft;
 
@@ -502,9 +738,8 @@ composeRow(int              const originleft,
             overlayPixel(overlayTuplerow[ovlcol], overlayPamP,
                          underlayTuplerow[col], underlayPamP,
                          alphaTuplen, invertAlpha,
-                         overlayHasOpacity, opacityPlane,
                          composedTuplerow[col], composedPamP,
-                         masterOpacity, sampleScale);
+                         masterOpacity, sampleScale, alphaMix);
         } else
             /* Overlay image does not touch this column. */
             pnm_assigntuple(composedPamP, composedTuplerow[col],
@@ -515,6 +750,55 @@ composeRow(int              const originleft,
 
 
 static void
+determineInputAdaptations(const struct pam * const underlayPamP,
+                          const struct pam * const overlayPamP,
+                          const struct pam * const composedPamP,
+                          struct pam *       const adaptUnderlayPamP,
+                          struct pam *       const adaptOverlayPamP) {
+/*----------------------------------------------------------------------------
+   For easy of computation, this program reads a tuple row from one of the
+   input files, then transforms it something similar to the format of the
+   eventual output tuple row.  E.g. if the input is grayscale and the
+   output color, it converts the depth 1 row read from the file to a depth
+   3 row for use in computations.
+
+   This function determines what the result of that transformation should be.
+   It's not as simple as it sounds because of opacity.  The overlay may have
+   an opacity plane that has to be kept for the computations, while the output
+   has no opacity plane.
+
+   Our output PAMs are meaningless except in the fields that pertain to a
+   row of tuples.  E.g. the file descriptor and image height members are
+   meaningless.
+-----------------------------------------------------------------------------*/
+    /* We make the underlay row identical to the composed (output) row,
+       except for its width.
+    */
+
+    *adaptUnderlayPamP = *composedPamP;
+    adaptUnderlayPamP->width = underlayPamP->width;
+
+    /* Same for the overlay row, except that it retains is original
+       opacity.
+    */
+
+    adaptOverlayPamP->width = overlayPamP->width;
+    adaptOverlayPamP->tuple_type[0] = '\0';  /* a hack; this doesn't matter */
+    adaptOverlayPamP->visual = true;
+    adaptOverlayPamP->color_depth = composedPamP->color_depth;
+    adaptOverlayPamP->have_opacity = overlayPamP->have_opacity;
+    adaptOverlayPamP->opacity_plane = composedPamP->color_depth;
+    adaptOverlayPamP->depth =
+        composedPamP->color_depth +
+        (overlayPamP->have_opacity ? 1 : 0);
+    adaptOverlayPamP->maxval = composedPamP->maxval;
+    adaptOverlayPamP->bytes_per_sample = composedPamP->bytes_per_sample;
+    adaptOverlayPamP->allocation_depth = overlayPamP->allocation_depth;
+}
+
+
+
+static void
 composite(int          const originleft, 
           int          const origintop, 
           struct pam * const underlayPamP,
@@ -523,7 +807,8 @@ composite(int          const originleft,
           bool         const invertAlpha,
           float        const masterOpacity,
           struct pam * const composedPamP,
-          bool         const assumeLinear) {
+          bool         const assumeLinear,
+          bool         const mixTransparency) {
 /*----------------------------------------------------------------------------
    Overlay the overlay image in the array 'overlayImage', described by
    *overlayPamP, onto the underlying image from the input image file
@@ -546,17 +831,17 @@ composite(int          const originleft,
 -----------------------------------------------------------------------------*/
     enum sampleScale const sampleScale = 
         assumeLinear ? INTENSITY_SAMPLE : GAMMA_SAMPLE;
+    enum alphaMix const alphaMix =
+        mixTransparency ? AM_OVERLAY : AM_KEEPUNDER;
 
     int underlayRow;  /* NB may be negative */
     int overlayRow;   /* NB may be negative */
     tuple * composedTuplerow;
     tuple * underlayTuplerow;
+    struct pam adaptUnderlayPam;
     tuple * overlayTuplerow;
+    struct pam adaptOverlayPam;
     tuplen * alphaTuplerown;
-    bool overlayHasOpacity;
-    unsigned int opacityPlane;
-
-    pnm_getopacity(overlayPamP, &overlayHasOpacity, &opacityPlane);
 
     composedTuplerow = pnm_allocpamrow(composedPamP);
     underlayTuplerow = pnm_allocpamrow(underlayPamP);
@@ -566,6 +851,9 @@ composite(int          const originleft,
     else
         alphaTuplerown = NULL;
 
+    determineInputAdaptations(underlayPamP, overlayPamP, composedPamP,
+                              &adaptUnderlayPam, &adaptOverlayPam);
+
     pnm_writepaminit(composedPamP);
 
     assert(INT_MAX - overlayPamP->height > origintop); /* arg constraint */
@@ -577,15 +865,13 @@ composite(int          const originleft,
 
         if (overlayRow >= 0 && overlayRow < overlayPamP->height) {
             pnm_readpamrow(overlayPamP, overlayTuplerow);
-            adaptRowToOutputFormat(overlayPamP, overlayTuplerow, composedPamP);
+            adaptRowFormat(overlayPamP, &adaptOverlayPam, overlayTuplerow);
             if (alphaPamP)
                 pnm_readpamrown(alphaPamP, alphaTuplerown);
         }
         if (underlayRow >= 0 && underlayRow < underlayPamP->height) {
             pnm_readpamrow(underlayPamP, underlayTuplerow);
-            adaptRowToOutputFormat(underlayPamP, underlayTuplerow, 
-                                   composedPamP);
-
+            adaptRowFormat(underlayPamP, &adaptUnderlayPam, underlayTuplerow);
             if (underlayRow < origintop || 
                 underlayRow >= origintop + overlayPamP->height) {
             
@@ -593,9 +879,9 @@ composite(int          const originleft,
 
                 pnm_writepamrow(composedPamP, underlayTuplerow);
             } else {
-                composeRow(originleft, underlayPamP, overlayPamP,
-                           invertAlpha, masterOpacity, overlayHasOpacity,
-                           opacityPlane, composedPamP, sampleScale,
+                composeRow(originleft, &adaptUnderlayPam, &adaptOverlayPam,
+                           invertAlpha, masterOpacity, 
+                           composedPamP, sampleScale, alphaMix,
                            underlayTuplerow, overlayTuplerow, alphaTuplerown,
                            composedTuplerow);
                 
@@ -612,6 +898,30 @@ composite(int          const originleft,
 
 
 
+static void
+initAlphaFile(struct cmdlineInfo const cmdline,
+              struct pam *       const overlayPamP,
+              FILE **            const filePP,
+              struct pam *       const pamP) {
+
+    FILE * fileP;
+    
+    if (cmdline.alphaFilespec) {
+        fileP = pm_openr(cmdline.alphaFilespec);
+        pamP->comment_p = NULL;
+        pnm_readpaminit(fileP, pamP, PAM_STRUCT_SIZE(opacity_plane));
+
+        if (overlayPamP->width != pamP->width || 
+            overlayPamP->height != pamP->height)
+            pm_error("Opacity map and overlay image are not the same size");
+    } else
+        fileP = NULL;
+
+    *filePP = fileP;
+}
+
+
+
 int
 main(int argc, const char *argv[]) {
 
@@ -630,34 +940,46 @@ main(int argc, const char *argv[]) {
     parseCommandLine(argc, argv, &cmdline);
 
     overlayFileP = pm_openr(cmdline.overlayFilespec);
+
+    overlayPam.comment_p = NULL;
     pnm_readpaminit(overlayFileP, &overlayPam, 
-                    PAM_STRUCT_SIZE(allocation_depth));
-    if (cmdline.alphaFilespec) {
-        alphaFileP = pm_openr(cmdline.alphaFilespec);
-        pnm_readpaminit(alphaFileP, &alphaPam, 
-                        PAM_STRUCT_SIZE(allocation_depth));
+                    PAM_STRUCT_SIZE(opacity_plane));
 
-        if (overlayPam.width != alphaPam.width || 
-            overlayPam.height != alphaPam.height)
-            pm_error("Opacity map and overlay image are not the same size");
-    } else
-        alphaFileP = NULL;
+    if (overlayPam.len < PAM_STRUCT_SIZE(opacity_plane))
+        pm_error("Libnetpbm is too old.  This program requires libnetpbm from "
+                 "Netpbm 10.56 (September 2011) or newer");
+
+    if (!overlayPam.visual)
+        pm_error("Overlay image has tuple type '%s', which is not a "
+                 "standard visual type.  We don't know how to compose.",
+                 overlayPam.tuple_type);
+
+    initAlphaFile(cmdline, &overlayPam, &alphaFileP, &alphaPam);
 
     underlayFileP = pm_openr(cmdline.underlyingFilespec);
 
+    underlayPam.comment_p = NULL;
     pnm_readpaminit(underlayFileP, &underlayPam, 
-                    PAM_STRUCT_SIZE(allocation_depth));
+                    PAM_STRUCT_SIZE(opacity_plane));
 
+    assert(underlayPam.len >= PAM_STRUCT_SIZE(opacity_plane));
+
+    if (!overlayPam.visual)
+        pm_error("Overlay image has tuple type '%s', which is not a "
+                 "standard visual type.  We don't know how to compose.",
+                 overlayPam.tuple_type);
+    
     computeOverlayPosition(underlayPam.width, underlayPam.height, 
                            overlayPam.width,  overlayPam.height,
                            cmdline, &originLeft, &originTop);
 
-    composedPam.size             = sizeof(composedPam);
+    composedPam.size             = PAM_STRUCT_SIZE(opacity_plane);
     composedPam.len              = PAM_STRUCT_SIZE(allocation_depth);
     composedPam.allocation_depth = 0;
     composedPam.file             = pm_openw(cmdline.outputFilespec);
+    composedPam.comment_p        = NULL;
 
-    determineOutputType(&composedPam, &underlayPam, &overlayPam);
+    determineOutputType(&underlayPam, &overlayPam, &composedPam);
 
     pnm_setminallocationdepth(&underlayPam, composedPam.depth);
     pnm_setminallocationdepth(&overlayPam,  composedPam.depth);
@@ -665,7 +987,7 @@ main(int argc, const char *argv[]) {
     composite(originLeft, originTop,
               &underlayPam, &overlayPam, alphaFileP ? &alphaPam : NULL,
               cmdline.alphaInvert, cmdline.opacity,
-              &composedPam, cmdline.linear);
+              &composedPam, cmdline.linear, cmdline.mixtransparency);
 
     if (alphaFileP)
         pm_close(alphaFileP);
@@ -678,3 +1000,6 @@ main(int argc, const char *argv[]) {
     */
     return 0;
 }
+
+
+
diff --git a/editor/pamcut.c b/editor/pamcut.c
index 8d4c2240..7c41af38 100644
--- a/editor/pamcut.c
+++ b/editor/pamcut.c
@@ -24,11 +24,11 @@
        but we hope not.
        */
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* Filespec of input file */
+    const char * inputFileName;  /* File name of input file */
 
     /* The following describe the rectangle the user wants to cut out. 
        the value UNSPEC for any of them indicates that value was not
@@ -51,8 +51,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
@@ -87,7 +87,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = TRUE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (cmdlineP->width < 0)
@@ -103,10 +103,10 @@ parseCommandLine(int argc, char ** const argv,
 
     switch (argc-1) {
     case 0:
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
         break;
     case 1:
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
         break;
     case 4:
     case 5: {
@@ -137,9 +137,9 @@ parseCommandLine(int argc, char ** const argv,
         }
 
         if (argc-1 == 4)
-            cmdlineP->inputFilespec = "-";
+            cmdlineP->inputFileName = "-";
         else
-            cmdlineP->inputFilespec = argv[5];
+            cmdlineP->inputFileName = argv[5];
         break;
     }
     }
@@ -628,7 +628,7 @@ extractRowsPBM(const struct pam * const inpamP,
 
 static void
 cutOneImage(FILE *             const ifP,
-            struct cmdlineInfo const cmdline,
+            struct CmdlineInfo const cmdline,
             FILE *             const ofP) {
 
     int leftcol, rightcol, toprow, bottomrow;
@@ -669,19 +669,19 @@ cutOneImage(FILE *             const ifP,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     FILE * const ofP = stdout;
 
-    struct cmdlineInfo cmdline;
-    FILE* ifP;
-    bool eof;
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    int eof;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
     eof = FALSE;
     while (!eof) {
diff --git a/editor/pamcut.test b/editor/pamcut.test
deleted file mode 100644
index be70f1fd..00000000
--- a/editor/pamcut.test
+++ /dev/null
@@ -1,10 +0,0 @@
-echo Test 1.  Should print 2958909756 124815
-./pamcut -top 0 -left 0 -width 260 -height 160 -pad ../testimg.ppm | cksum
-echo Test 2.  Should print 1550940962 10933
-./pamcut -top 200 -left 120 -width 40 -height 40 -pad ../testimg.ppm | cksum
-echo Test 3.  Should print 708474423 14
-./pamcut -top 5 -left 5 -bottom 5 -right 5 ../testimg.ppm | cksum
-echo Test 3.  Should print 3412257956 129
-pbmmake -g 50 50 | ./pamcut 5 5 30 30 | cksum
-
-
diff --git a/editor/pamdice.c b/editor/pamdice.c
index f72420ab..2c53a110 100644
--- a/editor/pamdice.c
+++ b/editor/pamdice.c
@@ -53,7 +53,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
     
@@ -80,7 +80,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (cmdlineP->sliceVertically) {
@@ -223,9 +223,9 @@ computeOutputFilenameFormat(int           const format,
     default:       filenameSuffix = "";    break;
     }
     
-    asprintfN(filenameFormatP, "%s_%%0%uu_%%0%uu.%s",
-              outstem, ndigits(nHorizSlice), ndigits(nVertSlice),
-              filenameSuffix);
+    pm_asprintf(filenameFormatP, "%s_%%0%uu_%%0%uu.%s",
+                outstem, ndigits(nHorizSlice), ndigits(nVertSlice),
+                filenameSuffix);
 
     if (*filenameFormatP == NULL)
         pm_error("Unable to allocate memory for filename format string");
@@ -258,7 +258,7 @@ openOutStreams(struct pam   const inpam,
     for (vertSlice = 0; vertSlice < nVertSlice; ++vertSlice) {
         const char * filename;
 
-        asprintfN(&filename, filenameFormat, horizSlice, vertSlice);
+        pm_asprintf(&filename, filenameFormat, horizSlice, vertSlice);
 
         if (filename == NULL)
             pm_error("Unable to allocate memory for output filename");
@@ -273,10 +273,10 @@ openOutStreams(struct pam   const inpam,
             
             pnm_writepaminit(&outpam[vertSlice]);
 
-            strfree(filename);
+            pm_strfree(filename);
         }
     }        
-    strfree(filenameFormat);
+    pm_strfree(filenameFormat);
 }
 
 
diff --git a/editor/pamdither.c b/editor/pamdither.c
deleted file mode 100644
index e084892c..00000000
--- a/editor/pamdither.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*=============================================================================
-                                 pamdither
-===============================================================================
-  By Bryan Henderson, July 2006.
-
-  Contributed to the public domain.
-
-  This is meant to replace Ppmdither by Christos Zoulas, 1991.
-=============================================================================*/
-
-#include "pm_c_util.h"
-#include "mallocvar.h"
-#include "shhopt.h"
-#include "pam.h"
-
-/* Besides having to have enough memory available, the limiting factor
-   in the dithering matrix power is the size of the dithering value.
-   We need 2*dith_power bits in an unsigned int.  We also reserve
-   one bit to give headroom to do calculations with these numbers.
-*/
-#define MAX_DITH_POWER ((sizeof(unsigned int)*8 - 1) / 2)
-
-
-/* COLOR():
- *	returns the index in the colormap for the
- *      r, g, b values specified.
- */
-#define COLOR(r,g,b) (((r) * dith_ng + (g)) * dith_nb + (b))
-
-struct cmdlineInfo {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    const char * inputFileName;  /* File name of input file */
-    const char * mapFileName;    /* File name of colormap file */
-    unsigned int dim;
-    unsigned int verbose;
-};
-
-
-
-static void
-parseCommandLine (int argc, 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 dimSpec, mapfileSpec;
-
-    MALLOCARRAY_NOFAIL(option_def, 100);
-    
-    option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "dim",      OPT_UINT, 
-            &cmdlineP->dim,    &dimSpec, 0);
-    OPTENT3(0,   "mapfile",      OPT_STRING, 
-            &cmdlineP->mapFilespec,    &mapfileSpec, 0);
-    OPTENT3(0, "verbose",        OPT_FLAG,   NULL,                  
-            &cmdlineP->verbose,        0 );
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    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 *cmdline_p and others. */
-
-    if (!dimSpec)
-        cmdlineP->dim = 4;
-
-    if (cmdlineP->dim > MAX_DITH_POWER)
-        pm_error("Dithering matrix power %u (-dim) is too large.  "
-                 "Must be <= %d",
-                 dithPower, MAX_DITH_POWER);
-        
-    if (!mapfileSpec)
-        pm_error("You must specify the -mapfile option.");
-
-    if (argc-1 > 1)
-        pm_error("Program takes at most one argument: the input file "
-                 "specification.  "
-                 "You specified %d arguments.", argc-1);
-    if (argc-1 < 1)
-        cmdlineP->inputFilespec = "-";
-    else
-        cmdlineP->inputFilespec = argv[1];
-}
-
-
-
-static unsigned int
-dither(sample       const p,
-       sample       const maxval,
-       unsigned int const d,
-       unsigned int const ditheredMaxval,
-       unsigned int const ditherMatrixArea) {
-/*----------------------------------------------------------------------------
-  Return the dithered brightness for a component of a pixel whose real 
-  brightness for that component is 'p' based on a maxval of 'maxval'.
-  The returned brightness is based on a maxval of ditheredMaxval.
-
-  'd' is the entry in the dithering matrix for the position of this pixel
-  within the dithered square.
-
-  'ditherMatrixArea' is the area (number of pixels in) the dithered square.
------------------------------------------------------------------------------*/
-    unsigned int const ditherSquareMaxval = ditheredMaxval * ditherMatrixArea;
-        /* This is the maxval for an intensity that an entire dithered
-           square can represent.
-        */
-    pixval const pScaled = ditherSquareMaxval * p / maxval;
-        /* This is the input intensity P expressed with a maxval of
-           'ditherSquareMaxval'
-        */
-    
-    /* Now we scale the intensity back down to the 'ditheredMaxval', and
-       as that will involve rounding, we round up or down based on the position
-       in the dithered square, as determined by 'd'
-    */
-
-    return (pScaled + d) / ditherMatrixArea;
-}
-
-
-
-static unsigned int
-dithValue(unsigned int const y,
-          unsigned int const x,
-          unsigned int const dithPower) { 
-/*----------------------------------------------------------------------------
-  Return the value of a dither matrix which is 2 ** dithPower elements
-  square at Row x, Column y.
-  [graphics gems, p. 714]
------------------------------------------------------------------------------*/
-    unsigned int d;
-        /*
-          Think of d as the density. At every iteration, d is shifted
-          left one and a new bit is put in the low bit based on x and y.
-          If x is odd and y is even, or visa versa, then a bit is shifted in.
-          This generates the checkerboard pattern seen in dithering.
-          This quantity is shifted again and the low bit of y is added in.
-          This whole thing interleaves a checkerboard pattern and y's bits
-          which is what you want.
-        */
-    unsigned int i;
-
-    for (i = 0, d = 0; i < dithPower; i++, x >>= 1, y >>= 1)
-        d = (d << 2) | (((x & 1) ^ (y & 1)) << 1) | (y & 1);
-
-    return(d);
-}
-
-
-
-static unsigned int **
-dithMatrix(unsigned int const dithPower) {
-/*----------------------------------------------------------------------------
-   Create the dithering matrix for dimension 'dithDim'.
-
-   Return it in newly malloc'ed storage.
-
-   Note that we assume 'dith_dim' is small enough that the dith_mat_sz
-   computed within fits in an int.  Otherwise, results are undefined.
------------------------------------------------------------------------------*/
-    unsigned int const dithDim = 1 << dithPower;
-
-    unsigned int ** dithMat;
-
-    assert(dithPower < sizeof(unsigned int) * 8);
-
-    {
-        unsigned int const dithMatSize = 
-            (dithDim * sizeof(*dithMat)) + /* pointers */
-            (dithDim * dithDim * sizeof(**dithMat)); /* data */
-        
-        dithMat = malloc(dithMatSize);
-        
-        if (dithMat == NULL) 
-            pm_error("Out of memory.  "
-                     "Cannot allocate %d bytes for dithering matrix.",
-                     dithMatSize);
-    }
-    {
-        unsigned int * const rowStorage = (unsigned int *)&dithMat[dithDim];
-        unsigned int y;
-        for (y = 0; y < dithDim; ++y)
-            dithMat[y] = &rowStorage[y * dithDim];
-    }
-    {
-        unsigned int y;
-        for (y = 0; y < dithDim; ++y) {
-            unsigned int x;
-            for (x = 0; x < dithDim; ++x)
-                dithMat[y][x] = dithValue(y, x, dithPower);
-        }
-    }
-    return dithMat;
-}
-
-    
-
-static void
-ditherImage(struct pam   const inpam,
-            tuple *      const colormap, 
-            unsigned int const dithPower,
-            struct pam   const outpam;
-            tuple **     const inTuples,
-            tuple ***    const outTuplesP) {
-
-    unsigned int const dithDim = 1 << dithPower;
-    unsigned int const ditherMatrixArea = SQR(dithDim);
-
-    unsigned int const modMask = (dithDim - 1);
-       /* And this into N to compute N % dithDim cheaply, since we
-          know (though the compiler doesn't) that dithDim is a power of 2
-       */
-    unsigned int ** const ditherMatrix = dithMatrix(dithPower);
-
-    tuple ** ouputTuples;
-    unsigned int row; 
-
-    assert(dithPower < sizeof(unsigned int) * 8);
-    assert(UINT_MAX / dithDim >= dithDim);
-
-    outTuples = ppm_allocpamarray(outpam);
-
-    for (row = 0; row < inpam.height; ++row) {
-        unsigned int col;
-        for (col = 0; col < inpam.width; ++col) {
-            unsigned int const d =
-                ditherMatrix[row & modMask][(width-col-1) & modMask];
-            tuple const inputTuple = inTuples[row][col];
-            unsigned int dithered[3];
-
-            unsigned int plane;
-
-            assert(inpam.depth >= 3);
-
-            for (plane = 0; plane < 3; ++plane)
-                dithered[plane] =
-                    dither(inputTuple[plane], inpam.maxval, d, outpam.maxval,
-                           ditherMatrixArea);
-
-            outTuples[row][col] = 
-                colormap[COLOR(dithered[RED_PLANE],
-                               dithered[GRN_PLANE],
-                               dithered[BLU_PLANE])];
-        }
-    }
-    free(ditherMatrix);
-    *outTuplesP = outTuples;
-}
-
-
-
-static void
-getColormap(const char * const mapFileName,
-            tuple **     const colormapP) {
-
-    TODO("write this");
-
-}
-
-
-
-int
-main(int argc,
-     char ** argv) {
-
-    struct cmdlineInfo cmdline;
-    FILE * ifP;
-    tuple ** inTuples;        /* Input image */
-    tuple ** outTuples;        /* Output image */
-    tuple * colormap;
-    int cols, rows;
-    pixval maxval;  /* Maxval of the input image */
-
-    struct pam outpamCommon;
-        /* Describes the output images.  Width and height fields are
-           not meaningful, because different output images might have
-           different dimensions.  The rest of the information is common
-           across all output images.
-        */
-
-    pnm_init(&argc, argv);
-
-    parseCommandLine(&argc, &argv);
-
-    pm_openr(cmdline.inputFileName);
-
-    inTuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
-    pm_close(ifP);
-
-    getColormap(cmdline.mapFileName, &colormap);
-
-    ditherImage(inpam, colormap, dithPower, inTuples, &outTuples);
-
-    ppm_writeppm(stdout, opixels, cols, rows, outputMaxval, 0);
-    pm_close(stdout);
-
-    free(colormap);
-
-    pnm_freepamarray(inTuples, &inpam);
-    pnm_freepamarray(outTuples, &outpam);
-
-    return 0;
-}
diff --git a/editor/pamditherbw.c b/editor/pamditherbw.c
index 90e2abd5..36eb7d9e 100644
--- a/editor/pamditherbw.c
+++ b/editor/pamditherbw.c
@@ -41,6 +41,8 @@ struct cmdlineInfo {
     unsigned int  clusterRadius;  
         /* Defined only for halftone == QT_CLUSTER */
     float         threshval;
+    unsigned int  randomseed;
+    unsigned int  randomseedSpec;
 };
 
 
@@ -54,7 +56,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -83,12 +85,14 @@ parseCommandLine(int argc, char ** argv,
             &valueSpec, 0);
     OPTENT3(0, "clump",     OPT_UINT,  &cmdlineP->clumpSize, 
             &clumpSpec, 0);
+    OPTENT3(0,   "randomseed",   OPT_UINT,    &cmdlineP->randomseed,
+            &cmdlineP->randomseedSpec,      0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (floydOpt + atkinsonOpt + thresholdOpt + hilbertOpt + dither8Opt + 
@@ -518,7 +522,6 @@ createFsConverter(struct pam * const graypamP,
     /* Initialize Floyd-Steinberg error vectors. */
     MALLOCARRAY_NOFAIL(stateP->thiserr, graypamP->width + 2);
     MALLOCARRAY_NOFAIL(stateP->nexterr, graypamP->width + 2);
-    srand(pm_randseed());
 
     {
         /* (random errors in [-1/8 .. 1/8]) */
@@ -661,8 +664,6 @@ createAtkinsonConverter(struct pam * const graypamP,
     for (relRow = 0; relRow < 3; ++relRow)
         MALLOCARRAY_NOFAIL(stateP->error[relRow], graypamP->width + 2);
 
-    srand(pm_randseed());
-
     {
         /* (random errors in [-1/8 .. 1/8]) */
         unsigned int col;
@@ -857,6 +858,8 @@ main(int argc, char *argv[]) {
 
     parseCommandLine(argc, argv, &cmdline);
 
+    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
+
     ifP = pm_openr(cmdline.inputFilespec);
 
     if (cmdline.halftone == QT_HILBERT)
diff --git a/editor/pamedge.c b/editor/pamedge.c
index ac94e2ae..f4384535 100644
--- a/editor/pamedge.c
+++ b/editor/pamedge.c
@@ -156,11 +156,12 @@ writeMiddleRows(struct pam * const inpamP,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char ** argv) {
+
     FILE *ifP;
     struct pam inpam, outpam;
 
-    pnm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     if (argc-1 == 1) 
         ifP = pm_openr(argv[1]);
diff --git a/editor/pamenlarge.c b/editor/pamenlarge.c
index b3039424..187bfb6e 100644
--- a/editor/pamenlarge.c
+++ b/editor/pamenlarge.c
@@ -115,6 +115,8 @@ enlargePbmRowHorizontally(struct pam *          const inpamP,
     static unsigned char const trp3[8] = { 
         0x00, 0x07, 0x38, 0x3F, 0xC0, 0xC7, 0xF8, 0xFF };
 
+    static unsigned char const quad[4] = { 0x00, 0x0F, 0xF0, 0xFF };
+
     static unsigned char const quin2[8] = {
         0x00, 0x01, 0x3E, 0x3F, 0xC0, 0xC1, 0xFE, 0xFF };
 
@@ -129,33 +131,127 @@ enlargePbmRowHorizontally(struct pam *          const inpamP,
     case 1:  break; /* outrow set to inrow */
     case 2:  /* Make outrow using prefabricated parts (same for 3, 5). */ 
         for (colChar = 0; colChar < inColChars; ++colChar) { 
-            outrow[colChar*2]  = dbl[(inrow[colChar] & 0xF0) >> 4];
-            outrow[colChar*2+1]= dbl[inrow[colChar] & 0x0F];
+            outrow[colChar*2]   = dbl[(inrow[colChar] & 0xF0) >> 4];
+            outrow[colChar*2+1] = dbl[(inrow[colChar] & 0x0F) >> 0];
             /* Possible outrow overrun by one byte. */
         }
-        break;   
+        break;
+
     case 3:
         for (colChar = 0; colChar < inColChars; ++colChar) { 
-            outrow[colChar*3]  = trp1[(inrow[colChar] & 0xF0) >> 5];
-            outrow[colChar*3+1]= trp2[(inrow[colChar] >> 2) & 0x0F];
-            outrow[colChar*3+2]= trp3[inrow[colChar] & 0x07];
+            outrow[colChar*3]   = trp1[(inrow[colChar] & 0xF0) >> 5];
+            outrow[colChar*3+1] = trp2[(inrow[colChar] >> 2) & 0x0F];
+            outrow[colChar*3+2] = trp3[(inrow[colChar] >> 0) & 0x07];
         }
         break;  
+
+    case 4:
+        for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned int i;
+            for (i = 0; i < 4; ++i) 
+                outrow[colChar*4+i]=
+                    quad[(inrow[colChar] >> (6 - 2 * i)) & 0x03]; 
+        }
+        break;
+
     case 5:
         for (colChar = 0; colChar < inColChars; ++colChar) { 
-            outrow[colChar*5]  = pair[ (inrow[colChar] >>6) & 0x03 ] >> 5; 
-            outrow[colChar*5+1]= quin2[(inrow[colChar] >>4) & 0x07 ]; 
-            outrow[colChar*5+2]= pair[ (inrow[colChar] >>3) & 0x03 ] >> 4; 
-            outrow[colChar*5+3]= quin4[(inrow[colChar] >>1) & 0x07 ];
-            outrow[colChar*5+4]= pair[ inrow[colChar] & 0x03 ] >>3; 
+            outrow[colChar*5]   = pair [(inrow[colChar] >> 6) & 0x03] >> 5; 
+            outrow[colChar*5+1] = quin2[(inrow[colChar] >> 4) & 0x07] >> 0; 
+            outrow[colChar*5+2] = quad [(inrow[colChar] >> 3) & 0x03] >> 0; 
+            outrow[colChar*5+3] = quin4[(inrow[colChar] >> 1) & 0x07] >> 0;
+            outrow[colChar*5+4] = pair [(inrow[colChar] >> 0) & 0x03] >> 3; 
+        }
+        break;
+
+    case 6:  /* Compound of 2 and 3 */ 
+        for (colChar = 0; colChar < inColChars; ++colChar) { 
+            unsigned char const hi = dbl[(inrow[colChar] & 0xF0) >> 4];
+            unsigned char const lo = dbl[(inrow[colChar] & 0x0F) >> 0];
+
+            outrow[colChar*6]   = trp1[(hi & 0xF0) >> 5];
+            outrow[colChar*6+1] = trp2[(hi >> 2) & 0x0F];
+            outrow[colChar*6+2] = trp3[hi & 0x07];
+
+            outrow[colChar*6+3] = trp1[(lo & 0xF0) >> 5];
+            outrow[colChar*6+4] = trp2[(lo >> 2) & 0x0F];
+            outrow[colChar*6+5] = trp3[lo & 0x07];
+        }
+        break;  
+
+    case 7:
+        for (colChar = 0; colChar < inColChars; ++colChar) { 
+            uint32_t hi, lo;
+
+            hi = inrow[colChar] >> 4;
+            hi = ((((hi>>1) * 0x00082080) | (0x01 & hi)) & 0x00204081 ) * 0x7F;
+            hi >>= 4;
+            outrow[colChar*7]   =  (unsigned char) ( hi >> 16);
+            outrow[colChar*7+1] =  (unsigned char) ((hi >>  8) & 0xFF);
+            outrow[colChar*7+2] =  (unsigned char) ((hi >>  0) & 0xFF);
+
+            lo = inrow[colChar] & 0x001F;
+            lo = ((((lo>>1) * 0x02082080) | (0x01 & lo)) & 0x10204081 ) * 0x7F;
+            outrow[colChar*7+3] =  (unsigned char) ((lo >> 24) & 0xFF);
+            outrow[colChar*7+4] =  (unsigned char) ((lo >> 16) & 0xFF); 
+            outrow[colChar*7+5] =  (unsigned char) ((lo >>  8) & 0xFF);
+            outrow[colChar*7+6] =  (unsigned char) ((lo >>  0) & 0xFF);
         }
         break;
-    case 4: default:
-        /*  Unlike the above cases, we iterate through outrow.  The color
-            composition of each outrow byte is computed by consulting
-            a single bit or two consecutive bits in inrow. 
+
+    case 8:
+        for (colChar = 0; colChar < inColChars; ++colChar) { 
+            unsigned int i;
+            for (i = 0; i < 8; ++i) {
+                outrow[colChar*8+i] =
+                    ((inrow[colChar] >> (7-i)) & 0x01) *0xFF;
+            }
+        }
+        break;
+
+    case 9:
+        for (colChar = 0; colChar < inColChars; ++colChar) { 
+            outrow[colChar*9]   =  ((inrow[colChar] >> 7) & 0x01) * 0xFF;
+            outrow[colChar*9+1] =  pair[(inrow[colChar] >> 6) & 0x03] >> 1; 
+            outrow[colChar*9+2] =  pair[(inrow[colChar] >> 5) & 0x03] >> 2; 
+            outrow[colChar*9+3] =  pair[(inrow[colChar] >> 4) & 0x03] >> 3; 
+            outrow[colChar*9+4] =  pair[(inrow[colChar] >> 3) & 0x03] >> 4; 
+            outrow[colChar*9+5] =  pair[(inrow[colChar] >> 2) & 0x03] >> 5; 
+            outrow[colChar*9+6] =  pair[(inrow[colChar] >> 1) & 0x03] >> 6; 
+            outrow[colChar*9+7] =  pair[(inrow[colChar] >> 0) & 0x03] >> 7; 
+            outrow[colChar*9+8] =  (inrow[colChar] & 0x01) * 0xFF;
+        }
+        break;
+
+    case 10:
+        for (colChar = 0; colChar < inColChars; ++colChar) { 
+            outrow[colChar*10]   = ((inrow[colChar] >> 7) & 0x01 ) * 0xFF;
+            outrow[colChar*10+1] = pair[(inrow[colChar] >> 6) & 0x03] >> 2; 
+            outrow[colChar*10+2] = pair[(inrow[colChar] >> 5) & 0x03] >> 4; 
+            outrow[colChar*10+3] = pair[(inrow[colChar] >> 4) & 0x03] >> 6;
+            outrow[colChar*10+4] = ((inrow[colChar] >> 4) & 0x01) * 0xFF;
+            outrow[colChar*10+5] = ((inrow[colChar] >> 3) & 0x01) * 0xFF; 
+            outrow[colChar*10+6] = pair[(inrow[colChar] >> 2) & 0x03] >> 2; 
+            outrow[colChar*10+7] = pair[(inrow[colChar] >> 1) & 0x03] >> 4; 
+            outrow[colChar*10+8] = pair[(inrow[colChar] >> 0) & 0x03] >> 6; 
+            outrow[colChar*10+9] = ((inrow[colChar] >> 0) & 0x01) * 0xFF;
+        }
+        break;
+ 
+
+    default:
+        /*  Unlike the above cases, we iterate through outrow.  To compute the
+            color composition of each outrow byte, we consult a single bit or
+            two consecutive bits in inrow.
+
             Color changes never happen twice in a single outrow byte.
+
+            This is a generalization of above routines for scale factors
+            9 and 10.
+
+            Logic works for scale factors 4, 6, 7, 8, and above (but not 5).
         */
+
         for (colChar = 0; colChar < outColChars; ++colChar) {
             unsigned int const mult = scaleFactor;
             unsigned int const mod = colChar % mult;
@@ -199,12 +295,14 @@ enlargePbm(struct pam * const inpamP,
     
     if (scaleFactor == 1)
         outrow = inrow;
-    else  
-        outrow = pbm_allocrow_packed(outcols + 32);
-            /* The 32 (=4 bytes) is to allow writes beyond outrow data
-               end when scaleFactor is 2, 3, 5.  (max 4 bytes when
-               scaleFactor is 5)
-            */
+    else  {
+        /* Allow writes beyond outrow data end when scaleFactor is
+           one of the special fast cases: 2, 3, 4, 5, 6, 7, 8, 9, 10.
+        */
+        unsigned int const rightPadding = 
+            scaleFactor > 10 ? 0 : (scaleFactor - 1) * 8;  
+        outrow = pbm_allocrow_packed(outcols + rightPadding);
+    }
 
     pbm_writepbminit(ofP, outcols, outrows, 0);
     
@@ -214,10 +312,8 @@ enlargePbm(struct pam * const inpamP,
         pbm_readpbmrow_packed(inpamP->file, inrow, inpamP->width,
                               inpamP->format);
 
-        if (inpamP->width % 8 > 0) {  /* clean final partial byte */ 
-            inrow[inColChars-1] >>= 8 - inpamP->width % 8;
-            inrow[inColChars-1] <<= 8 - inpamP->width % 8;
-        }
+        if (outcols % 8 > 0)           /* clean final partial byte */ 
+            pbm_cleanrowend_packed(inrow, inpamP->width);
 
         enlargePbmRowHorizontally(inpamP, inrow, inColChars, outColChars,
                                   scaleFactor, outrow);
diff --git a/editor/pamenlarge.test b/editor/pamenlarge.test
deleted file mode 100644
index 7584af01..00000000
--- a/editor/pamenlarge.test
+++ /dev/null
@@ -1,8 +0,0 @@
-echo Test 1.  Should print 3424505894 913236
-./pamenlarge 3 ../testimg.ppm | cksum
-echo Test 2.  Should print 4152147096 304422
-ppmtopgm ../testimg.ppm | ./pamenlarge 3 | cksum
-echo Test 3.  Should print 3342398172 297
-./pamenlarge 3 ../testgrid.pbm | cksum
-echo Test 4.  Should print 237488670 3133413
-./pamenlarge 3 -plain ../testimg.ppm | cksum
diff --git a/editor/pamflip.test b/editor/pamflip.test
deleted file mode 100644
index 96e889ea..00000000
--- a/editor/pamflip.test
+++ /dev/null
@@ -1,12 +0,0 @@
-echo Test 1.  Should print 2116496681 101484
-./pamflip -lr ../testimg.ppm | cksum 
-echo Test 2.  Should print 217037000 101484
-./pamflip -cw ../testimg.ppm | cksum
-echo Test 3.  Should print 2052917888 101484
-./pamflip -tb ../testimg.ppm | cksum
-echo Test 4.  Should print 3375384165 41
-./pamflip -lr ../testgrid.pbm | cksum
-echo Test 5.  Should print 604323149 41
-./pamflip -tb ../testgrid.pbm | cksum
-echo Test 6.  Should print 490797850 37
-./pamflip -cw ../testgrid.pbm | cksum
diff --git a/editor/pamflip/Makefile b/editor/pamflip/Makefile
new file mode 100644
index 00000000..ce3345f1
--- /dev/null
+++ b/editor/pamflip/Makefile
@@ -0,0 +1,36 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = editor/pamflip
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+default: all
+
+include $(BUILDDIR)/config.mk
+
+SUBDIRS =
+
+MERGEBINARIES = pamflip
+
+PORTBINARIES = pamflip
+
+BINARIES = $(PORTBINARIES)
+
+SCRIPTS =
+
+ADDL_OBJECTS = pamflip_sse.o
+
+OBJECTS = pamflip.o $(ADDL_OBJECTS)
+
+MERGE_OBJECTS = $(OBJECTS:%.o=%.o2)
+
+include $(SRCDIR)/common.mk
+
+.PHONY: all
+all: $(BINARIES) $(SUBDIRS:%=%/all)
+
+pamflip_sse.o pamflip_sse.o2: CFLAGS_TARGET = $(CFLAGS_SSE)
+pamflip.o pamflip.o2: CFLAGS_TARGET = $(CFLAGS_SSE)
+
+pamflip:%:%.o $(ADDL_OBJECTS)
diff --git a/editor/pamflip/config.h b/editor/pamflip/config.h
new file mode 100644
index 00000000..42aefb6e
--- /dev/null
+++ b/editor/pamflip/config.h
@@ -0,0 +1,7 @@
+#ifndef SSE_PBM_XY_FLIP
+  #if WANT_SSE && HAVE_WORKING_SSE2
+    #define SSE_PBM_XY_FLIP 1
+  #else
+    #define SSE_PBM_XY_FLIP 0
+  #endif
+#endif
diff --git a/editor/pamflip/flip.h b/editor/pamflip/flip.h
new file mode 100644
index 00000000..612a7f84
--- /dev/null
+++ b/editor/pamflip/flip.h
@@ -0,0 +1,16 @@
+#ifndef FLIP_H_INCLUDED
+#define FLIP_H_INCLUDED
+
+struct xformCore {
+    /* a b
+       c d
+    */
+    int a;  /* -1, 0, or 1 */
+    int b;  /* -1, 0, or 1 */
+    int c;  /* -1, 0, or 1 */
+    int d;  /* -1, 0, or 1 */
+};
+
+
+
+#endif
diff --git a/editor/pamflip.c b/editor/pamflip/pamflip.c
index 1c479e54..149ab310 100644
--- a/editor/pamflip.c
+++ b/editor/pamflip/pamflip.c
@@ -12,7 +12,7 @@
 
 /*
    transformNonPbmChunk() is the general transformation function.
-   It can tranform anything, albeit slowly and expensively.
+   It can transform anything, albeit slowly and expensively.
    
    The following are enhancements for specific cases:
    
@@ -26,6 +26,10 @@
      transformRowsBottomTopNonPbm()
        non-PBM image with bottom-top transformation
        (also works for PBM, but we don't use it)
+     transformRowsToColumnsPbmSse()
+       PBM image with column-for-row transformation
+       requires Intel/AMD x86 SSE2
+       (can only do 90 degree/xy flips)
      transformPbm()
        PBM image with column-for-row transformation
        (also works for all other transformations, but we don't use it)
@@ -68,6 +72,10 @@
 #include "nstring.h"
 #include "bitreverse.h"
 
+#include "config.h"  /* Defines SSE_PBM_XY_FLIP */
+#include "flip.h"
+#include "pamflip_sse.h"
+
 enum xformType {LEFTRIGHT, TOPBOTTOM, TRANSPOSE};
 
 static void
@@ -92,7 +100,7 @@ parseXformOpt(const char *     const xformOpt,
     xformCount = 0; /* initial value */
     while (!eol && xformCount < 10) {
         const char * token;
-        token = strsepN(&cursor, ",");
+        token = pm_strsep(&cursor, ",");
         if (token) {
             if (streq(token, "leftright"))
                 xformList[xformCount++] = LEFTRIGHT;
@@ -115,25 +123,13 @@ parseXformOpt(const char *     const xformOpt,
 
 
 
-/* See transformPoint() for an explanation of the transform matrix types.
-   The difference between the two types is that 'xformCore' is particular
-   to the source image dimensions and can be used to do the transformation,
-   while 'xformCore' is independent of the source image and just
-   tells what kind of transformation.
+/* See transformPoint() for an explanation of the transform matrix types.  The
+   difference between xformCore and xformMatrix is that 'xformCore' is
+   particular to the source image dimensions and can be used to do the
+   transformation, while 'xformCore' is independent of the source image and
+   just tells what kind of transformation.
 */
 
-struct xformCore {
-    /* a b
-       c d
-    */
-    int a;  /* -1, 0, or 1 */
-    int b;  /* -1, 0, or 1 */
-    int c;  /* -1, 0, or 1 */
-    int d;  /* -1, 0, or 1 */
-};
-
-
-
 struct xformMatrix {
     /* a b 0
        c d 0
@@ -276,7 +272,7 @@ interpretMemorySize(unsigned int const memsizeSpec,
         if (memsizeOpt > sizeMax / Meg)
             pm_error("-memsize value too large: %u MiB.  Maximum this program "
                      "can handle is %u MiB", 
-                     memsizeOpt, sizeMax / Meg);
+                     memsizeOpt, (unsigned)sizeMax / Meg);
         *availableMemoryP = memsizeOpt * Meg;
     } else
         *availableMemoryP = sizeMax;
@@ -337,7 +333,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We don't parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (lr + tb + xy + r90 + r180 + r270 + null > 1)
@@ -387,6 +383,8 @@ parseCommandLine(int argc, char ** const argv,
                  "specified %d", argc-1);
     else
         cmdlineP->inputFilespec = argv[1];
+
+    free(option_def);
 }
 
 
@@ -397,6 +395,9 @@ bitOrderReverse(unsigned char * const bitrow,
 /*----------------------------------------------------------------------------
   Reverse the bits in a packed pbm row (1 bit per pixel).  I.e. the leftmost
   bit becomes the rightmost, etc.
+
+  Exchange pixels in units of eight.  If both are zero, skip instead of
+  exchanging zeros.
 -----------------------------------------------------------------------------*/
     unsigned int const lastfullByteIdx = cols/8 - 1;
 
@@ -407,11 +408,14 @@ bitOrderReverse(unsigned char * const bitrow,
         bitrow[0] = bitreverse[bitrow[0]] << (8-cols);
     else if (cols % 8 == 0) {
         unsigned int i, j;
-        for (i = 0, j = lastfullByteIdx; i <= j; ++i, --j) {
-            unsigned char const t = bitreverse[bitrow[j]]; 
-            bitrow[j] = bitreverse[bitrow[i]];
-            bitrow[i] = t;
-        }
+        for (i = 0, j = lastfullByteIdx; i <= j; ++i, --j)
+            if ((bitrow[j] | bitrow[i]) == 0) {
+                /* Both are 0x00 - skip */
+            } else {
+                unsigned char const t = bitreverse[bitrow[j]]; 
+                bitrow[j] = bitreverse[bitrow[i]];
+                bitrow[i] = t;
+            }
     } else {
         unsigned int const m = cols % 8; 
 
@@ -422,18 +426,23 @@ bitOrderReverse(unsigned char * const bitrow,
         unsigned char th, tl;  /* 16 bit temp ( th << 8 | tl ) */
         tl = 0;
         for (i = 0, j = lastfullByteIdx+1; i <= lastfullByteIdx/2; ++i, --j) {
-            th = bitreverse[bitrow[i]];
-            bitrow[i] =
-                bitreverse[0xff & ((bitrow[j-1] << 8 | bitrow[j]) >> (8-m))];
-            bitrow[j] = 0xff & ((th << 8 | tl) >> m);
-            tl = th;
+            if( (tl | bitrow[i] | bitrow[j] | bitrow[j-1]) != 0) {
+                /* Skip if both are 0x00 */
+                th = bitreverse[bitrow[i]];
+                bitrow[i] =
+                    bitreverse[0xff & ((bitrow[j-1] << 8 
+                                        | bitrow[j]) >> (8-m))];
+                bitrow[j] = 0xff & ((th << 8 | tl) >> m);
+                tl = th;
+            }
         }
-        if (i == j) 
+        if (i == j && (bitrow[i] | tl) != 0) {
             /* bitrow[] has an odd number of bytes (an even number of
                full bytes; lastfullByteIdx is odd), so we did all but
                the center byte above.  We do the center byte now.
             */
             bitrow[j] = 0xff & ((bitreverse[bitrow[i]] << 8 | tl) >> m);
+        }
     }
 }
 
@@ -636,6 +645,10 @@ writeRaster(struct pam *    const pamP,
 
 
 
+
+
+
+
 static void
 transformPbmGen(struct pam *     const inpamP,
                 struct pam *     const outpamP,
@@ -656,28 +669,44 @@ transformPbmGen(struct pam *     const inpamP,
             
     computeXformMatrix(&xform, inpamP->width, inpamP->height, xformCore);
     
-    bitrow = pbm_allocrow(inpamP->width);
-    newbits = pbm_allocarray(pbm_packed_bytes(outpamP->width), 
-                             outpamP->height);
+    bitrow = pbm_allocrow_packed(inpamP->width);
+    newbits = pbm_allocarray_packed( outpamP->width, outpamP->height );
             
     /* Initialize entire array to zeroes.  One bits will be or'ed in later */
     for (row = 0; row < outpamP->height; ++row) {
         unsigned int col;
         for (col = 0; col < pbm_packed_bytes(outpamP->width); ++col) 
-             newbits[row][col] = 0; 
+            newbits[row][col] = 0; 
     }
     
     for (row = 0; row < inpamP->height; ++row) {
         unsigned int col;
-        pbm_readpbmrow(inpamP->file, bitrow, inpamP->width, inpamP->format);
-        for (col = 0; col < inpamP->width; ++col) {
-            unsigned int newcol, newrow;
-            transformPoint(col, row, xform, &newcol, &newrow);
-            newbits[newrow][newcol/8] |= bitrow[col] << (7 - newcol % 8);
-                /* Use of "|=" patterned after pbm_readpbmrow_packed. */
-         }
+
+        pbm_readpbmrow_packed(inpamP->file, bitrow,
+                              inpamP->width, inpamP->format);
+        for (col = 0; col < inpamP->width; ) {
+            if (bitrow[col/8] == 0x00) 
+                col += 8;  /* Blank.   Skip to next byte. */
+            else {      /* Examine each pixel. */
+                unsigned int const colLimit = MIN(col+8, inpamP->width);
+                unsigned int i;
+
+                for (i = 0; col < colLimit; ++i, ++col) {
+                    bool const bitIsOne = (bitrow[col/8] >> (7-i)) & 0x01;
+                    if (bitIsOne) {
+                        /* Write in only the one bits. */  
+                        unsigned int newcol, newrow;
+                        transformPoint(col, row, xform, &newcol, &newrow);
+                        newbits[newrow][newcol/8] |= 0x01 << (7 - newcol % 8);
+                            /* Use of "|=" patterned after
+                               pbm_readpbmrow_packed().
+                            */
+                    }
+                }
+            }
+        }
     }
-    
+
     for (row = 0; row < outpamP->height; ++row)
         pbm_writepbmrow_packed(outpamP->file, newbits[row], outpamP->width, 0);
     
@@ -745,7 +774,7 @@ typedef struct {
 /*----------------------------------------------------------------------------
    A description of the quilt of cells that make up the output image.
 -----------------------------------------------------------------------------*/
-    unsigned int ranks, files;
+    unsigned int rankCt, fileCt;
         /* Dimensions of the output image in cells */
     struct pam ** pam;
         /* pam[y][x] is the pam structure for the cell at rank y, file x
@@ -769,8 +798,12 @@ initOutCell(struct pam *     const outCellPamP,
             unsigned int     const inCellHeight,
             struct pam *     const inpamP,
             struct xformCore const xformCore) {
-
-    unsigned int outCellFiles, outCellRanks;
+/*----------------------------------------------------------------------------
+   Set up an output cell.  Create and open a temporary file to hold its
+   raster.  Figure out the dimensions of the cell.  Return a PAM structure
+   that describes the cell (including identifying that temporary file).
+-----------------------------------------------------------------------------*/
+    unsigned int outCellFileCt, outCellRankCt;
 
     *outCellPamP = *inpamP;  /* initial value */
 
@@ -779,10 +812,10 @@ initOutCell(struct pam *     const outCellPamP,
     outCellPamP->file = pm_tmpfile();
 
     xformDimensions(xformCore, inCellWidth, inCellHeight,
-                    &outCellFiles, &outCellRanks);
+                    &outCellFileCt, &outCellRankCt);
 
-    outCellPamP->width = outCellFiles;
-    outCellPamP->height = outCellRanks;
+    outCellPamP->width = outCellFileCt;
+    outCellPamP->height = outCellRankCt;
 }
 
 
@@ -792,9 +825,26 @@ createOutputMap(struct pam *       const inpamP,
                 unsigned int       const maxRows,
                 struct xformMatrix const cellXform,
                 struct xformCore   const xformCore) {
-
-    unsigned int const inCellFiles = 1;
-    unsigned int const inCellRanks = (inpamP->height + maxRows - 1) / maxRows;
+/*----------------------------------------------------------------------------
+   Create and return the output map.  That's a map of all the output cells
+   (from which the output image can be assembled, once those cells are filled
+   in).
+
+   The map contains the dimensions of each output cell as well as file
+   stream handles for the temporary files containing the pixels of each
+   cell.  We create and open those files.
+
+   Note that a complexity of determining cell dimensions (which we handle)
+   is that the input image isn't necessarily a whole multiple of the input
+   cell size, so the last cell may be short.
+
+   The map does not contain the mapping from input cells to output cells
+   (e.g. in a top-bottom transformation of a 10-cell image, input cell
+   0 maps to output cell 9); caller can compute that when needed from
+   'cellXform'.
+-----------------------------------------------------------------------------*/
+    unsigned int const inCellFileCt = 1;
+    unsigned int const inCellRankCt = (inpamP->height + maxRows - 1) / maxRows;
 
     outputMap * mapP;
     unsigned int outCellRank;
@@ -802,27 +852,27 @@ createOutputMap(struct pam *       const inpamP,
 
     MALLOCVAR_NOFAIL(mapP);
 
-    xformDimensions(xformCore, inCellFiles, inCellRanks,
-                    &mapP->files, &mapP->ranks);
+    xformDimensions(xformCore, inCellFileCt, inCellRankCt,
+                    &mapP->fileCt, &mapP->rankCt);
 
-    MALLOCARRAY(mapP->pam, mapP->ranks);
+    MALLOCARRAY(mapP->pam, mapP->rankCt);
     if (mapP->pam == NULL)
         pm_error("Could not allocate a cell array for %u ranks of cells.",
-                 mapP->ranks);
+                 mapP->rankCt);
 
-    for (outCellRank = 0; outCellRank < mapP->ranks; ++outCellRank) {
+    for (outCellRank = 0; outCellRank < mapP->rankCt; ++outCellRank) {
 
-        MALLOCARRAY(mapP->pam[outCellRank], mapP->files);
+        MALLOCARRAY(mapP->pam[outCellRank], mapP->fileCt);
 
         if (mapP->pam[outCellRank] == NULL)
             pm_error("Failed to allocate rank %u of the cell array, "
-                     "%u cells wide", outCellRank, mapP->files);
+                     "%u cells wide", outCellRank, mapP->fileCt);
     }
 
-    for (inCellRank = 0; inCellRank < inCellRanks; ++inCellRank) {
+    for (inCellRank = 0; inCellRank < inCellRankCt; ++inCellRank) {
         unsigned int const inCellFile = 0;
         unsigned int const inCellStartRow = inCellRank * maxRows;
-        unsigned int const inCellRows =
+        unsigned int const inCellRowCt =
             MIN(inpamP->height - inCellStartRow, maxRows);
 
         unsigned int outCellFile, outCellRank;
@@ -830,7 +880,7 @@ createOutputMap(struct pam *       const inpamP,
                        &outCellFile, &outCellRank);
     
         initOutCell(&mapP->pam[outCellRank][outCellFile],
-                    inpamP->width, inCellRows,
+                    inpamP->width, inCellRowCt,
                     inpamP, xformCore);
     }
     return mapP;
@@ -843,7 +893,7 @@ destroyOutputMap(outputMap * const mapP) {
 
     unsigned int outCellRank;
 
-    for (outCellRank = 0; outCellRank < mapP->ranks; ++outCellRank)
+    for (outCellRank = 0; outCellRank < mapP->rankCt; ++outCellRank)
         free(mapP->pam[outCellRank]);
 
     free(mapP->pam);
@@ -858,9 +908,9 @@ rewindCellFiles(outputMap * const outputMapP) {
 
     unsigned int outCellRank;
 
-    for (outCellRank = 0; outCellRank < outputMapP->ranks; ++outCellRank) {
+    for (outCellRank = 0; outCellRank < outputMapP->rankCt; ++outCellRank) {
         unsigned int outCellFile;
-        for (outCellFile = 0; outCellFile < outputMapP->files; ++outCellFile)
+        for (outCellFile = 0; outCellFile < outputMapP->fileCt; ++outCellFile)
             pm_seek(outputMapP->pam[outCellRank][outCellFile].file, 0);
     }
 }
@@ -872,9 +922,9 @@ closeCellFiles(outputMap * const outputMapP) {
 
     unsigned int outCellRank;
 
-    for (outCellRank = 0; outCellRank < outputMapP->ranks; ++outCellRank) {
+    for (outCellRank = 0; outCellRank < outputMapP->rankCt; ++outCellRank) {
         unsigned int outCellFile;
-        for (outCellFile = 0; outCellFile < outputMapP->files; ++outCellFile)
+        for (outCellFile = 0; outCellFile < outputMapP->fileCt; ++outCellFile)
             pm_close(outputMapP->pam[outCellRank][outCellFile].file);
     }
 }
@@ -932,7 +982,7 @@ stitchCellsToOutput(outputMap *  const outputMapP,
 
     tupleRow = pnm_allocpamrow(outpamP);
 
-    for (outRank = 0; outRank < outputMapP->ranks; ++outRank) {
+    for (outRank = 0; outRank < outputMapP->rankCt; ++outRank) {
         unsigned int const cellRows = outputMapP->pam[outRank][0].height;
             /* Number of rows in every cell in this rank */
 
@@ -944,7 +994,7 @@ stitchCellsToOutput(outputMap *  const outputMapP,
 
             outCol = 0;
 
-            for (outFile = 0; outFile < outputMapP->files; ++outFile) {
+            for (outFile = 0; outFile < outputMapP->fileCt; ++outFile) {
                 struct pam * const outCellPamP = 
                     &outputMapP->pam[outRank][outFile];
 
@@ -973,7 +1023,7 @@ transformNonPbmChunk(struct pam *     const inpamP,
                      unsigned int     const maxRows,
                      bool             const verbose) {
 /*----------------------------------------------------------------------------
-  Same as transformNonPbmChunk(), except we read 'maxRows' rows of the
+  Same as transformNonPbmWhole(), except we read 'maxRows' rows of the
   input into memory at a time, storing intermediate results in temporary
   files, to limit our use of virtual and real memory.
 
@@ -981,28 +1031,33 @@ transformNonPbmChunk(struct pam *     const inpamP,
   header).
 
   We call the strip of 'maxRows' rows that we read a source cell.  We
-  transform that cell according to 'xformCore' to create to create a
+  transform that cell according to 'xformCore' to create a
   target cell.  We store all the target cells in temporary files.
   We consider the target cells to be arranged in a column matrix the
   same as the source cells within the source image; we transform that
   matrix according to 'xformCore'.  The resulting cell matrix is the
   target image.
 -----------------------------------------------------------------------------*/
-    unsigned int const inCellFiles = 1;
-    unsigned int const inCellRanks = (inpamP->height + maxRows - 1) / maxRows;
+    /* The cells of the source image ("inCell") are in a 1-column matrix.
+       "rank" is the vertical position of a cell in that matrix; "file" is
+       the horizontal position (always 0, of course).
+    */
+    unsigned int const inCellFileCt = 1;
+    unsigned int const inCellRankCt = (inpamP->height + maxRows - 1) / maxRows;
 
     struct xformMatrix cellXform;
     unsigned int inCellRank;
     outputMap * outputMapP;
 
     if (verbose)
-        pm_message("Transforming in %u chunks, using temp files", inCellRanks);
+        pm_message("Transforming in %u chunks, using temp files",
+                   inCellRankCt);
 
-    computeXformMatrix(&cellXform, inCellFiles, inCellRanks, xformCore);
+    computeXformMatrix(&cellXform, inCellFileCt, inCellRankCt, xformCore);
 
     outputMapP = createOutputMap(inpamP, maxRows, cellXform, xformCore);
 
-    for (inCellRank = 0; inCellRank < inCellRanks; ++inCellRank) {
+    for (inCellRank = 0; inCellRank < inCellRankCt; ++inCellRank) {
         unsigned int const inCellFile = 0;
         unsigned int const inCellStartRow = inCellRank * maxRows;
         unsigned int const inCellRows =
@@ -1083,11 +1138,15 @@ transformPbm(struct pam *     const inpamP,
                through them only twice, so there is no page thrashing concern.
             */
             transformRowsBottomTopPbm(inpamP, outpamP, xform.a == -1);
-    } else
+    } else {
         /* This is a column-for-row type of transformation, which requires
            complex traversal of an in-memory image.
         */
-        transformPbmGen(inpamP, outpamP, xform);
+        if (SSE_PBM_XY_FLIP)
+            pamflip_transformRowsToColumnsPbmSse(inpamP, outpamP, xform);
+        else
+            transformPbmGen(inpamP, outpamP, xform);
+    }
 }
 
 
diff --git a/editor/pamflip/pamflip_sse.c b/editor/pamflip/pamflip_sse.c
new file mode 100644
index 00000000..e0929f65
--- /dev/null
+++ b/editor/pamflip/pamflip_sse.c
@@ -0,0 +1,429 @@
+/*=============================================================================
+                                   pamflip_sse.c
+===============================================================================
+  This is part of the Pamflip program.  It contains code that exploits
+  the SSE facility of some CPUs.
+
+  This code was originally written by Akira Urushibata ("Douso") in 2010 and is
+  contributed to the public domain by all authors.
+
+  The author makes the following request (which is not a reservation of legal
+  rights): Please study the code and make adjustments to meet specific needs.
+  This part is critical to performance.  I have seen code copied from
+  elsewhere poorly implemented.  A lot of work goes into the development of
+  free software.  It is sad to see derivative work which fails to reach its
+  potential.  Please put a comment in the code so people will know where it
+  came from.
+
+=============================================================================*/
+
+#include <assert.h>
+
+#include "pm_config.h"
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "pam.h"
+
+#include "config.h"  /* Defines SSE_PBM_XY_FLIP */
+#include "flip.h"
+
+#include "pamflip_sse.h"
+
+/* Note that WANT_SSE implies the user expects SSE to be available
+   (i.e. <emmintrin.h> exists).
+*/
+
+#if SSE_PBM_XY_FLIP
+
+/*----------------------------------------------------------------------------
+   This is a specialized routine for row-for-column PBM transformations.
+   (-cw, -ccw, -xy).  It requires GCC (>= v. 4.2.0) and SSE2. 
+
+   In each cycle, we read sixteen rows from the input.  We process this band
+   left to right in blocks 8 pixels wide.  We use the SSE2 instruction
+   pmovmskb128, which reports the MSB of each byte in a 16 byte array, for
+   fast processing.  We place the 8x16 block into a 16 byte array, and
+   pmovmskb128 reports the 16 pixels on the left edge in one instruction
+   execution.  pslldi128 shifts the array contents leftward.
+
+   The following routines can write both in both directions (left and right)
+   into the output rows.  They do this by controlling the vertical stacking
+   order when they make the 8x16 blocks.
+
+   We do all transposition in 8x16 block units, adding padding to
+   the 8 row input buffer and the output plane raster as necessary.
+   doPartialBlockTop() or doPartialBlockBottom() handles the partial
+   input band.  This part can come from either the top or bottom of the
+   vertical input column, but always goes to the right end of the output
+   rows.
+
+   As an enhancement, we clear the output raster to zero (=white) in the
+   beginning and flip only the 8x16 blocks that contain non-zero bits (=any
+   amount of black pixels).  When we add padding to the edges, we initialize
+   it all to zero to prevent unnecessary transpositions.  Because most
+   real-world documents are largely white, this saves much execution time.  If
+   you are porting this code to an environment in which non-zero bits are the
+   majority, for example, BMP where zero means black, you should seriously
+   consider modifying this.
+
+   All instructions unique to GCC/SSE are in transpose16Bitrows().
+   It is possible to write a non-SSE version by providing a generic
+   version of transpose16Bitrows() or one tuned for a specific
+   architecture.  Use 8x8 blocks to avoid endian issues.
+ 
+   Further enhancement should be possible by employing wider bands,
+   larger blocks as wider SIMD registers become available.  Clearing
+   the white parts after instead of before transposition is also a
+   possibility.
+-----------------------------------------------------------------------------*/
+
+#include <emmintrin.h>
+
+typedef char v16qi __attribute__ ((vector_size (16)));
+typedef int  v4di  __attribute__ ((vector_size (16)));
+
+/* Beware when making modifications to code which involve SSE.
+   This is a sensitive part of GCC.  Different compiler versions
+   respond differently to trivial matters such as the difference
+   between above v16qi, v4di and a union defined for handling both.
+   What can be placed into a register is another issue.  Some
+   compilers issue warnings, others abort with error.
+
+   A char array cannot be loaded into v16qi by casting.  A vector
+   variable must be vector from the beginning.
+
+   Changes for your local system are okay, but if you intend to
+   publish them, please specify the compiler version you used.
+
+   This code has been tested on gcc versions 4.2.0, 4.2.4, 4.3.2,
+   4.4.3, 4.4.4, 4.5.0, 4.5.2, 4.6.0 and 4.6.1 clang versions
+   3.0, 3.2, 3.3.
+
+   We use SSE instructions in "_mm_" form in favor of "__builtin_".
+   In GCC the "__builtin_" form is documented but "_mm_" is not.
+   Former versions of this source file used "__builtin_".  This was
+   changed to make possible compilation with clang.
+
+   _mm_slli_epi32 : __builtin_ia32_pslldi128
+   _mm_cmpeq_epi8 : __builtin_ia32_pcmpeqb128
+   _mm_movemask_epi8 : __builtin_ia32_pmovmskb128
+
+   The conversion requires <emmintrin.h> .
+
+*/
+
+
+
+static void
+transpose16Bitrows(unsigned int const cols,
+                   unsigned int const rows,
+                   const bit *  const block[16],
+                   uint16_t **  const outplane,
+                   unsigned int const outcol16) {
+/*--------------------------------------------------------------------------
+  Convert input rows to output columns.  Works in units of 8x16.
+
+  Uses pre-calculated pointers ( block[i][col8] ) instead of
+  (xdir > 0) ? (i & 0x08) + 7 - (i & 0x07) : (24 - rows % 16 +i) % 16
+  for efficiency.
+
+  We load the block directly into a register.  (using a union like:
+
+       union V16 {
+          v16qi v;
+          unsigned char i[16];
+       };
+  )
+
+  gcc (v. 4.2, 4.4) sees the suffix [x] of v16.i[x] and apparently decides
+  that the variable has to be addressable and therefore needs to be placed
+  into memory.)
+---------------------------------------------------------------------------*/
+    unsigned int col;
+    register v16qi zero128;   /* 16 bytes of zero, in a SSE register */
+
+    zero128 = zero128 ^ zero128;
+
+    for (col = 0; col < cols; col += 8) {
+        unsigned int const col8 = col / 8;
+
+        register v16qi vReg = {
+            block[0][col8],  block[1][col8],
+            block[2][col8],  block[3][col8],  
+            block[4][col8],  block[5][col8],
+            block[6][col8],  block[7][col8],
+            block[8][col8],  block[9][col8],
+            block[10][col8], block[11][col8],
+            block[12][col8], block[13][col8],
+            block[14][col8], block[15][col8] };
+
+        register __m128i const compare =
+            _mm_cmpeq_epi8((__m128i)vReg, (__m128i)zero128);
+
+        if (_mm_movemask_epi8(compare) != 0xffff) {
+
+            /* There is some black content in this block; write to outplane */
+            
+            unsigned int outrow;
+            unsigned int i;
+
+            outrow = col;  /* initial value */
+
+            for (i = 0; i < 7; ++i) {
+                /* GCC (>=4.2) automatically unrolls this loop */  
+                outplane[outrow++][outcol16] =
+                    _mm_movemask_epi8((__m128i)vReg);
+                vReg = (v16qi)_mm_slli_epi32((__m128i)vReg, 1);
+            }
+            outplane[outrow][outcol16] = _mm_movemask_epi8((__m128i)vReg);
+        } else {
+            /* The block is completely white; skip. */
+        }
+    }
+}
+
+
+
+static void
+analyzeBlock(const struct pam * const inpamP,
+             bit **             const inrow,
+             int                const xdir,
+             const bit **       const block,
+             const bit **       const blockPartial,
+             unsigned int *     const topOfFullBlockP,
+             unsigned int *     const outcol16P) {
+/*--------------------------------------------------------------------------
+  Set up block[] pointer array.  Provide for both directions and the two
+  "twists" brought about by Intel byte ordering which occur when:
+    (1) 16 bytes are loaded to a SSE register
+    (2) 16 bits are written to memory.
+ 
+  If 'rows' is not a multiple of 8, a partial input band appears at one edge.
+  Set *topOfFullBlockP accordingly.  blockPartial[] is an adjusted "block" for
+  this partial band, brought up to a size of 8 rows.  The extra pointers point
+  to a single row which doPartialBlockTop() and doPartialBlockBottom() clear
+  to white.
+---------------------------------------------------------------------------*/
+    if (xdir > 0){
+        /* Write output columns left to right */
+        unsigned int i;
+        for (i = 0; i < 16; ++i){
+            unsigned int const index = (i & 0x8) + 7 - (i & 0x7);
+            /* Absorb little-endian "twists" */
+            block[i] = inrow[index];
+            blockPartial[i] = index < inpamP->height%16 ? block[i] : inrow[15];
+        }
+        *topOfFullBlockP = 0;
+        *outcol16P = 0;
+    } else {
+        /* Write output columns right to left */
+        unsigned int i;
+        for (i = 0; i < 16; ++i){
+            unsigned int const index = ((i & 0x8) ^ 0x8) + (i & 0x7);
+            /* Absorb little-endian "twists" */
+            block[i]= inrow[index];
+            blockPartial[i] = index < (16-inpamP->height%16)
+                ? inrow[0]
+                : block[i];
+        }
+        *topOfFullBlockP = inpamP->height % 16;
+    
+        if (inpamP->height >= 16) {
+            *outcol16P = inpamP->height/16 - 1;
+        } else
+            *outcol16P = 0;
+    }
+}
+
+
+
+static void
+doPartialBlockTop(const struct pam * const inpamP,
+                  bit **             const inrow,
+                  const bit *        const blockPartial[16],
+                  unsigned int       const topOfFullBlock,
+                  uint16_t **        const outplane) {
+    
+    if (topOfFullBlock > 0) {
+        unsigned int colChar, row;
+        unsigned int pad = 16 - topOfFullBlock;
+
+        for (colChar=0; colChar < pbm_packed_bytes(inpamP->width); ++colChar)
+            inrow[0][colChar] = 0x00;
+
+        for (row = 0; row < topOfFullBlock; ++row){
+            pbm_readpbmrow_packed(inpamP->file, inrow[row+pad],
+                                  inpamP->width, inpamP->format);
+            if (inpamP->width % 8 > 0){
+                /* Clear partial byte at end of input row */
+                int const lastByte = pbm_packed_bytes(inpamP->width) -1;
+
+                inrow[row+pad][lastByte] >>= (8 - inpamP->width % 8);
+                inrow[row+pad][lastByte] <<= (8 - inpamP->width % 8);
+            }
+        }
+
+        transpose16Bitrows(inpamP->width, inpamP->height, blockPartial,
+                           outplane, inpamP->height /16);
+            /* Transpose partial rows on top of input.  Place on right edge of
+               output.
+            */ 
+    }
+}
+
+
+
+static void
+doFullBlocks(const struct pam * const inpamP,
+             bit **             const inrow,
+             int                const xdir,
+             const bit *        const block[16],
+             unsigned int       const topOfFullBlock,
+             unsigned int       const initOutcol16,
+             uint16_t **        const outplane) {
+
+    unsigned int row;
+    unsigned int outcol16;
+    unsigned int modrow;
+    /* Number of current row within buffer */
+
+    for (row = topOfFullBlock, outcol16 = initOutcol16, modrow = 0;
+         row < inpamP->height;
+         ++row) {
+
+        pbm_readpbmrow_packed(inpamP->file, inrow[modrow],
+                              inpamP->width, inpamP->format);
+        if (inpamP->width % 8 > 0) {
+            /* Clear partial byte at end of input row */
+            int const lastByte = pbm_packed_bytes(inpamP->width) - 1;
+            inrow[modrow][lastByte] >>= (8 - inpamP->width % 8);
+            inrow[modrow][lastByte] <<= (8 - inpamP->width % 8);
+        }
+
+        ++modrow;
+        if (modrow == 16) {
+            /* 16 row buffer is full.  Transpose. */
+            modrow = 0; 
+
+            transpose16Bitrows(inpamP->width, inpamP->height,
+                               block, outplane, outcol16);
+            outcol16 += xdir;
+        }
+    }
+}
+
+
+
+static void
+doPartialBlockBottom(const struct pam * const inpamP,
+                     bit **             const inrow,
+                     int                const xdir,
+                     const bit *        const blockPartial[16],
+                     uint16_t **        const outplane) {
+    
+    if (xdir > 0 && inpamP->height % 16 > 0) {
+        unsigned int colChar;
+
+        for (colChar=0; colChar < pbm_packed_bytes(inpamP->width); ++colChar)
+            inrow[15][colChar] = 0x00;
+
+        transpose16Bitrows(inpamP->width, inpamP->height, blockPartial,
+                           outplane, inpamP->height/16);
+            /* Transpose partial rows on bottom of input.  Place on right edge
+               of output.
+            */ 
+    }
+}
+
+
+
+static void
+writeOut(const struct pam * const outpamP,
+         uint16_t **        const outplane,
+         int                const ydir) {
+             
+    unsigned int row;
+
+    for (row = 0; row < outpamP->height; ++row) {
+        unsigned int const outrow = (ydir > 0) ?
+            row :
+            outpamP->height - row - 1;  /* reverse order */
+
+        pbm_writepbmrow_packed(stdout, (bit *)outplane[outrow],
+                               outpamP->width, 0);
+    }
+}
+
+
+static void
+clearOutplane(const struct pam * const outpamP,
+              uint16_t **        const outplane) { 
+    
+    unsigned int row;
+    
+    for (row = 0; row < outpamP->height; ++row) {
+        unsigned int col16;  /* column divided by 16 */
+        for (col16 = 0; col16 < (outpamP->width + 15)/16; ++col16)
+            outplane[row][col16] = 0x0000;
+    }
+} 
+
+
+
+void
+pamflip_transformRowsToColumnsPbmSse(const struct pam * const inpamP,
+                                     const struct pam * const outpamP,
+                                     struct xformCore const xformCore) { 
+/*----------------------------------------------------------------------------
+  This is a specialized routine for row-for-column PBM transformations.
+  (-cw, -ccw, -xy).
+-----------------------------------------------------------------------------*/
+    int const xdir = xformCore.c;
+        /* Input top  => output left (+1)/ right (-1)  */
+    int const ydir = xformCore.b;
+        /* Input left => output top  (+1)/ bottom (-1) */
+    int const blocksPerRow = ((unsigned int) outpamP->width + 15) /16;
+
+    bit ** inrow;
+    uint16_t ** outplane;
+    const bit * block[16];
+    const bit * blockPartial[16];
+    unsigned int topOfFullBlock;
+    unsigned int outcol16;
+
+    inrow = pbm_allocarray_packed( inpamP->width, 16);
+    MALLOCARRAY2(outplane, outpamP->height + 7, blocksPerRow);
+    if (outplane == NULL)
+        pm_error("Could not allocate %u x %u array of 16 bit units",
+                 blocksPerRow, outpamP->height + 7);
+
+    /* We write to the output array in 16 bit units.  Add margin. */  
+
+    clearOutplane(outpamP, outplane);
+
+    analyzeBlock(inpamP, inrow, xdir, block, blockPartial, 
+                 &topOfFullBlock, &outcol16);
+
+    doPartialBlockTop(inpamP, inrow, blockPartial, topOfFullBlock, outplane);
+
+    doFullBlocks(inpamP, inrow, xdir, block,
+                 topOfFullBlock, outcol16, outplane);
+
+    doPartialBlockBottom(inpamP, inrow, xdir, blockPartial, outplane);
+
+    writeOut(outpamP, outplane, ydir);
+
+    pbm_freearray(outplane, outpamP->height + 7);
+    pbm_freearray(inrow, 16);
+}
+#else  /* WANT_SSE */
+
+void
+pamflip_transformRowsToColumnsPbmSse(const struct pam * const inpamP,
+                                     const struct pam * const outpamP,
+                                     struct xformCore   const xformCore) { 
+
+    /* Nobody is supposed to call this */
+    assert(false);
+}
+#endif 
diff --git a/editor/pamflip/pamflip_sse.h b/editor/pamflip/pamflip_sse.h
new file mode 100644
index 00000000..59e7c026
--- /dev/null
+++ b/editor/pamflip/pamflip_sse.h
@@ -0,0 +1,12 @@
+#ifndef PAMFLIP_SSE_H_INCLUDED
+#define PAMFLIP_SSE_H_INCLUDED
+
+struct pam;
+#include "flip.h"
+
+void
+pamflip_transformRowsToColumnsPbmSse(const struct pam *     const inpamP,
+                                     const struct pam *     const outpamP,
+                                     struct xformCore       const xformCore);
+
+#endif
diff --git a/editor/pamfunc.c b/editor/pamfunc.c
index b6e56e17..5945b82d 100644
--- a/editor/pamfunc.c
+++ b/editor/pamfunc.c
@@ -13,8 +13,8 @@
   multiply/divide where possible.  Especially when multiplying by an 
   integer.
 
-  2) For multiply/divide, give option of simply changing the maxval and
-  leaving the raster alone.
+  2) speed up by not transforming the raster in the idempotent cases
+  (e.g. multiply by one).
 
 ******************************************************************************/
 
@@ -23,7 +23,7 @@
 #include "shhopt.h"
 #include "pam.h"
 
-enum function {
+enum Function {
     FN_MULTIPLY,
     FN_DIVIDE,
     FN_ADD,
@@ -42,22 +42,23 @@ enum function {
    a "max" function.
 */
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* Filespec of input file */
-    enum function function;
+    const char * inputFileName;
+    enum Function function;
     union {
-        float multiplier;
-        float divisor;
-        int adder;
-        int subtractor;
+        float        multiplier;
+        float        divisor;
+        int          adder;
+        int          subtractor;
         unsigned int max;
         unsigned int min;
         unsigned int mask;
         unsigned int shiftCount;
     } u;
+    unsigned int changemaxval;
     unsigned int verbose;
 };
 
@@ -80,8 +81,8 @@ parseHex(const char * const hexString) {
          
 
 static void
-parseCommandLine(int argc, char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
@@ -126,13 +127,16 @@ parseCommandLine(int argc, char ** const argv,
             &shiftleftSpec,  0);
     OPTENT3(0,   "shiftright", OPT_UINT,   &cmdlineP->u.shiftCount,
             &shiftrightSpec, 0);
-    OPTENT3(0,   "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,       0);
+    OPTENT3(0,   "verbose",      OPT_FLAG,   NULL, &cmdlineP->verbose,
+            0);
+    OPTENT3(0,   "changemaxval", OPT_FLAG,   NULL, &cmdlineP->changemaxval,
+            0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (multiplierSpec + divisorSpec + adderSpec + subtractorSpec +
@@ -186,16 +190,17 @@ parseCommandLine(int argc, char ** const argv,
                  argc-1);
 
     if (argc-1 < 1)
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
     else 
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
     
+    free(option_def);
 }
 
 
 
 static bool
-isDyadicMaskFunction(enum function const fn) {
+isDyadicMaskFunction(enum Function const fn) {
 
     return (fn == FN_AND || fn == FN_OR || fn == FN_XOR);
 }
@@ -203,7 +208,7 @@ isDyadicMaskFunction(enum function const fn) {
 
 
 static bool
-isMaskFunction(enum function const fn) {
+isMaskFunction(enum Function const fn) {
 
     return (isDyadicMaskFunction(fn) || fn == FN_NOT);
 }
@@ -211,7 +216,7 @@ isMaskFunction(enum function const fn) {
 
 
 static bool
-isShiftFunction(enum function const fn) {
+isShiftFunction(enum Function const fn) {
 
     return (fn == FN_SHIFTLEFT || fn == FN_SHIFTRIGHT);
 }
@@ -219,7 +224,7 @@ isShiftFunction(enum function const fn) {
 
 
 static bool
-isBitstringFunction(enum function const fn) {
+isBitstringFunction(enum Function const fn) {
 
     return isMaskFunction(fn) || isShiftFunction(fn);
 }
@@ -227,7 +232,7 @@ isBitstringFunction(enum function const fn) {
 
 
 static void
-validateFunction(struct cmdlineInfo const cmdline,
+validateFunction(struct CmdlineInfo const cmdline,
                  const struct pam * const pamP) {
 
     if (isBitstringFunction(cmdline.function)) {
@@ -259,7 +264,58 @@ validateFunction(struct cmdlineInfo const cmdline,
 
 
 static void
-applyFunction(struct cmdlineInfo const cmdline,
+planTransform(struct CmdlineInfo const cmdline,
+              sample             const inputMaxval,
+              sample *           const outputMaxvalP,
+              bool *             const mustChangeRasterP) {
+/*----------------------------------------------------------------------------
+   Plan the transform described by 'cmdline', given the maxval of the input
+   image is 'inputMaxval.
+
+   The plan just consists of whether to change the maxval or the raster.
+   Some multiplications and divisions can be achieved just by changing the
+   maxval and leaving the samples in the raster alone.
+-----------------------------------------------------------------------------*/
+    if (cmdline.changemaxval) {
+        /* User allows us to change the maxval, if that makes it easier */
+        if (cmdline.function == FN_MULTIPLY || cmdline.function == FN_DIVIDE) {
+            float const multiplier =
+                cmdline.function == FN_MULTIPLY ? cmdline.u.multiplier :
+                (1/cmdline.u.divisor);
+
+            float const neededMaxval = inputMaxval / multiplier;
+
+            if (neededMaxval + 0.5 < inputMaxval) {
+                /* Lowering the maxval might make some of the sample values
+                   higher than the maxval, so we'd have to modify the raster
+                   to clip them.
+                */
+                *outputMaxvalP     = inputMaxval;
+                *mustChangeRasterP = true;
+            } else if (neededMaxval > PAM_OVERALL_MAXVAL) {
+                *outputMaxvalP     = inputMaxval;
+                *mustChangeRasterP = true;
+            } else {
+                *outputMaxvalP     = ROUNDU(neededMaxval);
+                *mustChangeRasterP = false;
+            }
+        } else {
+            *outputMaxvalP     = inputMaxval;
+            *mustChangeRasterP = true;
+        }
+    } else {
+        *outputMaxvalP     = inputMaxval;
+        *mustChangeRasterP = true;
+    }
+    if (*outputMaxvalP != inputMaxval)
+        pm_message("Changing maxval to %u because of -changemaxval",
+                   (unsigned)*outputMaxvalP);
+}
+
+
+
+static void
+applyFunction(struct CmdlineInfo const cmdline,
               struct pam         const inpam,
               struct pam         const outpam,
               tuple *            const inputRow,
@@ -275,10 +331,10 @@ applyFunction(struct cmdlineInfo const cmdline,
            divide, both cmdline.u.divisor and oneOverDivisor are
            meaningless.  
         */
-    int col;
+    unsigned int col;
 
     for (col = 0; col < inpam.width; ++col) {
-        int plane;
+        unsigned int plane;
         for (plane = 0; plane < inpam.depth; ++plane) {
             sample const inSample = inputRow[col][plane];
             sample outSample;  /* Could be > maxval  */
@@ -330,21 +386,22 @@ applyFunction(struct cmdlineInfo const cmdline,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     FILE * ifP;
     tuple * inputRow;   /* Row from input image */
     tuple * outputRow;  /* Row of output image */
-    int row;
-    struct cmdlineInfo cmdline;
+    unsigned int row;
+    struct CmdlineInfo cmdline;
     struct pam inpam;   /* Input PAM image */
     struct pam outpam;  /* Output PAM image */
+    bool mustChangeRaster;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
@@ -355,16 +412,21 @@ main(int argc, char *argv[]) {
     outpam = inpam;    /* Initial value -- most fields should be same */
     outpam.file = stdout;
 
+    planTransform(cmdline, inpam.maxval, &outpam.maxval, &mustChangeRaster);
+
     pnm_writepaminit(&outpam);
 
     outputRow = pnm_allocpamrow(&outpam);
 
-    for (row = 0; row < inpam.height; row++) {
+    for (row = 0; row < inpam.height; ++row) {
         pnm_readpamrow(&inpam, inputRow);
 
-        applyFunction(cmdline, inpam, outpam, inputRow, outputRow);
+        if (mustChangeRaster) {
+            applyFunction(cmdline, inpam, outpam, inputRow, outputRow);
 
-        pnm_writepamrow(&outpam, outputRow);
+            pnm_writepamrow(&outpam, outputRow);
+        } else
+            pnm_writepamrow(&outpam, inputRow);
     }
     pnm_freepamrow(outputRow);
     pnm_freepamrow(inputRow);
@@ -374,3 +436,5 @@ main(int argc, char *argv[]) {
     return 0;
 }
 
+
+
diff --git a/editor/pammasksharpen.c b/editor/pammasksharpen.c
index e61237ca..6e34ab20 100644
--- a/editor/pammasksharpen.c
+++ b/editor/pammasksharpen.c
@@ -17,7 +17,7 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** const argv,
+parseCommandLine(int argc, const char ** const argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
@@ -46,7 +46,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (sharpSpec) {
@@ -109,7 +109,7 @@ sharpened(sample const inputSample,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char * *argv) {
 
     struct cmdlineInfo cmdline;
     struct pam inpam;
@@ -126,8 +126,8 @@ main(int argc, char *argv[]) {
            which they will be considered identical.
         */
     
-    pnm_init(&argc, argv);
-
+    pm_proginit(&argc, argv);
+    
     parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.inputFilespec);
diff --git a/editor/pamperspective.c b/editor/pamperspective.c
index 6bf8314e..16715c2e 100644
--- a/editor/pamperspective.c
+++ b/editor/pamperspective.c
@@ -18,6 +18,7 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup is int string.h */
 
 #include <assert.h>
@@ -486,7 +487,7 @@ parseCommandLine(int argc, const char * argv[],
   opt.opt_table = option_def;
   opt.short_allowed = FALSE;
   opt.allowNegNum = TRUE;
-  optParseOptions3 (&argc, (char **)argv, opt, sizeof(opt), 0);
+  pm_optParseOptions3 (&argc, (char **)argv, opt, sizeof(opt), 0);
 
   /* The non-option arguments are optionally all eight coordinates
      and optionally the input filename
@@ -536,7 +537,7 @@ parseCommandLine(int argc, const char * argv[],
                            parse_enum (bool_text[i], bool_token,
                                        bool_option_name[i]));
 
-  /* Propagate values where neccessary */
+  /* Propagate values where necessary */
 
   if (float_spec[10])           /* --margin */
     for (i=11; i<15; i++)       /* --top_margin through --right_margin */
diff --git a/editor/pamrecolor.c b/editor/pamrecolor.c
new file mode 100644
index 00000000..6937fd8d
--- /dev/null
+++ b/editor/pamrecolor.c
@@ -0,0 +1,528 @@
+/* ----------------------------------------------------------------------
+ *
+ * Replace every pixel in an image with one of equal luminance
+ *
+ * By Scott Pakin <scott+pbm@pakin.org>
+ *
+ * ----------------------------------------------------------------------
+ *
+ * Copyright (C) 2010 Scott Pakin <scott+pbm@pakin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+
+/* Two numbers less than REAL_EPSILON apart are considered equal. */
+#define REAL_EPSILON 0.00001
+
+/* Ensure a number N is no less than A and no greater than B. */
+#define CLAMPxy(N, A, B) MAX(MIN((float)(N), (float)(B)), (float)(A))
+
+
+struct rgbfrac {
+    /* This structure represents red, green, and blue, each expressed
+       as a fraction from 0.0 to 1.0.
+    */
+    float rfrac;
+    float gfrac;
+    float bfrac;
+};
+
+struct cmdlineInfo {
+    /* This structure represents all of the information the user
+       supplied in the command line but in a form that's easy for the
+       program to use.
+    */
+    const char *    inputFileName;  /* '-' if stdin */
+    const char *    colorfile;      /* NULL if unspecified */
+    struct rgbfrac  color2gray;
+        /* colorspace/rmult/gmult/bmult options.  Negative numbers if
+           unspecified.
+        */
+    unsigned int    targetcolorSpec;
+    struct rgbfrac  targetcolor;
+    unsigned int    randomseed;
+    unsigned int    randomseedSpec;
+};
+
+
+
+static float
+rgb2gray(struct rgbfrac * const color2grayP,
+         float            const red,
+         float            const grn,
+         float            const blu) {
+    return
+        color2grayP->rfrac * red +
+        color2grayP->gfrac * grn +
+        color2grayP->bfrac * blu;
+}
+
+
+
+static tuplen *
+getColorRow(struct pam  * const pamP,
+            tuplen     ** const imageData,
+            unsigned int  const row,
+            unsigned int  const desiredWidth) {
+/*----------------------------------------------------------------------
+  Return a row of color data.  If the number of columns is too small,
+  repeat the existing columns in tiled fashion.
+------------------------------------------------------------------------*/
+    unsigned int const imageRow = row % pamP->height;
+
+    static tuplen * oneRow = NULL;
+
+    tuplen * retval;
+
+    if (pamP->width >= desiredWidth)
+        retval = imageData[imageRow];
+    else {
+        unsigned int col;
+
+        if (!oneRow) {
+            struct pam widePam;
+
+            widePam = *pamP;
+            widePam.width = desiredWidth;
+
+            oneRow = pnm_allocpamrown(&widePam);
+        }
+        for (col = 0; col < desiredWidth; ++col)
+            oneRow[col] = imageData[imageRow][col % pamP->width];
+        retval = oneRow;
+    }
+    return retval;
+}
+
+
+
+static void
+convertRowToGray(struct pam     * const pamP,
+                 struct rgbfrac * const color2gray,
+                 tuplen         * const tupleRow,
+                 samplen        * const grayRow) {
+/*----------------------------------------------------------------------
+  Convert a row of RGB, grayscale, or black-and-white pixels to a row
+  of grayscale values in the range [0, 1].
+------------------------------------------------------------------------*/
+    switch (pamP->depth) {
+    case 1:
+    case 2: {
+        /* Black-and-white or grayscale */
+        unsigned int col;
+        for (col = 0; col < pamP->width; ++col)
+            grayRow[col] = tupleRow[col][0];
+    } break;
+
+    case 3:
+    case 4: {
+        /* RGB color */
+        unsigned int col;
+        for (col = 0; col < pamP->width; ++col)
+            grayRow[col] = rgb2gray(color2gray,
+                                    tupleRow[col][PAM_RED_PLANE],
+                                    tupleRow[col][PAM_GRN_PLANE],
+                                    tupleRow[col][PAM_BLU_PLANE]);
+    } break;
+
+    default:
+        pm_error("internal error: unexpected image depth %u", pamP->depth);
+        break;
+    }
+}
+
+
+
+static void
+explicitlyColorRow(struct pam *   const pamP,
+                   tuplen *       const rowData,
+                   struct rgbfrac const tint) {
+
+    unsigned int col;
+
+    for (col = 0; col < pamP->width; ++col) {
+        rowData[col][PAM_RED_PLANE] = tint.rfrac;
+        rowData[col][PAM_GRN_PLANE] = tint.gfrac;
+        rowData[col][PAM_BLU_PLANE] = tint.bfrac;
+    }
+}
+
+
+
+static void
+randomlyColorRow(struct pam *   const pamP,
+                 tuplen *       const rowData) {
+/*----------------------------------------------------------------------
+  Assign each tuple in a row a random color.
+------------------------------------------------------------------------*/
+    unsigned int col;
+
+    for (col = 0; col < pamP->width; ++col) {
+        rowData[col][PAM_RED_PLANE] = rand() / (float)RAND_MAX;
+        rowData[col][PAM_GRN_PLANE] = rand() / (float)RAND_MAX;
+        rowData[col][PAM_BLU_PLANE] = rand() / (float)RAND_MAX;
+    }
+}
+
+
+
+static void
+recolorRow(struct pam     * const inPamP,
+           tuplen         * const inRow,
+           struct rgbfrac * const color2grayP,
+           tuplen         * const colorRow,
+           struct pam     * const outPamP,
+           tuplen         * const outRow) {
+/*----------------------------------------------------------------------
+  Map each tuple in a given row to a random color with the same
+  luminance.
+------------------------------------------------------------------------*/
+    static samplen * grayRow = NULL;
+
+    unsigned int col;
+
+    if (!grayRow)
+        MALLOCARRAY_NOFAIL(grayRow, inPamP->width);
+
+    convertRowToGray(inPamP, color2grayP, inRow, grayRow);
+
+    for (col = 0; col < inPamP->width; ++col) {
+        float targetgray;
+        float givengray;
+        float red, grn, blu;
+
+        red = colorRow[col][PAM_RED_PLANE];   /* initial value */
+        grn = colorRow[col][PAM_GRN_PLANE];   /* initial value */
+        blu = colorRow[col][PAM_BLU_PLANE];   /* initial value */
+
+        targetgray = grayRow[col];
+        givengray = rgb2gray(color2grayP, red, grn, blu);
+
+        if (givengray == 0.0) {
+            /* Special case for black so we don't divide by zero */
+            red = targetgray;
+            grn = targetgray;
+            blu = targetgray;
+        }
+        else {
+            /* Try simply scaling each channel equally. */
+            red *= targetgray / givengray;
+            grn *= targetgray / givengray;
+            blu *= targetgray / givengray;
+
+            if (red > 1.0 || grn > 1.0 || blu > 1.0) {
+                /* Repeatedly raise the level of all non-1.0 channels
+                 * until all channels are at 1.0 or we reach our
+                 * target gray. */
+                red = MIN(red, 1.0);
+                grn = MIN(grn, 1.0);
+                blu = MIN(blu, 1.0);
+                givengray = rgb2gray(color2grayP, red, grn, blu);
+
+                while (fabsf(givengray - targetgray) > REAL_EPSILON) {
+                    float increment;
+                        /* How much to increase each channel (unscaled
+                           amount)
+                        */
+                    int   subOne = 0;
+                        /* Number of channels with sub-1.0 values */
+
+                    /* Tally the number of channels that aren't yet maxed
+                       out.
+                    */
+                    if (red < 1.0)
+                        subOne++;
+                    if (grn < 1.0)
+                        subOne++;
+                    if (blu < 1.0)
+                        subOne++;
+
+                    /* Stop if we've reached our target or can't increment
+                     * any channel any further. */
+                    if (subOne == 0)
+                        break;
+
+                    /* Brighten each non-maxed channel equally. */
+                    increment = (targetgray - givengray) / subOne;
+                    if (red < 1.0)
+                        red = MIN(red + increment / color2grayP->rfrac, 1.0);
+                    if (grn < 1.0)
+                        grn = MIN(grn + increment / color2grayP->gfrac, 1.0);
+                    if (blu < 1.0)
+                        blu = MIN(blu + increment / color2grayP->bfrac, 1.0);
+
+                    /* Prepare to try again. */
+                    givengray = rgb2gray(color2grayP, red, grn, blu);
+                }
+            }
+            else
+                givengray = rgb2gray(color2grayP, red, grn, blu);
+        }
+
+        outRow[col][PAM_RED_PLANE] = red;
+        outRow[col][PAM_GRN_PLANE] = grn;
+        outRow[col][PAM_BLU_PLANE] = blu;
+        if (outPamP->depth == 4)
+            outRow[col][PAM_TRN_PLANE] = inRow[col][PAM_TRN_PLANE];
+    }
+}
+
+
+
+static struct rgbfrac
+color2GrayFromCsName(const char * const csName) {
+
+    struct rgbfrac retval;
+
+    /* Thanks to
+       http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
+       for these values.
+    */
+    if (streq(csName, "ntsc")) {
+        /* NTSC RGB with an Illuminant C reference white */
+        retval.rfrac = 0.2989164;
+        retval.gfrac = 0.5865990;
+        retval.bfrac = 0.1144845;
+    } else if (streq(csName, "srgb")) {
+        /* sRGB with a D65 reference white */
+        retval.rfrac = 0.2126729;
+        retval.gfrac = 0.7151522;
+        retval.bfrac = 0.0721750;
+    } else if (streq(csName, "adobe")) {
+        /* Adobe RGB (1998) with a D65 reference white */
+        retval.rfrac = 0.2973769;
+        retval.gfrac = 0.6273491;
+        retval.bfrac = 0.0752741;
+    } else if (streq(csName, "apple")) {
+        /* Apple RGB with a D65 reference white */
+        retval.rfrac = 0.2446525;
+        retval.gfrac = 0.6720283;
+        retval.bfrac = 0.0833192;
+    } else if (streq(csName, "cie")) {
+        /* CIE with an Illuminant E reference white */
+        retval.rfrac = 0.1762044;
+        retval.gfrac = 0.8129847;
+        retval.bfrac = 0.0108109;
+    } else if (streq(csName, "pal")) {
+        /* PAL/SECAM with a D65 reference white */
+        retval.rfrac = 0.2220379;
+        retval.gfrac = 0.7066384;
+        retval.bfrac = 0.0713236;
+    } else if (streq(csName, "smpte-c")) {
+        /* SMPTE-C with a D65 reference white */
+        retval.rfrac = 0.2124132;
+        retval.gfrac = 0.7010437;
+        retval.bfrac = 0.0865432;
+    } else if (streq(csName, "wide")) {
+        /* Wide gamut with a D50 reference white */
+        retval.rfrac = 0.2581874;
+        retval.gfrac = 0.7249378;
+        retval.bfrac = 0.0168748;
+    } else
+        pm_error("Unknown color space name \"%s\"", csName);
+
+    return retval;
+}
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct cmdlineInfo * const cmdlineP ) {
+
+    optEntry     * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options */
+    optStruct3     opt;
+    unsigned int   option_def_index;
+    const char *   colorspaceOpt;
+    const char *   targetcolorOpt;
+    unsigned int   csSpec, rmultSpec, gmultSpec, bmultSpec,
+        colorfileSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+    option_def_index = 0;          /* Incremented by OPTENTRY */
+
+    OPTENT3(0, "colorspace",   OPT_STRING, &colorspaceOpt, &csSpec, 0);
+    OPTENT3(0, "rmult",        OPT_FLOAT,  &cmdlineP->color2gray.rfrac,
+            &rmultSpec, 0);
+    OPTENT3(0, "gmult",        OPT_FLOAT,  &cmdlineP->color2gray.gfrac,
+            &gmultSpec, 0);
+    OPTENT3(0, "bmult",        OPT_FLOAT,  &cmdlineP->color2gray.bfrac,
+            &bmultSpec, 0);
+    OPTENT3(0, "colorfile",    OPT_STRING, &cmdlineP->colorfile,
+            &colorfileSpec, 0);
+    OPTENT3(0, "targetcolor",  OPT_STRING, &targetcolorOpt,
+            &cmdlineP->targetcolorSpec, 0);
+    OPTENT3(0,   "randomseed",   OPT_UINT,    &cmdlineP->randomseed,
+            &cmdlineP->randomseedSpec,      0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = 0;
+    opt.allowNegNum = 0;
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+
+    if (rmultSpec || gmultSpec || bmultSpec) {
+        /* If the user explicitly specified RGB multipliers, ensure that
+         * (a) he didn't specify --colorspace,
+         * (b) he specified all three channels, and
+         * (c) the values add up to 1.
+         */
+        float maxLuminance;
+
+        if (csSpec)
+            pm_error("The --colorspace option is mutually exclusive with "
+                     "the --rmult, --gmult, and --bmult options");
+        if (!(rmultSpec && gmultSpec && bmultSpec))
+            pm_error("If you specify any of --rmult, --gmult, or --bmult, "
+                     "you must specify all of them");
+        maxLuminance =
+            cmdlineP->color2gray.rfrac +
+            cmdlineP->color2gray.gfrac +
+            cmdlineP->color2gray.bfrac;
+        if (fabsf(1.0 - maxLuminance) > REAL_EPSILON)
+            pm_error("The values given for --rmult, --gmult, and --bmult must "
+                     "sum to 1.0, not %.10g", maxLuminance);
+    } else if (csSpec)
+        cmdlineP->color2gray = color2GrayFromCsName(colorspaceOpt);
+    else
+        cmdlineP->color2gray = color2GrayFromCsName("ntsc");
+
+    if (colorfileSpec && cmdlineP->targetcolorSpec)
+        pm_error("The --colorfile option and the --targetcolor option are "
+                 "mutually exclusive");
+
+    if (!colorfileSpec)
+        cmdlineP->colorfile = NULL;
+
+    if (cmdlineP->targetcolorSpec) {
+        sample const colorMaxVal = (1<<16) - 1;
+            /* Maximum PAM maxval for precise sample-to-float conversion */
+        tuple const targetTuple = pnm_parsecolor(targetcolorOpt, colorMaxVal);
+        cmdlineP->targetcolor.rfrac =
+            targetTuple[PAM_RED_PLANE] / (float)colorMaxVal;
+        cmdlineP->targetcolor.gfrac =
+            targetTuple[PAM_GRN_PLANE] / (float)colorMaxVal;
+        cmdlineP->targetcolor.bfrac =
+            targetTuple[PAM_BLU_PLANE] / (float)colorMaxVal;
+    }
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+        if (argc-1 > 1)
+            pm_error("Too many arguments: %u.  The only argument is the "
+                     "optional input file name", argc-1);
+    }
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+    struct cmdlineInfo cmdline;          /* Command-line parameters */
+    struct pam         inPam;
+    struct pam         outPam;
+    struct pam         colorPam;
+    FILE *             ifP;
+    FILE *             colorfP;
+    const char *       comments;
+    tuplen *           inRow;
+    tuplen *           outRow;
+    tuplen **          colorData;
+    tuplen *           colorRowBuffer;
+    unsigned int       row;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
+
+    ifP = pm_openr(cmdline.inputFileName);
+    inPam.comment_p = &comments;
+    pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(comment_p));
+
+    outPam = inPam;
+    outPam.file = stdout;
+    outPam.format = PAM_FORMAT;
+    outPam.depth = 4 - (inPam.depth % 2);
+    outPam.allocation_depth = outPam.depth;
+    strcpy(outPam.tuple_type, PAM_PPM_TUPLETYPE);
+    pnm_writepaminit(&outPam);
+
+    if (cmdline.colorfile) {
+        colorfP = pm_openr(cmdline.colorfile);
+        colorPam.comment_p = NULL;
+        colorData =
+            pnm_readpamn(colorfP, &colorPam, PAM_STRUCT_SIZE(comment_p));
+    } else {
+        colorfP = NULL;
+        colorPam = outPam;
+        colorData = NULL;
+    }
+
+    inRow = pnm_allocpamrown(&inPam);
+    outRow = pnm_allocpamrown(&outPam);
+
+    colorRowBuffer = pnm_allocpamrown(&outPam);
+
+    for (row = 0; row < inPam.height; ++row) {
+        tuplen * colorRow;
+
+        pnm_readpamrown(&inPam, inRow);
+
+        if (cmdline.colorfile)
+            colorRow = getColorRow(&colorPam, colorData, row, outPam.width);
+        else {
+            colorRow = colorRowBuffer;
+
+            if (cmdline.targetcolorSpec)
+                explicitlyColorRow(&colorPam, colorRow, cmdline.targetcolor);
+            else
+                randomlyColorRow(&colorPam, colorRow);
+        }
+        recolorRow(&inPam, inRow,
+                   &cmdline.color2gray, colorRow,
+                   &outPam, outRow);
+        pnm_writepamrown(&outPam, outRow);
+    }
+    pnm_freepamrown(outRow);
+    pnm_freepamrown(inRow);
+    pnm_freepamrown(colorRowBuffer);
+
+    if (colorData)
+        pnm_freepamarrayn(colorData, &colorPam);
+
+    if (colorfP)
+        pm_close(colorfP);
+    pm_close(ifP);
+
+    return 0;
+}
+
diff --git a/editor/pamrubber.c b/editor/pamrubber.c
new file mode 100644
index 00000000..4378c340
--- /dev/null
+++ b/editor/pamrubber.c
@@ -0,0 +1,1475 @@
+/*----------------------------------------------------------------------------*/
+
+/* pamrubber.c - transform images using Rubber Sheeting algorithm
+**               see: http://www.schaik.com/netpbm/rubber/
+**
+** Copyright (C) 2011 by 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.
+*/
+
+/*----------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <limits.h>
+#include <math.h>
+#include <time.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
+#include "pamdraw.h"
+
+
+
+typedef struct {
+  double x;
+  double y;
+} point;
+
+typedef struct {
+  point p1;
+  point p2;
+} line;
+
+typedef struct {
+  point p1;
+  point p2;
+  point p3;
+} triangle;
+
+typedef struct {
+    point tl;  /* top left     */
+    point tr;  /* top right    */
+    point bl;  /* bottom left  */
+    point br;  /* bottom right */
+} quadrilateral;
+
+struct cmdlineInfo {
+    unsigned int nCP;
+    point        oldCP[4];
+    point        newCP[4];
+    const char * fileName;
+    unsigned int quad;
+    unsigned int tri;
+    unsigned int frame;
+    unsigned int linear;
+    unsigned int verbose;
+    unsigned int randseedSpec;
+    unsigned int randseed;
+};
+
+
+static void
+parseCmdline(int argc, const char ** argv,
+             struct cmdlineInfo * const cmdlineP) {
+
+/* parse all parameters from the command line */
+
+    unsigned int option_def_index;
+    char * endptr;
+    unsigned int i;
+    unsigned int nCP;
+
+    /* instructions to optParseOptions3 on how to parse our options. */
+    optEntry * option_def;
+    optStruct3 opt;
+    
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "quad",     OPT_FLAG, NULL, &cmdlineP->quad,     0);
+    OPTENT3(0, "tri",      OPT_FLAG, NULL, &cmdlineP->tri,      0);
+    OPTENT3(0, "frame",    OPT_FLAG, NULL, &cmdlineP->frame,    0);
+    OPTENT3(0, "linear",   OPT_FLAG, NULL, &cmdlineP->linear,   0);
+    OPTENT3(0, "verbose",  OPT_FLAG, NULL, &cmdlineP->verbose,  0);
+    OPTENT3(0, "randseed", OPT_UINT, &cmdlineP->randseed,
+            &cmdlineP->randseedSpec, 0);
+    OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randseed,
+            &cmdlineP->randseedSpec, 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 */
+
+    /* uses and sets argc, argv, and some of *cmdlineP and others. */
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+
+    if (!cmdlineP->tri && !cmdlineP->quad)
+        pm_error("You must specify either -tri or -quad");
+
+    if (cmdlineP->tri && cmdlineP->quad)
+        pm_error("You may not specify both -tri and -quad");
+
+    /* Parameters are the control points (in qty of 4) and possibly a file name
+     */
+    nCP = (argc-1) / 4;
+
+    if (nCP > 4)
+        pm_error("Too many arguments: %u.  Arguments are "
+                 "control point coordinates and an optional file name, "
+                 "with a maximum of 4 control points", argc-1);
+
+    cmdlineP->nCP = nCP;
+
+    assert(nCP <= ARRAY_SIZE(cmdlineP->oldCP));
+    assert(nCP <= ARRAY_SIZE(cmdlineP->newCP));
+
+    for (i = 0; i < nCP; ++i) {
+        cmdlineP->oldCP[i].x = strtol(argv[i * 2 + 1], &endptr, 10);
+        cmdlineP->oldCP[i].y = strtol(argv[i * 2 + 2], &endptr, 10);
+        cmdlineP->newCP[i].x = strtol(argv[4 * nCP / 2 + i * 2 + 1],
+                                      &endptr, 10);
+        cmdlineP->newCP[i].y = strtol(argv[4 * nCP / 2 + i * 2 + 2],
+                                      &endptr, 10);
+    }
+
+    if (argc - 1 == 4 * nCP)
+        cmdlineP->fileName = "-";
+    else if (argc - 2 == 4 * nCP)
+        cmdlineP->fileName = argv[nCP * 4 + 1];
+    else
+        pm_error("Invalid number of arguments.  Arguments are "
+                 "control point coordinates and an optional file name, "
+                 "so there must be a multiple of 4 or a multiple of 4 "
+                 "plus 1.");
+}
+
+
+
+/* global variables */
+
+static int nCP;
+static point oldCP[4];
+static point newCP[4];
+static int nTri;
+static triangle tri1s[10];
+static triangle tri2s[10];
+static quadrilateral quad1;
+static quadrilateral quad2;
+static tuple black;
+
+static point
+makepoint(double const x,
+          double const y) {
+
+    point retval;
+
+    retval.x = x;
+    retval.y = y;
+
+    return retval;
+}
+
+
+
+static double
+distance(point const p1,
+         point const p2) {
+
+    return sqrt(SQR(p1.x - p2.x) + SQR(p1.y - p2.y));
+}
+
+
+
+static line
+makeline(point const p1,
+         point const p2) {
+
+    line retval;
+
+    retval.p1 = p1;
+    retval.p2 = p2;
+
+    return retval;
+}
+
+
+
+static bool
+intersect(line *  const l1P,
+          line *  const l2P,
+          point * const pP) {
+
+    bool cross;
+
+    if (((l2P->p2.y - l2P->p1.y) * (l1P->p2.x - l1P->p1.x) -
+         (l2P->p2.x - l2P->p1.x) * (l1P->p2.y - l1P->p1.y)) == 0) {
+        /* parallel lines */
+
+        cross = false;
+
+        if ((l1P->p1.x == l1P->p2.x) && (l2P->p1.x == l2P->p2.x)) {
+            /* two vertical lines */
+            pP->x = (l1P->p1.x + l2P->p1.x) / 2.0;
+            pP->y = 1e10;
+        } else if ((l1P->p1.y == l1P->p2.y) && (l2P->p1.y == l2P->p2.y)) {
+            /* two horizontal lines */
+            pP->x = 1e10;
+            pP->y = (l1P->p1.y + l2P->p1.y) / 2.0;
+        } else {
+            if (fabs(l1P->p2.y - l1P->p1.y) > fabs(l1P->p2.x - l1P->p1.x)) {
+                /* steep slope */
+                pP->y = 1e10;
+                pP->x = (l1P->p2.x - l1P->p1.x) / (l1P->p2.y - l1P->p1.y)
+                    * 1e10;
+            } else {
+                /* even slope */
+                pP->x = 1e10;
+                pP->y = (l1P->p2.y - l1P->p1.y) / (l1P->p2.x - l1P->p1.x)
+                    * 1e10;
+            }
+        }
+    } else {
+        /* intersecting lines */
+        double const ua =
+            ((l2P->p2.x - l2P->p1.x) * (l1P->p1.y - l2P->p1.y)
+              - (l2P->p2.y - l2P->p1.y) * (l1P->p1.x - l2P->p1.x))
+            / ((l2P->p2.y - l2P->p1.y)
+               * (l1P->p2.x - l1P->p1.x) - (l2P->p2.x - l2P->p1.x)
+               * (l1P->p2.y - l1P->p1.y));
+        double const ub =
+            ((l1P->p2.x - l1P->p1.x) * (l1P->p1.y - l2P->p1.y)
+              - (l1P->p2.y - l1P->p1.y) * (l1P->p1.x - l2P->p1.x))
+            / ((l2P->p2.y - l2P->p1.y)
+               * (l1P->p2.x - l1P->p1.x) - (l2P->p2.x - l2P->p1.x)
+               * (l1P->p2.y - l1P->p1.y));
+
+        pP->x = l1P->p1.x + ua * (l1P->p2.x - l1P->p1.x);
+        pP->y = l1P->p1.y + ua * (l1P->p2.y - l1P->p1.y);
+
+        if ((ua >= 0.0) && (ua <= 1.0) && (ub >= 0.0) && (ub <= 1.0))
+            cross = true;
+        else
+            cross = false;
+    }
+
+    return cross;
+}
+
+
+
+static triangle
+maketriangle(point const p1,
+             point const p2,
+             point const p3) {
+
+    triangle retval;
+
+    retval.p1 = p1;
+    retval.p2 = p2;
+    retval.p3 = p3;
+    
+    return retval;
+}
+
+
+
+static int
+insidetri(triangle * const triP,
+          point      const p) {
+
+    int cnt;
+
+    cnt = 0;  /* initial value */
+
+    if ((((triP->p1.y <= p.y) && (p.y < triP->p3.y))
+         || ((triP->p3.y <= p.y) && (p.y < triP->p1.y)))
+        &&
+        (p.x < (triP->p3.x - triP->p1.x) * (p.y - triP->p1.y)
+         / (triP->p3.y - triP->p1.y) + triP->p1.x))
+        cnt = !cnt;
+
+    if ((((triP->p2.y <= p.y) && (p.y < triP->p1.y))
+         || ((triP->p1.y <= p.y) && (p.y < triP->p2.y)))
+        &&
+        (p.x < (triP->p1.x - triP->p2.x) * (p.y - triP->p2.y)
+         / (triP->p1.y - triP->p2.y) + triP->p2.x))
+        cnt = !cnt;
+
+    if ((((triP->p3.y <= p.y) && (p.y < triP->p2.y))
+         || ((triP->p2.y <= p.y) && (p.y < triP->p3.y)))
+        &&
+        (p.x < (triP->p2.x - triP->p3.x) * (p.y - triP->p3.y)
+         / (triP->p2.y - triP->p3.y) + triP->p3.x))
+        cnt = !cnt;
+
+    return cnt;
+}
+
+
+
+static bool
+windtriangle(triangle * const tP,
+             point      const p1,
+             point      const p2,
+             point      const p3) {
+    point f, c;
+    line le, lv;
+    bool cw;
+
+    /* find cross of vertical through p3 and the edge p1-p2 */
+    f.x = p3.x;
+    f.y = -1.0;
+    lv = makeline(p3, f);
+    le = makeline(p1, p2);
+    intersect(&le, &lv, &c);
+
+    /* check for clockwise winding triangle */
+    if (((p1.x > p2.x) && (p3.y < c.y)) ||
+        ((p1.x < p2.x) && (p3.y > c.y))) {
+        *tP = maketriangle(p1, p2, p3);
+        cw = true;
+    } else { /* p1/2/3 were counter clockwise */
+        *tP = maketriangle(p1, p3, p2);
+        cw = false;
+    }
+    return cw;
+}
+
+
+
+static double
+tiny(void) {
+
+    if (rand() % 2)
+        return +1E-6 * (double) ((rand() % 90) + 9);
+    else
+        return -1E-6 * (double) ((rand() % 90) + 9);
+}
+
+
+
+static void
+angle(point * const p1P,
+      point * const p2P) {
+/*----------------------------------------------------------------------------
+   Move *p2P slightly if necessary to make sure the line (*p1P, *p2P)
+   is not horizontal or vertical.
+-----------------------------------------------------------------------------*/
+    if (p1P->x == p2P->x) { /* vertical line */
+        p2P->x += tiny();
+    }
+    if (p1P->y == p2P->y) { /* horizontal line */
+        p2P->y += tiny();
+    }
+}
+
+
+
+static void
+sideTriangleVerticalEdge(unsigned int const n,
+                         triangle *   const trig1P,
+                         point        const p11,
+                         point        const p12,
+                         point        const p13,
+                         point        const p14,
+                         point        const r11,
+                         point        const r12,
+                         triangle *   const trig2P,
+                         point        const p21,
+                         point        const p22,
+                         point        const p23,
+                         point        const p24,
+                         point        const r21,
+                         point        const r22) {
+                                   
+    if (((n >= 4) && (r11.x < p11.x)
+         && (p14.x < p13.x) && (p14.x < p12.x)
+         && (p14.x < p11.x)) /* left edge */
+        || 
+        ((n >= 4) && (r11.x > p11.x)
+         && (p14.x > p13.x) && (p14.x > p12.x)
+         && (p14.x > p11.x))) /* right edge */ {
+        *trig1P = maketriangle(r11, r12, p14);
+        *trig2P = maketriangle(r21, r22, p24);
+    } else if (((n >= 3) && (r11.x < p11.x) && (p13.x < p12.x)
+                && (p13.x < p11.x)) /* left edge */
+               || 
+               ((n >= 3) && (r11.x > p11.x) && (p13.x > p12.x)
+                && (p13.x > p11.x))) /* right edge */ {
+        *trig1P = maketriangle(r11, r12, p13);
+        *trig2P = maketriangle(r21, r22, p23);
+    } else if (((n >= 2) && (r11.x < p11.x)
+                && (p12.x < p11.x)) /* left edge */
+               ||
+               ((n >= 2) && (r11.x > p11.x)
+                && (p12.x > p11.x))) /* right edge */ { 
+        *trig1P = maketriangle(r11, r12, p12);
+        *trig2P = maketriangle(r21, r22, p22);
+    } else if (n >= 1) {
+        *trig1P = maketriangle(r11, r12, p11);
+        *trig2P = maketriangle(r21, r22, p21);
+    }
+}
+
+
+
+static void
+sideTriangleHorizontalEdge(unsigned int const n,
+                           triangle *   const trig1P,
+                           point        const p11,
+                           point        const p12,
+                           point        const p13,
+                           point        const p14,
+                           point        const r11,
+                           point        const r12,
+                           triangle *   const trig2P,
+                           point        const p21,
+                           point        const p22,
+                           point        const p23,
+                           point        const p24,
+                           point        const r21,
+                           point        const r22) {
+                                   
+    if (((n >= 4) && (r11.y < p11.y) && (p14.y < p13.y)
+         && (p14.y < p12.y) && (p14.y < p11.y)) /* top edge */
+        || 
+        ((n >= 4) && (r11.y > p11.y) && (p14.y > p13.y)
+         && (p14.y > p12.y) && (p14.y > p11.y))) /* bottom edge */ {
+        *trig1P = maketriangle(r11, r12, p14);
+        *trig2P = maketriangle(r21, r22, p24);
+    } else if (((n >= 3) && (r11.y < p11.y) && (p13.y < p12.y)
+                && (p13.y < p11.y)) /* top edge */
+               || 
+               ((n >= 3) && (r11.y > p11.y) && (p13.y > p12.y)
+                && (p13.y > p11.y))) /* bottom edge */ {
+        *trig1P = maketriangle(r11, r12, p13);
+        *trig2P = maketriangle(r21, r22, p23);
+    } else if (((n >= 2) && (r11.y < p11.y)
+                && (p12.y < p11.y)) /* top edge */
+               || 
+               ((n >= 2) && (r11.y > p11.y)
+                && (p12.y > p11.y))) /* bottom edge */ {
+        *trig1P = maketriangle(r11, r12, p12);
+        *trig2P = maketriangle(r21, r22, p22);
+    } else if (n >= 1) {
+        *trig1P = maketriangle(r11, r12, p11);
+        *trig2P = maketriangle(r21, r22, p21);
+    }
+}
+
+
+
+static void
+sideTriangle(unsigned int const n,
+             triangle *   const trig1P,
+             point        const p11,
+             point        const p12,
+             point        const p13,
+             point        const p14,
+             point        const r11,
+             point        const r12,
+             triangle *   const trig2P,
+             point        const p21,
+             point        const p22,
+             point        const p23,
+             point        const p24,
+             point        const r21,
+             point        const r22) {
+
+    if (fabs (r11.x - r12.x) < 1.0)
+        sideTriangleVerticalEdge(n,
+                                 trig1P, p11, p12, p13, p14, r11, r12,
+                                 trig2P, p21, p22, p23, p24, r21, r22);
+
+    else if (fabs (r11.y - r12.y) < 1.0)
+        sideTriangleHorizontalEdge(n,
+                                 trig1P, p11, p12, p13, p14, r11, r12,
+                                 trig2P, p21, p22, p23, p24, r21, r22);
+}
+
+
+
+static void
+edgeTriangle(triangle * const trig1P,
+             point      const p11,
+             point      const p12,
+             point      const tl1,
+             point      const tr1,
+             point      const bl1,
+             point      const br1,
+             triangle * const trig2P,
+             point      const p21,
+             point      const p22,
+             point      const tl2,
+             point      const tr2,
+             point      const bl2,
+             point      const br2) {
+             
+    if ((p11.x < p12.x) && (p11.y < p12.y)) {
+        /* up/left to down/right */
+        *trig1P = maketriangle(tr1, p12, p11);
+        *trig2P = maketriangle(tr2, p22, p21);
+    } else if ((p11.x > p12.x) && (p11.y > p12.y)) {
+        /* down/right to up/left */
+        *trig1P = maketriangle(bl1, p12, p11);
+        *trig2P = maketriangle(bl2, p22, p21);
+    } else if ((p11.x < p12.x) && (p11.y > p12.y)) {
+        /* down/left to up/right */
+        *trig1P = maketriangle(tl1, p12, p11);
+        *trig2P = maketriangle(tl2, p22, p21);
+    } else if ((p11.x > p12.x) && (p11.y < p12.y)) {
+        /* up/right to down/left */
+        *trig1P = maketriangle(br1, p12, p11);
+        *trig2P = maketriangle(br2, p22, p21);
+    }
+}
+
+
+
+static quadrilateral
+quadRect(double  const lft,
+         double  const rgt,
+         double  const top,
+         double  const bot) {
+
+    quadrilateral retval;
+
+    retval.tl = makepoint(lft, top);
+    retval.tr = makepoint(rgt, top);
+    retval.bl = makepoint(lft, bot);
+    retval.br = makepoint(rgt, bot);
+
+    return retval;
+}
+
+
+
+static void
+quadCornerSized(point           const p0,
+                point           const p1,
+                point           const p2,
+                point           const p3,
+                point           const mid,
+                quadrilateral * const quadP,
+                triangle *      const triP) {
+
+/* P0-P1 and P2-P3 are the diagonals */
+/* P0-P1 are further apart than P2-P3 */
+
+    if ((p0.x < p1.x) && (p0.y < p1.y)) {
+        /* p0 is top-left */
+        quadP->tl = p0; quadP->br = p1;
+        if (windtriangle(triP, p0, p2, p1)) {
+            /* p2 is top-right */
+            quadP->tr = p2; quadP->bl = p3;
+        } else {
+            /* p3 is top-right */
+            quadP->tr = p3; quadP->bl = p2;
+        }
+    } else if ((p0.x > p1.x) && (p0.y < p1.y)) {
+        /* p0 is top-right */
+        quadP->tr = p0; quadP->bl = p1;
+        if (windtriangle(triP, p0, p2, p1)) {
+            /* p2 is bottom-right */
+            quadP->br = p2; quadP->tl = p3;
+        } else {
+            /* p3 is bottom-right */
+            quadP->br = p3; quadP->tl = p2;
+        }
+    } else if ((p0.x < p1.x) && (p0.y > p1.y)) {
+        /* p0 is bottom-left */
+        quadP->bl = p0; quadP->tr = p1;
+        if (windtriangle(triP, p0, p2, p1)) {
+            /* p2 is top-left */
+            quadP->tl = p2; quadP->br = p3;
+        } else {
+            /* p3 is top-left */
+            quadP->tl = p3; quadP->br = p2;
+        }
+    } else if ((p0.x > p1.x) && (p0.y > p1.y)) {
+        /* p0 is bottom-right */
+        quadP->br = p0; quadP->tl = p1;
+        if (windtriangle(triP, p0, p2, p1)) {
+            /* p2 is bottom-left */
+            quadP->bl = p2; quadP->tr = p3;
+        } else {
+            /* p3 is bottom-left */
+            quadP->bl = p3; quadP->tr = p2;
+        }
+    }
+}
+
+
+
+static void
+quadCorner(point           const p0,
+           point           const p1,
+           point           const p2,
+           point           const p3,
+           point           const mid,
+           quadrilateral * const quadP,
+           triangle *      const triP) {
+
+    /* p0-p1 and p2-p3 are the diagonals */
+
+    if (fabs(p0.x - p1.x) + fabs(p0.y - p1.y) >=
+        fabs(p2.x - p3.x) + fabs(p2.y - p3.y)) {
+        quadCornerSized(p0, p1, p2, p3, mid, quadP, triP);
+    } else {
+        quadCornerSized(p2, p3, p0, p1, mid, quadP, triP);
+    }
+}
+
+
+
+static pamd_drawproc frameDrawproc;
+
+static void
+frameDrawproc (tuple **     const tuples,
+               unsigned int const cols,
+               unsigned int const rows,
+               unsigned int const depth,
+               sample       const maxval,
+               pamd_point   const p,
+               const void * const clientdata) {
+    
+    int yy;
+
+    for (yy = p.y - 1; yy <= p.y + 1; ++yy) {
+        int xx;
+        for (xx = p.x - 1; xx <= p.x + 1; ++xx) {
+            if (xx >= 0 && xx < cols && yy >= 0 && yy < rows) {
+                unsigned int i;
+                for (i = 0; i < depth; ++i)
+                    tuples[yy][xx][i] = (sample) *((tuple *) clientdata + i);
+            }
+        }
+    }
+}
+
+
+
+static void
+drawExtendedLine(const struct pam * const pamP,
+                 tuple **           const outTuples,
+                 point              const p1,
+                 point              const p2) {
+
+    pamd_point const p1ext =
+        pamd_makePoint(p1.x - 10 * (p2.x - p1.x),
+                       p1.y - 10 * (p2.y - p1.y));
+
+    pamd_point const p2ext =
+        pamd_makePoint(p2.x + 10 * (p2.x - p1.x),
+                       p2.y + 10 * (p2.y - p1.y));
+
+    pamd_line(outTuples, pamP->width, pamP->height, pamP->depth, pamP->maxval,
+              p1ext, p2ext, frameDrawproc, black);
+}
+
+
+
+static pamd_point
+clippedPoint(const struct pam * const pamP,
+             point              const p) {
+
+    int const roundedX = ROUND(p.x);
+    int const roundedY = ROUND(p.x);
+
+    int clippedX, clippedY;
+
+    assert(pamP->width  >= 2);
+    assert(pamP->height >= 2);
+
+    if (roundedX <= 0)
+        clippedX = 1;
+    else if (roundedX > pamP->width - 1)
+        clippedX = pamP->width - 2;
+    else
+        clippedX = roundedX;
+        
+    if (roundedY <= 0)
+        clippedY = 1;
+    else if (roundedY > pamP->width - 1)
+        clippedY = pamP->width - 2;
+    else
+        clippedY = roundedY;
+        
+    return pamd_makePoint(clippedX, clippedY);
+}
+
+
+
+static void drawClippedTriangle(const struct pam * const pamP,
+                                tuple **           const tuples,
+                                triangle           const tri) {
+
+    pamd_point const p1 = clippedPoint(pamP, tri.p1);
+    pamd_point const p2 = clippedPoint(pamP, tri.p2);
+    pamd_point const p3 = clippedPoint(pamP, tri.p3);
+
+    pamd_line(tuples, pamP->width, pamP->height, pamP->depth, pamP->maxval,
+              p1, p2, frameDrawproc, black);
+    pamd_line(tuples, pamP->width, pamP->height, pamP->depth, pamP->maxval,
+              p2, p3, frameDrawproc, black);
+    pamd_line(tuples, pamP->width, pamP->height, pamP->depth, pamP->maxval,
+              p3, p1, frameDrawproc, black);
+}
+
+
+
+static void
+prepTrig(int const wd,
+         int const ht) {
+
+/* create triangles using control points */
+
+    point rtl1, rtr1, rbl1, rbr1;
+    point rtl2, rtr2, rbl2, rbr2;
+    point c1p1, c1p2, c1p3, c1p4;
+    point c2p1, c2p2, c2p3, c2p4;
+    line l1, l2;
+    point p0;
+
+    rtl1 = makepoint(0.0 + tiny(),               0.0 + tiny());
+    rtr1 = makepoint((double) wd - 1.0 + tiny(), 0.0 + tiny());
+    rbl1 = makepoint(0.0 + tiny(),               (double) ht - 1.0 + tiny());
+    rbr1 = makepoint((double) wd - 1.0 + tiny(), (double) ht - 1.0 + tiny());
+
+    rtl2 = makepoint(0.0 + tiny(),               0.0 + tiny());
+    rtr2 = makepoint((double) wd - 1.0 + tiny(), 0.0 + tiny());
+    rbl2 = makepoint(0.0 + tiny(),               (double) ht - 1.0 + tiny());
+    rbr2 = makepoint((double) wd - 1.0 + tiny(), (double) ht - 1.0 + tiny());
+
+    if (nCP == 1) {
+        c1p1 = oldCP[0];
+        c2p1 = newCP[0];
+
+        /* connect control point to all corners to get 4 triangles */
+        /* left side triangle */
+        sideTriangle(nCP,
+                     &tri1s[0], c1p1, p0, p0, p0, rbl1, rtl1, 
+                     &tri2s[0], c2p1, p0, p0, p0, rbl2, rtl2);
+        /* top side triangle */
+        sideTriangle(nCP,
+                     &tri1s[1], c1p1, p0, p0, p0, rtl1, rtr1, 
+                     &tri2s[1], c2p1, p0, p0, p0, rtl2, rtr2);
+        /* right side triangle */
+        sideTriangle(nCP,
+                     &tri1s[2], c1p1, p0, p0, p0, rtr1, rbr1, 
+                     &tri2s[2], c2p1, p0, p0, p0, rtr2, rbr2);
+        /* bottom side triangle */
+        sideTriangle(nCP,
+                     &tri1s[3], c1p1, p0, p0, p0, rbr1, rbl1, 
+                     &tri2s[3], c2p1, p0, p0, p0, rbr2, rbl2);
+
+        nTri = 4;
+    } else if (nCP == 2) {
+        c1p1 = oldCP[0];
+        c1p2 = oldCP[1];
+        c2p1 = newCP[0];
+        c2p2 = newCP[1];
+
+        /* check for hor/ver edges */
+        angle (&c1p1, &c1p2);
+        angle (&c2p1, &c2p2);
+
+        /* connect two control points to corners to get 6 triangles */
+        /* left side */
+        sideTriangle(nCP,
+                     &tri1s[0], c1p1, c1p2, p0, p0, rbl1, rtl1, 
+                     &tri2s[0], c2p1, c2p2, p0, p0, rbl2, rtl2);
+        /* top side */
+        sideTriangle(nCP, 
+                     &tri1s[1], c1p1, c1p2, p0, p0, rtl1, rtr1, 
+                     &tri2s[1], c2p1, c2p2, p0, p0, rtl2, rtr2);
+        /* right side */
+        sideTriangle(nCP, 
+                     &tri1s[2], c1p1, c1p2, p0, p0, rtr1, rbr1, 
+                     &tri2s[2], c2p1, c2p2, p0, p0, rtr2, rbr2);
+        /* bottom side */
+        sideTriangle(nCP, 
+                     &tri1s[3], c1p1, c1p2, p0, p0, rbr1, rbl1, 
+                     &tri2s[3], c2p1, c2p2, p0, p0, rbr2, rbl2);
+
+        /* edge to corner triangles */
+        edgeTriangle(&tri1s[4], c1p1, c1p2, rtl1, rtr1, rbl1, rbr1,
+                     &tri2s[4], c2p1, c2p2, rtl2, rtr2, rbl2, rbr2);
+        edgeTriangle(&tri1s[5], c1p2, c1p1, rtl1, rtr1, rbl1, rbr1,
+                     &tri2s[5], c2p2, c2p1, rtl2, rtr2, rbl2, rbr2);
+        nTri = 6;
+    } else if (nCP == 3) {
+        c1p1 = oldCP[0];
+        c1p2 = oldCP[1];
+        c1p3 = oldCP[2];
+         
+        c2p1 = newCP[0];
+        c2p2 = newCP[1];
+        c2p3 = newCP[2];
+
+        /* Move vertices slightly if necessary to make sure no edge is
+           horizontal or vertical.
+        */
+        angle(&c1p1, &c1p2);
+        angle(&c1p2, &c1p3);
+        angle(&c1p3, &c1p1);
+
+        angle(&c2p1, &c2p2);
+        angle(&c2p2, &c2p3);
+        angle(&c2p3, &c2p1);
+
+        if (windtriangle(&tri1s[0], c1p1, c1p2, c1p3)) {
+            tri2s[0] = maketriangle(c2p1, c2p2, c2p3);
+        } else {
+            tri2s[0] = maketriangle(c2p1, c2p3, c2p2);
+        }
+
+        c1p1 = tri1s[0].p1;
+        c1p2 = tri1s[0].p2;
+        c1p3 = tri1s[0].p3;
+         
+        c2p1 = tri2s[0].p1;
+        c2p2 = tri2s[0].p2;
+        c2p3 = tri2s[0].p3;
+
+        /* point to side triangles */
+        /* left side */
+        sideTriangle(nCP,
+                     &tri1s[1], c1p1, c1p2, c1p3, p0, rbl1, rtl1, 
+                     &tri2s[1], c2p1, c2p2, c2p3, p0, rbl2, rtl2);
+        /* top side */
+        sideTriangle(nCP, 
+                     &tri1s[2], c1p1, c1p2, c1p3, p0, rtl1, rtr1, 
+                     &tri2s[2], c2p1, c2p2, c2p3, p0, rtl2, rtr2);
+        /* right side */
+        sideTriangle(nCP, 
+                     &tri1s[3], c1p1, c1p2, c1p3, p0, rtr1, rbr1, 
+                     &tri2s[3], c2p1, c2p2, c2p3, p0, rtr2, rbr2);
+        /* bottom side */
+        sideTriangle(nCP, 
+                     &tri1s[4], c1p1, c1p2, c1p3, p0, rbr1, rbl1, 
+                     &tri2s[4], c2p1, c2p2, c2p3, p0, rbr2, rbl2);
+
+        /* edge to corner triangles */
+        edgeTriangle(&tri1s[5], c1p1, c1p2, rtl1, rtr1, rbl1, rbr1,
+                     &tri2s[5], c2p1, c2p2, rtl2, rtr2, rbl2, rbr2);
+        edgeTriangle(&tri1s[6], c1p2, c1p3, rtl1, rtr1, rbl1, rbr1,
+                     &tri2s[6], c2p2, c2p3, rtl2, rtr2, rbl2, rbr2);
+        edgeTriangle(&tri1s[7], c1p3, c1p1, rtl1, rtr1, rbl1, rbr1,
+                     &tri2s[7], c2p3, c2p1, rtl2, rtr2, rbl2, rbr2);
+        nTri = 8;
+    } else if (nCP == 4) {
+        c1p1 = oldCP[0];
+        c1p2 = oldCP[1];
+        c1p3 = oldCP[2];
+        c1p4 = oldCP[3];
+         
+        c2p1 = newCP[0];
+        c2p2 = newCP[1];
+        c2p3 = newCP[2];
+        c2p4 = newCP[3];
+
+        /* check for hor/ver edges */
+        angle (&c1p1, &c1p2);
+        angle (&c1p2, &c1p3);
+        angle (&c1p3, &c1p4);
+        angle (&c1p4, &c1p1);
+        angle (&c1p1, &c1p3);
+        angle (&c1p2, &c1p4);
+
+        angle (&c2p1, &c2p2);
+        angle (&c2p2, &c2p3);
+        angle (&c2p3, &c2p4);
+        angle (&c2p4, &c2p1);
+        angle (&c2p1, &c2p3);
+        angle (&c2p2, &c2p4);
+
+        /*-------------------------------------------------------------------*/
+        /*        -1-      -2-        -3-      -4-        -5-      -6-       */
+        /*       1   2    1   3      1   2    1   4      1   3    1   4      */
+        /*         X        X          X        X          X        X        */
+        /*       3   4    2   4      4   3    2   3      4   2    3   2      */
+        /*-------------------------------------------------------------------*/
+
+        /* center two triangles */
+        l1 = makeline(c1p1, c1p4);
+        l2 = makeline(c1p2, c1p3);
+        if (intersect(&l1, &l2, &p0)) {
+            if (windtriangle(&tri1s[0], c1p1, c1p2, c1p3)) {
+                tri1s[1] = maketriangle(c1p4, c1p3, c1p2);
+                tri2s[0] = maketriangle(c2p1, c2p2, c2p3);
+                tri2s[1] = maketriangle(c2p4, c2p3, c2p2);
+            } else {
+                tri1s[1] = maketriangle(c1p4, c1p2, c1p3);
+                tri2s[0] = maketriangle(c2p1, c2p3, c2p2);
+                tri2s[1] = maketriangle(c2p4, c2p2, c2p3);
+            }
+        }
+        l1 = makeline(c1p1, c1p3);
+        l2 = makeline(c1p2, c1p4);
+        if (intersect(&l1, &l2, &p0)) {
+            if (windtriangle(&tri1s[0], c1p1, c1p2, c1p4)) {
+                tri1s[1] = maketriangle(c1p3, c1p4, c1p2);
+                tri2s[0] = maketriangle(c2p1, c2p2, c2p4);
+                tri2s[1] = maketriangle(c2p3, c2p4, c2p2);
+            } else {
+                tri1s[1] = maketriangle(c1p3, c1p2, c1p4);
+                tri2s[0] = maketriangle(c2p1, c2p4, c2p2);
+                tri2s[1] = maketriangle(c2p3, c2p2, c2p4);
+            }
+        }
+        l1 = makeline(c1p1, c1p2);
+        l2 = makeline(c1p3, c1p4);
+        if (intersect(&l1, &l2, &p0)) {
+            if (windtriangle(&tri1s[0], c1p1, c1p3, c1p4)) {
+                tri1s[1] = maketriangle(c1p2, c1p4, c1p3);
+                tri2s[0] = maketriangle(c2p1, c2p3, c2p4);
+                tri2s[1] = maketriangle(c2p2, c2p4, c2p3);
+            } else {
+                tri1s[1] = maketriangle(c1p2, c1p3, c1p4);
+                tri2s[0] = maketriangle(c2p1, c2p4, c2p3);
+                tri2s[1] = maketriangle(c2p2, c2p3, c2p4);
+            }
+        }
+
+        /* control points in correct orientation */
+        c1p1 = tri1s[0].p1;
+        c1p2 = tri1s[0].p2;
+        c1p3 = tri1s[0].p3;
+        c1p4 = tri1s[1].p1;
+        c2p1 = tri2s[0].p1;
+        c2p2 = tri2s[0].p2;
+        c2p3 = tri2s[0].p3;
+        c2p4 = tri2s[1].p1;
+
+        /* triangle from triangle point to side of image */
+        /* left side triangle */
+        sideTriangle(nCP, 
+                     &tri1s[2], c1p1, c1p2, c1p3, c1p4, rbl1, rtl1, 
+                     &tri2s[2], c2p1, c2p2, c2p3, c2p4, rbl2, rtl2);
+        /* top side triangle */
+        sideTriangle(nCP, 
+                     &tri1s[3], c1p1, c1p2, c1p3, c1p4, rtl1, rtr1, 
+                     &tri2s[3], c2p1, c2p2, c2p3, c2p4, rtl2, rtr2);
+        /* right side triangle */
+        sideTriangle(nCP, 
+                     &tri1s[4], c1p1, c1p2, c1p3, c1p4, rtr1, rbr1, 
+                     &tri2s[4], c2p1, c2p2, c2p3, c2p4, rtr2, rbr2);
+        /* bottom side triangle */
+        sideTriangle(nCP, 
+                     &tri1s[5], c1p1, c1p2, c1p3, c1p4, rbr1, rbl1, 
+                     &tri2s[5], c2p1, c2p2, c2p3, c2p4, rbr2, rbl2);
+
+        /*-------------------------------------------------------------------*/
+        /*        -1-      -2-        -3-      -4-        -5-      -6-       */
+        /*       1   2    1   3      1   2    1   4      1   3    1   4      */
+        /*         X        X          X        X          X        X        */
+        /*       3   4    2   4      4   3    2   3      4   2    3   2      */
+        /*-------------------------------------------------------------------*/
+
+        /* edge-corner triangles */
+        edgeTriangle(&tri1s[6], c1p1, c1p2, rtl1, rtr1, rbl1, rbr1,
+                     &tri2s[6], c2p1, c2p2, rtl2, rtr2, rbl2, rbr2);
+        edgeTriangle(&tri1s[7], c1p2, c1p4, rtl1, rtr1, rbl1, rbr1,
+                     &tri2s[7], c2p2, c2p4, rtl2, rtr2, rbl2, rbr2);
+        edgeTriangle(&tri1s[8], c1p4, c1p3, rtl1, rtr1, rbl1, rbr1,
+                     &tri2s[8], c2p4, c2p3, rtl2, rtr2, rbl2, rbr2);
+        edgeTriangle(&tri1s[9], c1p3, c1p1, rtl1, rtr1, rbl1, rbr1,
+                     &tri2s[9], c2p3, c2p1, rtl2, rtr2, rbl2, rbr2);
+        nTri = 10;
+    }
+}
+
+
+
+static void
+prepQuad(void) {
+
+/* order quad control points */
+
+    double d01, d12, d20;
+    line l1, l2;
+    point mid;
+    triangle tri;
+
+    if (nCP == 1) {
+        /* create a rectangle from top-left corner of image and control
+           point
+        */
+        quad1 = quadRect(0.0, oldCP[0].x, 0.0, oldCP[0].y);
+        quad2 = quadRect(0.0, newCP[0].x, 0.0, newCP[0].y);
+    } else if (nCP == 2) {
+        /* create a rectangle with the two points as opposite corners */
+        if ((oldCP[0].x < oldCP[1].x) && (oldCP[0].y < oldCP[1].y)) {
+            /* top-left and bottom-right */
+            quad1 = quadRect(oldCP[0].x,oldCP[1].x, oldCP[0].y, oldCP[1].y);
+        } else if ((oldCP[0].x > oldCP[1].x) && (oldCP[0].y < oldCP[1].y)) {
+            /* top-right and bottom-left */
+            quad1 = quadRect(oldCP[1].x, oldCP[0].x, oldCP[0].y, oldCP[1].y);
+        } else if ((oldCP[0].x < oldCP[1].x) && (oldCP[0].y < oldCP[1].y)) {
+            /* bottom-left and top-right */
+            quad1 = quadRect(oldCP[0].x, oldCP[1].x, oldCP[1].y, oldCP[0].y);
+        } else if ((oldCP[0].x > oldCP[1].x) && (oldCP[0].y < oldCP[1].y)) {
+            /* bottom-right and top-left */
+            quad1 = quadRect(oldCP[1].x, oldCP[0].x, oldCP[1].y, oldCP[0].y);
+        }
+        
+        if ((newCP[0].x < newCP[1].x) && (newCP[0].y < newCP[1].y)) {
+            /* top-left and bottom-right */
+            quad2 = quadRect(newCP[0].x, newCP[1].x, newCP[0].y, newCP[1].y);
+        } else if ((newCP[0].x > newCP[1].x) && (newCP[0].y < newCP[1].y)) {
+            /* top-right and bottom-left */
+            quad2 = quadRect(newCP[1].x, newCP[0].x, newCP[0].y, newCP[1].y);
+        } else if ((newCP[0].x < newCP[1].x) && (newCP[0].y < newCP[1].y)) {
+            /* bottom-left and top-right */
+            quad2 = quadRect(newCP[0].x, newCP[1].x, newCP[1].y, newCP[0].y);
+        } else if ((newCP[0].x > newCP[1].x) && (newCP[0].y < newCP[1].y)) {
+            /* bottom-right and top-left */
+            quad2 = quadRect(newCP[1].x, newCP[0].x, newCP[1].y, newCP[0].y);
+        }
+    } else {
+        if (nCP == 3) {
+            /* add the fourth corner of a parallelogram */
+            /* diagonal of the parallelogram is the two control points
+               furthest apart
+            */
+            
+            d01 = distance(newCP[0], newCP[1]);
+            d12 = distance(newCP[1], newCP[2]);
+            d20 = distance(newCP[2], newCP[0]);
+
+            if ((d01 > d12) && (d01 > d20)) {
+                oldCP[3].x = oldCP[0].x + oldCP[1].x - oldCP[2].x;
+                oldCP[3].y = oldCP[0].y + oldCP[1].y - oldCP[2].y;
+            } else
+                if (d12 > d20) {
+                    oldCP[3].x = oldCP[1].x + oldCP[2].x - oldCP[0].x;
+                    oldCP[3].y = oldCP[1].y + oldCP[2].y - oldCP[0].y;
+                } else {
+                    oldCP[3].x = oldCP[2].x + oldCP[0].x - oldCP[1].x;
+                    oldCP[3].y = oldCP[2].y + oldCP[0].y - oldCP[1].y;
+                }
+
+            if ((d01 > d12) && (d01 > d20)) {
+                newCP[3].x = newCP[0].x + newCP[1].x - newCP[2].x;
+                newCP[3].y = newCP[0].y + newCP[1].y - newCP[2].y;
+            } else
+                if (d12 > d20) {
+                    newCP[3].x = newCP[1].x + newCP[2].x - newCP[0].x;
+                    newCP[3].y = newCP[1].y + newCP[2].y - newCP[0].y;
+                } else {
+                    newCP[3].x = newCP[2].x + newCP[0].x - newCP[1].x;
+                    newCP[3].y = newCP[2].y + newCP[0].y - newCP[1].y;
+                }
+
+        } /* end nCP = 3 */
+
+        /* nCP = 3 or 4 */
+
+        /* find the intersection of the diagonals */
+        l1 = makeline(oldCP[0], oldCP[1]);
+        l2 = makeline(oldCP[2], oldCP[3]);
+        if (intersect(&l1, &l2, &mid)) {
+            quadCorner(oldCP[0], oldCP[1], oldCP[2], oldCP[3],
+                       mid, &quad1, &tri);
+        } else {
+            l1 = makeline(oldCP[0], oldCP[2]);
+            l2 = makeline(oldCP[1], oldCP[3]);
+            if (intersect(&l1, &l2, &mid))
+                quadCorner(oldCP[0], oldCP[2], oldCP[1], oldCP[3],
+                           mid, &quad1, &tri);
+            else {
+                l1 = makeline(oldCP[0], oldCP[3]);
+                l2 = makeline(oldCP[1], oldCP[2]);
+                if (intersect(&l1, &l2, &mid))
+                    quadCorner(oldCP[0], oldCP[3],
+                               oldCP[1], oldCP[2], mid, &quad1, &tri);
+                else
+                    pm_error("The four old control points don't seem "
+                             "to be corners.");
+            }
+        }
+
+        /* repeat for the "to-be" control points */
+        l1 = makeline(newCP[0], newCP[1]);
+        l2 = makeline(newCP[2], newCP[3]);
+        if (intersect(&l1, &l2, &mid))
+            quadCorner(newCP[0], newCP[1], newCP[2], newCP[3],
+                       mid, &quad2, &tri);
+        else {
+            l1 = makeline(newCP[0], newCP[2]);
+            l2 = makeline(newCP[1], newCP[3]);
+            if (intersect(&l1, &l2, &mid))
+                quadCorner(newCP[0], newCP[2], newCP[1], newCP[3],
+                           mid, &quad2, &tri);
+            else {
+                l1 = makeline(newCP[0], newCP[3]);
+                l2 = makeline(newCP[1], newCP[2]);
+                if (intersect(&l1, &l2, &mid))
+                    quadCorner(newCP[0], newCP[3],
+                               newCP[1], newCP[2], mid, &quad2, &tri);
+                else
+                    pm_error("The four new control points don't seem "
+                             "to be corners.");
+            }
+        }
+    }
+}
+
+
+
+static void
+warpTrig(point   const p2,
+         point * const p1P) {
+
+/* map target to source by triangulation */
+
+    point e1p1, e1p2, e1p3;
+    point e2p1, e2p2, e2p3;
+    line lp, le;
+    line l1, l2, l3;
+    double d1, d2, d3;
+    int i;
+
+    /* find in which triangle p2 lies */
+    for (i = 0; i < nTri; i++) {
+        if (insidetri (&tri2s[i], p2))
+            break;
+    }
+
+    if (i == nTri)
+        *p1P = makepoint(0.0, 0.0);
+    else {
+        /* where in triangle is point */
+        d1 = fabs (p2.x - tri2s[i].p1.x) + fabs (p2.y - tri2s[i].p1.y);
+        d2 = fabs (p2.x - tri2s[i].p2.x) + fabs (p2.y - tri2s[i].p2.y);
+        d3 = fabs (p2.x - tri2s[i].p3.x) + fabs (p2.y - tri2s[i].p3.y);
+
+        /* line through p1 and p intersecting with edge p2-p3 */
+        lp = makeline(tri2s[i].p1, p2);
+        le = makeline(tri2s[i].p2, tri2s[i].p3);
+        intersect (&lp, &le, &e2p1);
+
+        /* line through p2 and p intersecting with edge p3-p1 */
+        lp = makeline(tri2s[i].p2, p2);
+        le = makeline(tri2s[i].p3, tri2s[i].p1);
+        intersect (&lp, &le, &e2p2);
+
+        /* line through p3 and p intersecting with edge p1-p2 */
+        lp = makeline(tri2s[i].p3, p2);
+        le = makeline(tri2s[i].p1, tri2s[i].p2);
+        intersect (&lp, &le, &e2p3);
+
+        /* map target control points to source control points */
+        e1p1.x = tri1s[i].p2.x
+            + (e2p1.x - tri2s[i].p2.x)/(tri2s[i].p3.x - tri2s[i].p2.x) 
+            * (tri1s[i].p3.x - tri1s[i].p2.x);
+        e1p1.y = tri1s[i].p2.y
+            + (e2p1.y - tri2s[i].p2.y)/(tri2s[i].p3.y - tri2s[i].p2.y)
+            * (tri1s[i].p3.y - tri1s[i].p2.y);
+        e1p2.x = tri1s[i].p3.x
+            + (e2p2.x - tri2s[i].p3.x)/(tri2s[i].p1.x - tri2s[i].p3.x)
+            * (tri1s[i].p1.x - tri1s[i].p3.x);
+        e1p2.y = tri1s[i].p3.y
+            + (e2p2.y - tri2s[i].p3.y)/(tri2s[i].p1.y - tri2s[i].p3.y)
+            * (tri1s[i].p1.y - tri1s[i].p3.y);
+        e1p3.x = tri1s[i].p1.x
+            + (e2p3.x - tri2s[i].p1.x)/(tri2s[i].p2.x - tri2s[i].p1.x)
+            * (tri1s[i].p2.x - tri1s[i].p1.x);
+        e1p3.y = tri1s[i].p1.y
+            + (e2p3.y - tri2s[i].p1.y)/(tri2s[i].p2.y - tri2s[i].p1.y)
+            * (tri1s[i].p2.y - tri1s[i].p1.y);
+
+        /* intersect grid lines in source */
+        l1 = makeline(tri1s[i].p1, e1p1);
+        l2 = makeline(tri1s[i].p2, e1p2);
+        l3 = makeline(tri1s[i].p3, e1p3);
+
+        if ((d1 < d2) && (d1 < d3))
+            intersect (&l2, &l3, p1P);
+        else if (d2 < d3)
+            intersect (&l1, &l3, p1P);
+        else
+            intersect (&l1, &l2, p1P);
+    }
+}
+
+
+
+static void
+warpQuad(point   const p2,
+         point * const p1P) {
+
+/* map target to source for quad control points */
+
+    point h2, v2;
+    point c1tl, c1tr, c1bl, c1br;
+    point c2tl, c2tr, c2bl, c2br;
+    point e1t, e1b, e1l, e1r;
+    point e2t, e2b, e2l, e2r;
+    line l2t, l2b, l2l, l2r;
+    line lh, lv;
+
+    c1tl = quad1.tl;
+    c1tr = quad1.tr;
+    c1bl = quad1.bl;
+    c1br = quad1.br;
+       
+    c2tl = quad2.tl;
+    c2tr = quad2.tr;
+    c2bl = quad2.bl;
+    c2br = quad2.br;
+
+    l2t = makeline(c2tl, c2tr);
+    l2b = makeline(c2bl, c2br);
+    l2l = makeline(c2tl, c2bl);
+    l2r = makeline(c2tr, c2br);
+
+    /* find intersections of lines through control points */
+    intersect (&l2t, &l2b, &h2);
+    intersect (&l2l, &l2r, &v2);
+
+    /* find intersections of axes through P with control point box */
+    lv = makeline(p2, v2);
+    intersect (&l2t, &lv, &e2t);
+    intersect (&l2b, &lv, &e2b);
+
+    lh = makeline(p2, h2);
+    intersect (&l2l, &lh, &e2l);
+    intersect (&l2r, &lh, &e2r);
+
+    /* map target control points to source control points */
+    e1t.x = c1tl.x + (e2t.x - c2tl.x)/(c2tr.x - c2tl.x) * (c1tr.x - c1tl.x);
+    if (c1tl.y == c1tr.y)
+        e1t.y = c1tl.y;
+    else
+        e1t.y =
+            c1tl.y + (e2t.x - c2tl.x)/(c2tr.x - c2tl.x) * (c1tr.y - c1tl.y);
+
+    e1b.x = c1bl.x + (e2b.x - c2bl.x)/(c2br.x - c2bl.x) * (c1br.x - c1bl.x);
+    if (c1bl.y == c1br.y)
+        e1b.y = c1bl.y;
+    else
+        e1b.y =
+            c1bl.y + (e2b.x - c2bl.x)/(c2br.x - c2bl.x) * (c1br.y - c1bl.y);
+
+    if (c1tl.x == c1bl.x)
+        e1l.x = c1tl.x;
+    else
+        e1l.x =
+            c1tl.x + (e2l.y - c2tl.y)/(c2bl.y - c2tl.y) * (c1bl.x - c1tl.x);
+    e1l.y = c1tl.y + (e2l.y - c2tl.y)/(c2bl.y - c2tl.y) * (c1bl.y - c1tl.y);
+
+    if (c1tr.x == c1br.x)
+        e1r.x = c1tr.x;
+    else
+        e1r.x
+            = c1tr.x + (e2r.y - c2tr.y)/(c2br.y - c2tr.y) * (c1br.x - c1tr.x);
+    e1r.y = c1tr.y + (e2r.y - c2tr.y)/(c2br.y - c2tr.y) * (c1br.y - c1tr.y);
+
+    /* intersect grid lines in source */
+    lv = makeline(e1t, e1b);
+    lh = makeline(e1l, e1r);
+    intersect (&lh, &lv, p1P);
+}
+
+
+
+static void
+setGlobalCP(struct cmdlineInfo const cmdline) {
+
+    unsigned int i;
+
+    for (i = 0; i < cmdline.nCP; ++i) {
+        oldCP[i] = cmdline.oldCP[i];
+        newCP[i] = cmdline.newCP[i];
+    }
+    nCP = cmdline.nCP;
+    for (i = nCP; i < ARRAY_SIZE(oldCP); ++i)
+        oldCP[i] = makepoint(-1.0, -1.0);
+    for (i = nCP; i < ARRAY_SIZE(newCP); ++i)
+        newCP[i] = makepoint(-1.0, -1.0);
+}
+
+
+
+static void
+createWhiteTuple(const struct pam * const pamP,
+                 tuple *            const tupleP) {
+
+    tuple white;
+    unsigned int plane;
+
+    white = pnm_allocpamtuple(pamP);
+
+    for (plane = 0; plane < pamP->depth; ++plane)
+        white[plane] = pamP->maxval;
+
+    *tupleP = white;
+}
+
+
+
+static void
+makeAllWhite(struct pam * const pamP,
+             tuple **     const tuples) {
+
+    tuple white;
+    unsigned int row;
+
+    createWhiteTuple(pamP, &white);
+
+    for (row = 0; row < pamP->height; ++row)  {
+        unsigned int col;
+        for (col = 0; col < pamP->width; ++col)
+            pnm_assigntuple(pamP, tuples[row][col], white);
+    }
+
+    pnm_freepamtuple(white);
+}
+
+
+
+static sample
+pix(tuple **     const tuples,
+    unsigned int const width,
+    unsigned int const height,
+    point        const p1,
+    unsigned int const plane,
+    bool         const linear) {
+
+    point p;
+    double pix;
+
+    p.x = p1.x + 1E-3;
+    p.y = p1.y + 1E-3;
+
+    if (p.x < 0.0)
+        p.x = 0.0;
+    if (p.x > (double) width - 1.0)
+        p.x = (double) width - 1.0;
+    if (p.y < 0.0)
+        p.y = 0.0;
+    if (p.y > (double) height - 1.0)
+        p.y = (double) height - 1.0;
+
+    if (!linear) {
+        pix = tuples
+            [(int) floor(p.y + 0.5)]
+            [(int) floor(p.x + 0.5)][plane];
+    } else {
+        double const rx = p.x - floor(p.x);
+        double const ry = p.y - floor(p.y);
+        pix = 0.0;
+        if (((int) floor(p.x) >= 0) && ((int) floor(p.x) < width) &&
+            ((int) floor(p.y) >= 0) && ((int) floor(p.y) < height)) {
+            pix += (1.0 - rx) * (1.0 - ry) 
+                * tuples[(int) floor(p.y)][(int) floor(p.x)][plane];
+        }
+        if (((int) floor(p.x) + 1 >= 0) && ((int) floor(p.x) + 1 < width) &&
+            ((int) floor(p.y) >= 0) && ((int) floor(p.y) < height)) {
+            pix += rx * (1.0 - ry) 
+                * tuples[(int) floor(p.y)][(int) floor(p.x) + 1][plane];
+        }
+        if (((int) floor(p.x) >= 0) && ((int) floor(p.x) < width) &&
+            ((int) floor(p.y) + 1 >= 0) && ((int) floor(p.y) + 1 < height)) {
+            pix += (1.0 - rx) * ry 
+                * tuples[(int) floor(p.y) + 1][(int) floor(p.x)][plane];
+        }
+        if (((int) floor(p.x) + 1 >= 0) && ((int) floor(p.x) + 1 < width) &&
+            ((int) floor(p.y) + 1 >= 0) && ((int) floor(p.y) + 1 < height)) {
+            pix += rx * ry 
+                * tuples[(int) floor(p.y) + 1][(int) floor(p.x) + 1][plane];
+        }
+    }
+
+    return (int) floor(pix);
+}
+
+
+
+int
+main(int argc, const char ** const argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    struct pam inpam, outpam;
+    tuple ** inTuples;
+    tuple ** outTuples;
+    unsigned int p2y;
+  
+    pm_proginit(&argc, argv);
+
+    parseCmdline(argc, argv, &cmdline);
+
+    setGlobalCP(cmdline);
+
+    srand(cmdline.randseedSpec ? cmdline.randseed : time(NULL));
+
+    ifP = pm_openr(cmdline.fileName);
+
+    inTuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    outpam = inpam;  /* initial value */
+    outpam.file = stdout;
+
+    outTuples = pnm_allocpamarray(&outpam);
+
+    pnm_createBlackTuple(&outpam, &black);
+
+    makeAllWhite(&outpam, outTuples);
+
+    if (cmdline.tri)
+        prepTrig(inpam.width, inpam.height);
+    if (cmdline.quad)
+        prepQuad();
+
+    for (p2y = 0; p2y < inpam.height; ++p2y) {
+        unsigned int p2x;
+        for (p2x = 0; p2x < inpam.width; ++p2x) {
+            point p1, p2;
+            unsigned int plane;
+
+            p2 = makepoint(p2x, p2y);
+            if (cmdline.quad)
+                warpQuad(p2, &p1);
+            if (cmdline.tri)
+                warpTrig(p2, &p1);
+
+            for (plane = 0; plane < inpam.depth; ++plane) {
+                outTuples[p2y][p2x][plane] =
+                    pix(inTuples, inpam.width, inpam.height, p1, plane,
+                        cmdline.linear);
+            }
+        }
+    }
+
+    if (cmdline.frame) {
+        if (cmdline.quad) {
+            drawExtendedLine(&outpam, outTuples, quad2.tl, quad2.tr);
+            drawExtendedLine(&outpam, outTuples, quad2.bl, quad2.br);
+            drawExtendedLine(&outpam, outTuples, quad2.tl, quad2.bl);
+            drawExtendedLine(&outpam, outTuples, quad2.tr, quad2.br);
+        }
+        if (cmdline.tri) {
+            unsigned int i;
+            for (i = 0; i < nTri; ++i)
+                drawClippedTriangle(&outpam, outTuples, tri2s[i]);
+        }
+    }
+
+    pnm_writepam(&outpam, outTuples);
+
+    pnm_freepamtuple(black);
+    pnm_freepamarray(outTuples, &outpam);
+    pnm_freepamarray(inTuples, &inpam);
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
+
+
+
diff --git a/editor/pamscale.c b/editor/pamscale.c
index 0abbc205..7c6ee256 100644
--- a/editor/pamscale.c
+++ b/editor/pamscale.c
@@ -31,9 +31,9 @@
 #include <assert.h>
 
 #include "pm_c_util.h"
-#include "pam.h"
-#include "shhopt.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
 
 
 /****************************/
@@ -52,20 +52,22 @@
 #define EPSILON 1e-7
 
 
+
 /* x^2 and x^3 helper functions */
 static __inline__ double 
-pow2 (double x)
-{
-  return x*x;
+pow2(double const x) {
+    return x * x;
 }
 
+
+
 static __inline__ double 
-pow3 (double x) 
-{
-  return x*x*x;
+pow3(double const x) {
+    return x * x * x;
 }
 
 
+
 /* box, pulse, Fourier window, */
 /* box function also know as rectangle function */
 /* 1st order (constant) b-spline */
@@ -74,14 +76,15 @@ pow3 (double x)
 #define radius_box (0.5)
 
 static double 
-filter_box (double x)
-{
-    if (x <  0.0) x = -x;
-    if (x <= 0.5) return 1.0;
-    return 0.0;
+filter_box(double const x) {
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return (absx <= 0.5) ? 1.0 : 0.0;
 }
 
 
+
 /* triangle, Bartlett window, */
 /* triangle function also known as lambda function */
 /* 2nd order (linear) b-spline */
@@ -89,56 +92,66 @@ filter_box (double x)
 #define radius_triangle (1.0)
 
 static double 
-filter_triangle (double x)
-{
-    if (x <  0.0) x = -x;
-    if (x < 1.0) return 1.0-x;
-    return 0.0;
+filter_triangle(double const x) {
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return absx < 1.0 ? 1.0 - absx : 0.0;
 }
 
 
+
 /* 3rd order (quadratic) b-spline */
 
 #define radius_quadratic (1.5)
 
 static double 
-filter_quadratic(double x)
-{
-    if (x <  0.0) x = -x;
-    if (x < 0.5) return 0.75-pow2(x);
-    if (x < 1.5) return 0.50*pow2(x-1.5);
-    return 0.0;
+filter_quadratic(double const x) {
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return
+        absx < 0.5 ? 0.75 - pow2(absx) :
+        absx < 1.5 ? 0.50 * pow2(absx - 1.5) :
+        0.0;
 }
 
 
+
 /* 4th order (cubic) b-spline */
 
 #define radius_cubic (2.0)
 
 static double 
-filter_cubic(double x)
-{
-    if (x <  0.0) x = -x;
-    if (x < 1.0) return 0.5*pow3(x) - pow2(x) + 2.0/3.0;
-    if (x < 2.0) return pow3(2.0-x)/6.0;
-    return 0.0;
+filter_cubic(double const x) {
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return
+        absx < 1.0 ? 0.5 * pow3(absx) - pow2(absx) + 2.0/3.0 :
+        absx < 2.0 ? pow3(2.0-absx)/6.0 :
+        0.0;
 }
 
 
+
 /* Catmull-Rom spline, Overhauser spline */
 
 #define radius_catrom (2.0)
 
 static double 
-filter_catrom(double x)
-{
-    if (x <  0.0) x = -x;
-    if (x < 1.0) return  1.5*pow3(x) - 2.5*pow2(x)         + 1.0;
-    if (x < 2.0) return -0.5*pow3(x) + 2.5*pow2(x) - 4.0*x + 2.0;
-    return 0.0;
+filter_catrom(double const x) {
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return
+        absx < 1.0 ?  1.5 * pow3(absx) - 2.5 * pow2(absx)         + 1.0 :
+        absx < 2.0 ? -0.5 * pow3(absx) + 2.5 * pow2(absx) - 4.0 * absx + 2.0 :
+        0.0;
 }
 
 
+
 /* Mitchell & Netravali's two-param cubic */
 /* see Mitchell&Netravali,  */
 /* "Reconstruction Filters in Computer Graphics", SIGGRAPH 88 */
@@ -149,98 +162,106 @@ static double
 filter_mitchell(double x)
 {
 
-    double b = 1.0/3.0;
-    double c = 1.0/3.0;
-
-    double p0 = (  6.0 -  2.0*b         ) / 6.0;
-    double p2 = (-18.0 + 12.0*b +  6.0*c) / 6.0;
-    double p3 = ( 12.0 -  9.0*b -  6.0*c) / 6.0;
-    double q0 = (         8.0*b + 24.0*c) / 6.0;
-    double q1 = (      - 12.0*b - 48.0*c) / 6.0;
-    double q2 = (         6.0*b + 30.0*c) / 6.0;
-    double q3 = (      -      b -  6.0*c) / 6.0;
-
-    if (x <  0.0) x = -x;
-    if (x <  1.0) return p3*pow3(x) + p2*pow2(x)        + p0;
-    if (x < 2.0) return q3*pow3(x) + q2*pow2(x) + q1*x + q0;
-    return 0.0;
+    double const b = 1.0/3.0;
+    double const c = 1.0/3.0;
+
+    double const p0 = (  6.0 -  2.0*b         ) / 6.0;
+    double const p2 = (-18.0 + 12.0*b +  6.0*c) / 6.0;
+    double const p3 = ( 12.0 -  9.0*b -  6.0*c) / 6.0;
+    double const q0 = (         8.0*b + 24.0*c) / 6.0;
+    double const q1 = (      - 12.0*b - 48.0*c) / 6.0;
+    double const q2 = (         6.0*b + 30.0*c) / 6.0;
+    double const q3 = (      -      b -  6.0*c) / 6.0;
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return
+        absx <  1.0 ? p3 * pow3(absx) + p2 * pow2(absx)        + p0 :
+        absx < 2.0 ? q3 * pow3(absx) + q2 * pow2(absx) + q1 * absx + q0 :
+        0.0;
 }
 
 
+
 /* Gaussian filter (infinite) */
 
 #define radius_gauss (1.25)
 
 static double 
-filter_gauss(double x)
-{
+filter_gauss(double const x) {
+
     return exp(-2.0*pow2(x)) * sqrt(2.0/M_PI);
 }
 
 
+
 /* sinc, perfect lowpass filter (infinite) */
 
 #define radius_sinc (4.0)
 
 static double 
-filter_sinc(double x)
-{
+filter_sinc(double const x) {
     /* Note: Some people say sinc(x) is sin(x)/x.  Others say it's
        sin(PI*x)/(PI*x), a horizontal compression of the former which is
        zero at integer values.  We use the latter, whose Fourier transform
        is a canonical rectangle function (edges at -1/2, +1/2, height 1).
     */
-    if (x == 0.0) return 1.0;
-    return sin(M_PI*x)/(M_PI*x);
+    return 
+        x == 0.0 ? 1.0 :
+        sin(M_PI*x)/(M_PI*x);
 }
 
 
+
 /* Bessel (for circularly symm. 2-d filt, infinite) */
 /* See Pratt "Digital Image Processing" p. 97 for Bessel functions */
 
 #define radius_bessel (3.2383)
 
 static double 
-filter_bessel(double x)
-{
-    if (x == 0.0) return M_PI/4.0;
-    return j1(M_PI*x)/(2.0*x);
+filter_bessel(double const x) {
+
+    return 
+        x == 0.0 ? M_PI/4.0 :
+        j1(M_PI * x) / (2.0 * x);
 }
 
 
+
 /* Hanning window (infinite) */
 
 #define radius_hanning (1.0)
 
 static double 
-filter_hanning(double x)
-{
-    return 0.5*cos(M_PI*x) + 0.5;
+filter_hanning(double const x) {
+
+    return 0.5 * cos(M_PI * x) + 0.5;
 }
 
 
+
 /* Hamming window (infinite) */
 
 #define radius_hamming (1.0)
 
 static double 
-filter_hamming(double x)
-{
-  return 0.46*cos(M_PI*x) + 0.54;
+filter_hamming(double const x) {
+    return 0.46 * cos(M_PI * x) + 0.54;
 }
 
 
+
 /* Blackman window (infinite) */
 
 #define radius_blackman (1.0)
 
 static double 
-filter_blackman(double x)
-{
-    return 0.5*cos(M_PI*x) + 0.08*cos(2.0*M_PI*x) + 0.42;
+filter_blackman(double const x) {
+    return 0.5 * cos(M_PI * x) + 0.08 * cos(2.0 * M_PI * x) + 0.42;
 }
 
 
+
 /* parameterized Kaiser window (infinite) */
 /* from Oppenheim & Schafer, Hamming */
 
@@ -248,8 +269,7 @@ filter_blackman(double x)
 
 /* modified zeroth order Bessel function of the first kind. */
 static double 
-bessel_i0(double x)
-{
+bessel_i0(double const x) {
   
     int i;
     double sum, y, t;
@@ -257,64 +277,70 @@ bessel_i0(double x)
     sum = 1.0;
     y = pow2(x)/4.0;
     t = y;
-    for (i=2; t>EPSILON; i++) {
+    for (i=2; t>EPSILON; ++i) {
         sum += t;
         t   *= (double)y/pow2(i);
     }
     return sum;
 }
 
+
+
 static double 
-filter_kaiser(double x)
-{
-    /* typically 4<a<9 */
+filter_kaiser(double const x) {
+    /* typically 4 < a < 9 */
     /* param a trades off main lobe width (sharpness) */
     /* for side lobe amplitude (ringing) */
   
-    double a   = 6.5;
-    double i0a = 1.0/bessel_i0(a);
+    double const a   = 6.5;
+    double const i0a = 1.0/bessel_i0(a);
   
-    return i0a*bessel_i0(a*sqrt(1.0-pow2(x)));
+    return i0a * bessel_i0(a * sqrt(1.0-pow2(x)));
 }
 
 
+
 /* normal distribution (infinite) */
 /* Normal(x) = Gaussian(x/2)/2 */
 
 #define radius_normal (1.0)
 
 static double 
-filter_normal(double x)
-{
+filter_normal(double const x) {
     return exp(-pow2(x)/2.0) / sqrt(2.0*M_PI);
-    return 0.0;
 }
 
 
+
 /* Hermite filter */
 
 #define radius_hermite  (1.0)
 
 static double 
-filter_hermite(double x)
-{
+filter_hermite(double const x) {
     /* f(x) = 2|x|^3 - 3|x|^2 + 1, -1 <= x <= 1 */
-    if (x <  0.0) x = -x;
-    if (x <  1.0) return 2.0*pow3(x) - 3.0*pow2(x) + 1.0;
-    return 0.0;
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return
+        absx <  1.0 ? 2.0 * pow3(absx) - 3.0 * pow2(absx) + 1.0 :
+        0.0;
 }
 
 
+
 /* Lanczos filter */
 
 #define radius_lanczos (3.0)
 
 static double 
-filter_lanczos(double x)
-{
-    if (x <  0.0) x = -x;
-    if (x <  3.0) return filter_sinc(x) * filter_sinc(x/3.0);
-    return(0.0);
+filter_lanczos(double const x) {
+
+    double const absx = x < 0.0 ? -x : x;
+
+    return
+        x <  3.0 ? filter_sinc(absx) * filter_sinc(absx/3.0) :
+        0.0;
 }
 
 
@@ -385,7 +411,7 @@ enum scaleType {SCALE_SEPARATE, SCALE_BOXFIT, SCALE_BOXFILL, SCALE_PIXELMAX};
        size.
     */
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
      * in a form easy for the program to use.
      */
@@ -458,7 +484,7 @@ processFilterOptions(unsigned int const         filterSpec,
                      const char                 filterOpt[],
                      unsigned int const         windowSpec,
                      const char                 windowOpt[],
-                     struct cmdlineInfo * const cmdlineP) {
+                     struct CmdlineInfo * const cmdlineP) {
 
     if (filterSpec) {
         filter baseFilter;
@@ -490,9 +516,35 @@ processFilterOptions(unsigned int const         filterSpec,
 
 
 static void
+parseSizeParm(const char *   const sizeString,
+              const char *   const description,
+              unsigned int * const sizeP) {
+
+    char * endptr;
+    long int sizeLong;
+
+
+    sizeLong = strtol(sizeString, &endptr, 10);
+    if (strlen(sizeString) > 0 && *endptr != '\0')
+        pm_error("%s size argument not an integer: '%s'", 
+                 description, sizeString);
+    else if (sizeLong > INT_MAX - 2)
+        pm_error("%s size argument is too large "
+                 "for computations: %ld", 
+                 description, sizeLong);
+    else if (sizeLong <= 0)
+        pm_error("%s size argument is not positive: %ld", 
+                 description, sizeLong);
+    else
+        *sizeP = (unsigned int) sizeLong;
+}        
+
+
+
+static void
 parseXyParms(int                  const argc, 
-             char **              const argv,
-             struct cmdlineInfo * const cmdlineP) {
+             const char **        const argv,
+             struct CmdlineInfo * const cmdlineP) {
 
     /* parameters are box width (columns), box height (rows), and
        optional filespec 
@@ -505,23 +557,10 @@ parseXyParms(int                  const argc,
         pm_error("Too many arguments.  With -xyfit/xyfill/xysize, "
                  "you need 2 or 3 arguments.");
     else {
-        char * endptr;
-        cmdlineP->xsize = strtol(argv[1], &endptr, 10);
-        if (strlen(argv[1]) > 0 && *endptr != '\0')
-            pm_error("horizontal size argument not an integer: '%s'", 
-                     argv[1]);
-        if (cmdlineP->xsize <= 0)
-            pm_error("horizontal size argument is not positive: %d", 
-                     cmdlineP->xsize);
-        
-        cmdlineP->ysize = strtol(argv[2], &endptr, 10);
-        if (strlen(argv[2]) > 0 && *endptr != '\0')
-            pm_error("vertical size argument not an integer: '%s'", 
-                     argv[2]);
-        if (cmdlineP->ysize <= 0)
-            pm_error("vertical size argument is not positive: %d", 
-                     cmdlineP->ysize);
-        
+        parseSizeParm(argv[1], "horizontal", &cmdlineP->xsize);
+
+        parseSizeParm(argv[2], "vertical", &cmdlineP->ysize);
+
         if (argc-1 < 3)
             cmdlineP->inputFileName = "-";
         else
@@ -533,24 +572,33 @@ parseXyParms(int                  const argc,
 
 static void
 parseScaleParms(int                   const argc, 
-                char **               const argv,
-                struct cmdlineInfo  * const cmdlineP) {
-
-    /* parameters are scale factor and optional filespec */
+                const char **         const argv,
+                struct CmdlineInfo  * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Parse the parameters as a scale factor and optional filespec
+   (e.g. 'pamscale .5' or 'pamscale .5 testimg.ppm').
+-----------------------------------------------------------------------------*/
     if (argc-1 < 1)
         pm_error("With no dimension options, you must supply at least "
                  "one parameter: the scale factor.");
     else {
         cmdlineP->xscale = cmdlineP->yscale = atof(argv[1]);
         
-        if (cmdlineP->xscale == 0.0)
+        if (cmdlineP->xscale <= 0.0)
             pm_error("The scale parameter %s is not a positive number.",
                      argv[1]);
         else {
             if (argc-1 < 2)
                 cmdlineP->inputFileName = "-";
-            else
+            else {
                 cmdlineP->inputFileName = argv[2];
+                
+                if (argc-1 > 2)
+                    pm_error("Too many arguments.  There are at most two "
+                             "arguments with this set of options: "
+                             "scale factor and input file name.  "
+                             "You specified %u", argc-1);
+            }
         }
     }
 }
@@ -559,8 +607,8 @@ parseScaleParms(int                   const argc,
 
 static void
 parseFilespecOnlyParms(int                   const argc, 
-                       char **               const argv,
-                       struct cmdlineInfo  * const cmdlineP) {
+                       const char **         const argv,
+                       struct CmdlineInfo  * const cmdlineP) {
 
     /* Only parameter allowed is optional filespec */
     if (argc-1 < 1)
@@ -572,8 +620,8 @@ parseFilespecOnlyParms(int                   const argc,
 
 static void 
 parseCommandLine(int argc, 
-                 char ** argv, 
-                 struct cmdlineInfo  * const cmdlineP) {
+                 const char ** argv, 
+                 struct CmdlineInfo  * const cmdlineP) {
 /* --------------------------------------------------------------------------
    Parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -584,9 +632,9 @@ 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;
     optStruct3 opt;
+        /* Instructions to pm_optParseOptions3 on how to parse our options. */
   
     unsigned int option_def_index;
     unsigned int xyfit, xyfill;
@@ -595,45 +643,34 @@ parseCommandLine(int argc,
     float xscale, yscale;
     const char *filterOpt, *window;
     unsigned int filterSpec, windowSpec;
+    unsigned int xscaleSpec, yscaleSpec, xsizeSpec, ysizeSpec;
+    unsigned int pixelsSpec, reduceSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "xsize",     OPT_UINT,    &xsize,     NULL,                 0);
-    OPTENT3(0, "width",     OPT_UINT,    &xsize,     NULL,                 0);
-    OPTENT3(0, "ysize",     OPT_UINT,    &ysize,     NULL,                 0);
-    OPTENT3(0, "height",    OPT_UINT,    &ysize,     NULL,                 0);
-    OPTENT3(0, "xscale",    OPT_FLOAT,   &xscale,    NULL,                 0);
-    OPTENT3(0, "yscale",    OPT_FLOAT,   &yscale,    NULL,                 0);
-    OPTENT3(0, "pixels",    OPT_UINT,    &pixels,    NULL,                 0);
-    OPTENT3(0, "reduce",    OPT_UINT,    &reduce,    NULL,                 0);
+    OPTENT3(0, "xsize",     OPT_UINT,    &xsize,     &xsizeSpec,           0);
+    OPTENT3(0, "width",     OPT_UINT,    &xsize,     &xsizeSpec,           0);
+    OPTENT3(0, "ysize",     OPT_UINT,    &ysize,     &ysizeSpec,           0);
+    OPTENT3(0, "height",    OPT_UINT,    &ysize,     &ysizeSpec,           0);
+    OPTENT3(0, "xscale",    OPT_FLOAT,   &xscale,    &xscaleSpec,          0);
+    OPTENT3(0, "yscale",    OPT_FLOAT,   &yscale,    &yscaleSpec,          0);
+    OPTENT3(0, "pixels",    OPT_UINT,    &pixels,    &pixelsSpec,          0);
+    OPTENT3(0, "reduce",    OPT_UINT,    &reduce,    &reduceSpec,          0);
     OPTENT3(0, "xysize",    OPT_FLAG,    NULL,       &xyfit,               0);
     OPTENT3(0, "xyfit",     OPT_FLAG,    NULL,       &xyfit,               0);
     OPTENT3(0, "xyfill",    OPT_FLAG,    NULL,       &xyfill,              0);
-    OPTENT3(0, "verbose",   OPT_FLAG,    NULL,       &cmdlineP->verbose,  0);
+    OPTENT3(0, "verbose",   OPT_FLAG,    NULL,       &cmdlineP->verbose,   0);
     OPTENT3(0, "filter",    OPT_STRING,  &filterOpt, &filterSpec,          0);
     OPTENT3(0, "window",    OPT_STRING,  &window,    &windowSpec,          0);
-    OPTENT3(0, "nomix",     OPT_FLAG,    NULL,       &cmdlineP->nomix,    0);
-    OPTENT3(0, "linear",    OPT_FLAG,    NULL,       &cmdlineP->linear,   0);
+    OPTENT3(0, "nomix",     OPT_FLAG,    NULL,       &cmdlineP->nomix,     0);
+    OPTENT3(0, "linear",    OPT_FLAG,    NULL,       &cmdlineP->linear,    0);
   
-    /* Set the defaults. -1 = unspecified */
-
-    /* (Now that we're using ParseOptions3, we don't have to do this -1
-     * nonsense, but we don't want to risk screwing these complex 
-     * option compatibilities up, so we'll convert that later.
-     */
-    xsize = -1;
-    ysize = -1;
-    xscale = -1.0;
-    yscale = -1.0;
-    pixels = -1;
-    reduce = -1;
-    
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;   /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
     /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (cmdlineP->nomix && filterSpec) 
@@ -642,38 +679,38 @@ parseCommandLine(int argc,
     processFilterOptions(filterSpec, filterOpt, windowSpec, window,
                          cmdlineP);
 
-    if (xsize == 0)
+    if (xsizeSpec && xsize == 0)
         pm_error("-xsize/width must be greater than zero.");
-    if (ysize == 0)
+    if (ysizeSpec && ysize == 0)
         pm_error("-ysize/height must be greater than zero.");
-    if (xscale != -1.0 && xscale <= 0.0)
+    if (xscaleSpec && xscale <= 0.0)
         pm_error("-xscale must be greater than zero.");
-    if (yscale != -1.0 && yscale <= 0.0)
+    if (yscaleSpec && yscale <= 0.0)
         pm_error("-yscale must be greater than zero.");
-    if (reduce <= 0 && reduce != -1)
+    if (reduceSpec && reduce <= 0)
         pm_error("-reduce must be greater than zero.");
 
-    if (xsize != -1 && xscale != -1)
+    if (xsizeSpec && xscaleSpec)
         pm_error("Cannot specify both -xsize/width and -xscale.");
-    if (ysize != -1 && yscale != -1)
+    if (ysizeSpec && yscaleSpec)
         pm_error("Cannot specify both -ysize/height and -yscale.");
     
     if ((xyfit || xyfill) &&
-        (xsize != -1 || xscale != -1 || ysize != -1 || yscale != -1 || 
-         reduce != -1 || pixels != -1) )
+        (xsizeSpec || xscaleSpec || ysizeSpec || yscaleSpec || 
+         reduceSpec || pixelsSpec) )
         pm_error("Cannot specify -xyfit/xyfill/xysize with other "
                  "dimension options.");
     if (xyfit && xyfill)
         pm_error("Cannot specify both -xyfit and -xyfill");
-    if (pixels != -1 && 
-        (xsize != -1 || xscale != -1 || ysize != -1 || yscale != -1 ||
-         reduce != -1) )
+    if (pixelsSpec && 
+        (xsizeSpec || xscaleSpec || ysizeSpec || yscaleSpec ||
+         reduceSpec) )
         pm_error("Cannot specify -pixels with other dimension options.");
-    if (reduce != -1 && 
-        (xsize != -1 || xscale != -1 || ysize != -1 || yscale != -1) )
+    if (reduceSpec && 
+        (xsizeSpec || xscaleSpec || ysizeSpec || yscaleSpec) )
         pm_error("Cannot specify -reduce with other dimension options.");
 
-    if (pixels == 0)
+    if (pixelsSpec && pixels == 0)
         pm_error("-pixels must be greater than zero");
 
     /* Get the program parameters */
@@ -681,7 +718,7 @@ parseCommandLine(int argc,
     if (xyfit || xyfill) {
         cmdlineP->scaleType = xyfit ? SCALE_BOXFIT : SCALE_BOXFILL;
         parseXyParms(argc, argv, cmdlineP);
-    } else if (reduce != -1) {
+    } else if (reduceSpec) {
         cmdlineP->scaleType = SCALE_SEPARATE;
         parseFilespecOnlyParms(argc, argv, cmdlineP);
         cmdlineP->xsize = cmdlineP->ysize = 0;
@@ -689,44 +726,50 @@ parseCommandLine(int argc,
             ((double) 1.0) / ((double) reduce);
         pm_message("reducing by %d gives scale factor of %f.", 
                    reduce, cmdlineP->xscale);
-    } else if (pixels != -1) {
+    } else if (pixelsSpec) {
         cmdlineP->scaleType = SCALE_PIXELMAX;
         parseFilespecOnlyParms(argc, argv, cmdlineP);
         cmdlineP->pixels = pixels;
-    } else if (xsize == -1 && xscale == -1 && ysize == -1 && yscale == -1
-               && pixels == -1 && reduce == -1) {
+    } else if (xsizeSpec || xscaleSpec || ysizeSpec || yscaleSpec) {
         cmdlineP->scaleType = SCALE_SEPARATE;
-        parseScaleParms(argc, argv, cmdlineP);
-        cmdlineP->xsize = cmdlineP->ysize = 0;
+        parseFilespecOnlyParms(argc, argv, cmdlineP);
+        cmdlineP->xsize = xsizeSpec ? xsize : 0;
+        cmdlineP->ysize = ysizeSpec ? ysize : 0;
+        cmdlineP->xscale = xscaleSpec ? xscale : 0.0;
+        cmdlineP->yscale = yscaleSpec ? yscale : 0.0;
     } else {
         cmdlineP->scaleType = SCALE_SEPARATE;
-        parseFilespecOnlyParms(argc, argv, cmdlineP);
-        cmdlineP->xsize = xsize == -1 ? 0 : xsize;
-        cmdlineP->ysize = ysize == -1 ? 0 : ysize;
-        cmdlineP->xscale = xscale == -1.0 ? 0.0 : xscale;
-        cmdlineP->yscale = yscale == -1.0 ? 0.0 : yscale;
+        parseScaleParms(argc, argv, cmdlineP);
+        cmdlineP->xsize = cmdlineP->ysize = 0;
     }
 }
 
 
 
 static void 
-computeOutputDimensions(struct cmdlineInfo  const cmdline, 
-                        int                 const rows, 
-                        int                 const cols, 
-                        int *               const newrowsP, 
-                        int *               const newcolsP) {
+computeOutputDimensions(struct CmdlineInfo  const cmdline, 
+                        unsigned int        const cols, 
+                        unsigned int        const rows, 
+                        int *               const newcolsP,
+                        int *               const newrowsP) { 
+
+    double newcolsD, newrowsD;
+        /* Intermediate calculation of the output dimensions, in double
+           precision floating point to avoid arithmetic overflow.
+        */
+    unsigned int newcols, newrows;
+        /* The output dimensions we return */
 
     switch(cmdline.scaleType) {
     case SCALE_PIXELMAX: {
         if (rows * cols <= cmdline.pixels) {
-            *newrowsP = rows;
-            *newcolsP = cols;
+            newrowsD = rows;
+            newcolsD = cols;
         } else {
             const double scale =
                 sqrt( (float) cmdline.pixels / ((float) cols * (float) rows));
-            *newrowsP = rows * scale;
-            *newcolsP = cols * scale;
+            newrowsD = rows * scale;
+            newcolsD = cols * scale;
         }
     } break;
     case SCALE_BOXFIT:
@@ -739,48 +782,56 @@ computeOutputDimensions(struct cmdlineInfo  const cmdline,
              cmdline.scaleType == SCALE_BOXFIT) ||
             (box_aspect_ratio < aspect_ratio &&
              cmdline.scaleType == SCALE_BOXFILL)) {
-            *newrowsP = cmdline.ysize;
-            *newcolsP = *newrowsP * aspect_ratio + 0.5;
+            newrowsD = cmdline.ysize;
+            newcolsD = newrowsD * aspect_ratio;
         } else {
-            *newcolsP = cmdline.xsize;
-            *newrowsP = *newcolsP / aspect_ratio + 0.5;
+            newcolsD = cmdline.xsize;
+            newrowsD = newcolsD / aspect_ratio;
         }
     } break;
     case SCALE_SEPARATE: {
         if (cmdline.xsize)
-            *newcolsP = cmdline.xsize;
+            newcolsD = cmdline.xsize;
         else if (cmdline.xscale)
-            *newcolsP = cmdline.xscale * cols + .5;
+            newcolsD = cmdline.xscale * cols;
         else if (cmdline.ysize)
-            *newcolsP = cols * ((float) cmdline.ysize/rows) +.5;
+            newcolsD = cols * ((float) cmdline.ysize/rows);
         else
-            *newcolsP = cols;
+            newcolsD = cols;
 
         if (cmdline.ysize)
-            *newrowsP = cmdline.ysize;
+            newrowsD = cmdline.ysize;
         else if (cmdline.yscale)
-            *newrowsP = cmdline.yscale * rows +.5;
+            newrowsD = cmdline.yscale * rows;
         else if (cmdline.xsize)
-            *newrowsP = rows * ((float) cmdline.xsize/cols) +.5;
+            newrowsD = rows * ((float) cmdline.xsize/cols);
         else
-            *newrowsP = rows;
+            newrowsD = rows;
     }
     }
-
-    /* If the calculations above yielded (due to rounding) a zero 
-     * dimension, we fudge it up to 1.  We do this rather than considering
-     * it a specification error (and dying) because it's friendlier to 
-     * automated processes that work on arbitrary input.  It saves them
-     * having to check their numbers to avoid catastrophe.
-     */
-  
-    if (*newcolsP < 1) *newcolsP = 1;
-    if (*newrowsP < 1) *newrowsP = 1;
+    
+    /* If the rounding yields a zero dimension, we fudge it up to 1.  We do
+       this rather than considering it a specification error (and dying)
+       because it's friendlier to automated processes that work on arbitrary
+       input.  It saves them having to check their numbers to avoid
+       catastrophe.
+    */
+    newcols = MAX(1, ROUNDU(newcolsD));
+    newrows = MAX(1, ROUNDU(newrowsD));
+
+    if (newcols > INT_MAX - 2)
+        pm_error("output image width (%u) too large for computations",
+                 newcols);
+    if (newrows > INT_MAX - 2)
+        pm_error("output image height (%u) too large for computation",
+                 newrows);
+
+    *newcolsP = newcols;
+    *newrowsP = newrows;
 }
 
 
 
-
 /****************************/
 /****************************/
 /******* resampling *********/
@@ -876,7 +927,7 @@ typedef struct {
            window.  The index order is NOT the order of the rows in the
            image.  E.g. line[0] isn't always the topmost row of the window.
            Rather, the rows are arranged in a cycle and you have to know
-           indpendently where the topmost one is.  E.g. the rows of a 5
+           independently where the topmost one is.  E.g. the rows of a 5
            line window with topmost row at index 3 might be:
 
               line[0] = Row 24
@@ -974,12 +1025,12 @@ createWeightList(unsigned int          const targetPos,
    row.  Assume 'filter' is a triangle function -- 1 at 0, sloping
    down to 0 at -1 and 1.
 
-   Now assume that the scale factor is 2 -- the target image will be
-   twice the size of the source image.  That means the two-pixel-wide
-   window of the source row that affects Column 5 of the target row
-   (centered at target position 5.5) goes from position 1.75 to
-   3.75, centered at 2.75.  That means the window covers 1/4 of
-   Column 1, all of Column 2, and 3/4 of Column 3 of the source row.
+   Now assume that the scale factor is 2 -- the target image will be twice the
+   size of the source image.  That means the two-pixel-wide window of the
+   source row that affects Column 5 of the target row, which is centered at
+   target position 5.5, is centered at source position 5.5/2 = 2.75.  So it
+   goes from source position 1.75 to 3.75.  That means the window covers 1/4
+   of Column 1, all of Column 2, and 3/4 of Column 3 of the source row.
 
    We want to calculate 3 weights, one to be applied to each source pixel
    in computing the target pixel.  Ideally, we would compute the average
@@ -992,22 +1043,22 @@ createWeightList(unsigned int          const targetPos,
    -.875 from the center of the window, we assume a constant function
    value of triangle(-.875), which equals .125.  For the 2.00-3.00
    region, we get triangle(-.25) = .75.  For the 3.00-3.75 region, we
-   get triangle(.125) = .875.  So the weights for the 3 regions, which
+   get triangle(.625) = .375.  So the weights for the 3 regions, which
    we get by multiplying this constant function value by the width of
    the region and normalizing so they add up to 1 are:
 
-      Source Column 1:  .125*.25 / 1.4375 = .022
-      Source Column 2:  .75*1.00 / 1.4375 = .521
-      Source Column 3:  .875*.75 / 1.4375 = .457
+      Source Column 1:  .125*.25 / 1.0625 = .029
+      Source Column 2:  .75*1.00 / 1.0625 = .706
+      Source Column 3:  .375*.75 / 1.0625 = .265
 
    These are the weights we return.  Thus, if we assume that the source
    pixel 1 has value 10, source pixel 2 has value 20, and source pixel 3
    has value 30, Caller would compute target pixel 5's value as
 
-      10*.022 + 20*.521 + 30*.457 = 24
+      10*.029 + 20*.706 + 30*.265 = 22
 
 -----------------------------------------------------------------------------*/
-    /* 'windowCenter', is the continous position within the source of
+    /* 'windowCenter', is the continuous position within the source of
        the center of the window that will influence target pixel
        'targetPos'.  'left' and 'right' are the edges of the window.
        'leftPixel' and 'rightPixel' are the pixel positions of the
@@ -1092,7 +1143,7 @@ createWeightListSet(unsigned int          const sourceSize,
       
    2) Filter out any frequencies that are too high to be captured
       by the new sampling -- i.e. frequencies above 1/2 the new
-      sample rate.  This is the information we must lose due to low
+      sample rate.  This is the information we must lose because of low
       sample rate.
       
    3) Sample the result at the new sample rate.
@@ -1341,7 +1392,7 @@ outputOneResampledRow(const struct pam * const outpamP,
 -----------------------------------------------------------------------------*/
     unsigned int col;
 
-    bool haveOpacity;           /* There is an opacity plane */
+    int haveOpacity;           /* There is an opacity plane */
     unsigned int opacityPlane;  /* Plane number of opacity plane, if any */
 
     pnm_getopacity(outpamP, &haveOpacity, &opacityPlane);
@@ -1601,11 +1652,11 @@ horizontalScale(tuplen *     const inputtuplenrow,
                 float        const xscale,
                 float *      const stretchP) {
 /*----------------------------------------------------------------------------
-  Take the input row 'inputtuplenrow', decribed by *inpamP, and scale
+  Take the input row 'inputtuplenrow', described by *inpamP, and scale
   it by a factor of 'xscale', to create the output row 'newtuplenrow',
   described by *outpamP.
 
-  Due to arithmetic imprecision, we may have to stretch slightly the
+  Because of arithmetic imprecision, we may have to stretch slightly the
   contents of the last pixel of the output row to make a full pixel.
   Return as *stretchP the fraction of a pixel by which we had to
   stretch in this way.
@@ -1646,7 +1697,7 @@ horizontalScale(tuplen *     const inputtuplenrow,
         }
         /* There's not enough left in the current input pixel to fill up 
            a whole output column, so just accumulate the remainder of the
-           pixel into the current output column.  Due to rounding, we may
+           pixel into the current output column.  Because of rounding, we may
            have a tiny bit of pixel left and have run out of output pixels.
            In that case, we throw away what's left.
         */
@@ -1799,7 +1850,7 @@ issueStretchWarning(bool   const verbose,
        row.  
     */
     if (verbose)
-        pm_message("%f of bottom row stretched due to "
+        pm_message("%f of bottom row stretched because of "
                    "arithmetic imprecision", 
                    fracrowtofill);
 }
@@ -1833,7 +1884,7 @@ scaleHorizontallyAndOutputRow(struct pam *             const inpamP,
                         xscale, &stretch);
             
         if (verbose && row == 0)
-            pm_message("%f of right column stretched due to "
+            pm_message("%f of right column stretched because of "
                        "arithmetic imprecision", 
                        stretch);
             
@@ -2085,24 +2136,18 @@ scaleWithoutMixing(const struct pam * const inpamP,
 
 
 
-int
-main(int argc, char **argv ) {
-
-    struct cmdlineInfo cmdline;
-    FILE* ifP;
+static void
+pamscale(FILE *             const ifP,
+         FILE *             const ofP,
+         struct CmdlineInfo const cmdline) {
+    
     struct pam inpam, outpam;
     float xscale, yscale;
 
-    pnm_init(&argc, argv);
-
-    parseCommandLine(argc, argv, &cmdline);
-
-    ifP = pm_openr(cmdline.inputFileName);
-
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
     outpam = inpam;  /* initial value */
-    outpam.file = stdout;
+    outpam.file = ofP;
 
     if (PNM_FORMAT_TYPE(inpam.format) == PBM_TYPE && !cmdline.nomix) {
         outpam.format = PGM_TYPE;
@@ -2113,8 +2158,8 @@ main(int argc, char **argv ) {
         outpam.maxval = inpam.maxval;
     }
 
-    computeOutputDimensions(cmdline, inpam.height, inpam.width,
-                            &outpam.height, &outpam.width);
+    computeOutputDimensions(cmdline, inpam.width, inpam.height, 
+                            &outpam.width, &outpam.height);
 
     xscale = (float) outpam.width / inpam.width;
     yscale = (float) outpam.height / inpam.height;
@@ -2151,6 +2196,29 @@ main(int argc, char **argv ) {
                  cmdline.windowFunction, cmdline.verbose,
                  cmdline.linear);
     }
+}
+
+
+
+int
+main(int argc, const char **argv ) {
+
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    int eof;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    eof = FALSE;
+    while (!eof) {
+        pamscale(ifP, stdout, cmdline);
+        pnm_nextimage(ifP, &eof);
+    }
+
     pm_close(ifP);
     pm_close(stdout);
     
diff --git a/editor/pamsistoaglyph.c b/editor/pamsistoaglyph.c
index 1c92658c..6b093520 100644
--- a/editor/pamsistoaglyph.c
+++ b/editor/pamsistoaglyph.c
@@ -83,7 +83,7 @@ parseCommandLine( int argc, const char ** const argv,
     opt.short_allowed = 1;
     opt.allowNegNum = 0;
 
-    optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0 );
 
     if (argc-1 < 1)
         cmdlineP->inputFilename = "-";
@@ -247,12 +247,19 @@ findRegionEyeSeparation( gray ** const grayArray,
 
 
 
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn compareInts;
+#endif
+
 static int
-compare_ints( const void * const firstP,
-              const void * const secondP ) {
+compareInts(const void * const a,
+            const void * const b) {
+
+    const int * const firstP = a;
+    const int * const secondP = b;
 
-    int const first  = *(int *)firstP;
-    int const second = *(int *)secondP;
+    int const first  = *firstP;
+    int const second = *secondP;
 
     int retval;
 
@@ -311,7 +318,7 @@ findEyeSeparation( struct pam *  const pamP,
                 rowSeparation[numValidRows++] = sep;
         }
         if (numValidRows > 0) {
-            qsort( rowSeparation, numValidRows, sizeof(int), compare_ints );
+            qsort(rowSeparation, numValidRows, sizeof(int), compareInts);
             bestSeparation = rowSeparation[numValidRows/2];
         }
         free( rowSeparation );
diff --git a/editor/pamstretch.c b/editor/pamstretch.c
index cfbddcb4..04883c35 100644
--- a/editor/pamstretch.c
+++ b/editor/pamstretch.c
@@ -88,7 +88,7 @@ parse_command_line(int argc, char ** argv,
     opt.short_allowed = FALSE; /* We have some short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (blackedge && dropedge) 
diff --git a/editor/pamthreshold.c b/editor/pamthreshold.c
index c5f48147..8d28bc4a 100644
--- a/editor/pamthreshold.c
+++ b/editor/pamthreshold.c
@@ -103,15 +103,15 @@ parseGeometry(const char *   const wxl,
 
     char * const xPos = strchr(wxl, 'x');
     if (!xPos)
-        asprintfN(errorP, "There is no 'x'.  It should be WIDTHxHEIGHT");
+        pm_asprintf(errorP, "There is no 'x'.  It should be WIDTHxHEIGHT");
     else {
         *widthP  = atoi(wxl);
         *heightP = atoi(xPos + 1);
 
         if (*widthP == 0)
-            asprintfN(errorP, "Width is zero.");
+            pm_asprintf(errorP, "Width is zero.");
         else if (*heightP == 0)
-            asprintfN(errorP, "Height is zero.");
+            pm_asprintf(errorP, "Height is zero.");
         else
             *errorP = NULL;
     }
@@ -161,13 +161,13 @@ parseCommandLine(int                 argc,
     /* set the defaults */
     cmdlineP->width = cmdlineP->height = 0U;
 
-    /* set the option description for optParseOptions3 */
+    /* set the option description for pm_optParseOptions3 */
     opt.opt_table     = option_def;
     opt.short_allowed = FALSE;           /* long options only */
     opt.allowNegNum   = FALSE;           /* we have no numbers at all */
 
     /* parse commandline, change argc, argv, and *cmdlineP */
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
 
     if (cmdlineP->simple + localSpec + dualSpec > 1)
         pm_error("You may specify only one of -simple, -local, and -dual");
@@ -194,7 +194,7 @@ parseCommandLine(int                 argc,
 
         if (error) {
             pm_error("Invalid -local value '%s'.  %s", localOpt, error);
-            strfree(error);
+            pm_strfree(error);
         }
     } else
         cmdlineP->local = FALSE;
@@ -207,7 +207,7 @@ parseCommandLine(int                 argc,
 
         if (error) {
             pm_error("Invalid -dual value '%s'.  %s", dualOpt, error);
-            strfree(error);
+            pm_strfree(error);
         }
     } else
         cmdlineP->dual = FALSE;
@@ -302,7 +302,7 @@ analyzeDistribution(struct pam *          const inpamP,
         pm_error("Unable to allocate space for %lu-entry histogram",
                  inpamP->maxval+1);
 
-    /* Initialize histogram -- zero occurences of everything */
+    /* Initialize histogram -- zero occurrences of everything */
     for (i = 0; i <= inpamP->maxval; ++i)
         histogram[i] = 0;
 
@@ -376,7 +376,7 @@ computeGlobalThreshold(struct pam *         const inpamP,
    Compute the proper threshold to use for the image described by
    *inpamP, and:
 
-     'histogram' describes the frequency of occurence of the various sample
+     'histogram' describes the frequency of occurrence of the various sample
      values in the image.
 
      'globalRange' describes the range (minimum, maximum) of sample values
@@ -658,7 +658,7 @@ main(int argc, char **argv) {
     FILE * ifP; 
     struct cmdlineInfo cmdline;
     struct pam inpam, outpam;
-    bool eof;  /* No more images in input stream */
+    int eof;  /* No more images in input stream */
 
     pnm_init(&argc, argv);
 
diff --git a/editor/pamundice.c b/editor/pamundice.c
index 89d8b6b5..9a80e46d 100644
--- a/editor/pamundice.c
+++ b/editor/pamundice.c
@@ -50,7 +50,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
     
@@ -76,7 +76,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (!acrossSpec)
@@ -232,12 +232,12 @@ doSubstitution(const char *    const pattern,
 
             switch (pattern[inCursor]) {
             case 'a':
-                asprintfN(&substString, "%0*u", precision, file);
-                asprintfN(&desc, "file (across)");
+                pm_asprintf(&substString, "%0*u", precision, file);
+                pm_asprintf(&desc, "file (across)");
                 break;
             case 'd':
-                asprintfN(&substString, "%0*u", precision, rank);
-                asprintfN(&desc, "rank (down)");
+                pm_asprintf(&substString, "%0*u", precision, rank);
+                pm_asprintf(&desc, "rank (down)");
                 break;
             default:
                 pm_error("Unknown format specifier '%c' in input file "
@@ -249,12 +249,12 @@ doSubstitution(const char *    const pattern,
                 pm_error("%s number %u is wider than "
                          "the %u characters specified in the "
                          "input file pattern",
-                         desc, strlen(substString), precision);
+                         desc, (unsigned)strlen(substString), precision);
             else
                 buffer_addString(bufferP, substString);
             
-            strfree(desc);
-            strfree(substString);
+            pm_strfree(desc);
+            pm_strfree(substString);
 
             ++inCursor;
         }
@@ -288,7 +288,7 @@ computeInputFileName(const char *  const pattern,
             buffer_addChar(&buffer, pattern[inCursor++]);
     }
 
-    asprintfN(fileNameP, "%s", buffer.string);
+    pm_asprintf(fileNameP, "%s", buffer.string);
 
     buffer_term(&buffer);
 }
@@ -327,7 +327,7 @@ getCommonInfo(const char *   const inputFilePattern,
 
     pm_close(ifP);
 
-    strfree(fileName);
+    pm_strfree(fileName);
 }
 
 
@@ -344,7 +344,7 @@ openInputImage(const char * const inputFilePattern,
 
     retval = pm_openr(fileName);
     
-    strfree(fileName);
+    pm_strfree(fileName);
 
     return retval;
 }
@@ -491,7 +491,12 @@ openInStreams(struct pam         inpam[],
 /*----------------------------------------------------------------------------
    Open the input files for a single horizontal slice (there's one file
    for each vertical slice) and read the Netpbm headers from them.  Return
-   the pam structures to describe each.
+   the pam structures to describe each as inpam[].
+
+   Open the files for horizontal slice number 'rank', assuming there are
+   'fileCount' vertical slices (so open 'fileCount' files).  Use
+   inputFilePattern[] with each rank and file number to compute the name of
+   each file.
 -----------------------------------------------------------------------------*/
     unsigned int file;
 
@@ -507,7 +512,9 @@ openInStreams(struct pam         inpam[],
 static void
 closeInFiles(struct pam         pam[],
              unsigned int const fileCount) {
-
+/*----------------------------------------------------------------------------
+   Close the 'fileCount' input file streams represented by pam[].
+-----------------------------------------------------------------------------*/
     unsigned int file;
     
     for (file = 0; file < fileCount; ++file)
diff --git a/editor/pamwipeout.c b/editor/pamwipeout.c
new file mode 100644
index 00000000..0fff3fca
--- /dev/null
+++ b/editor/pamwipeout.c
@@ -0,0 +1,229 @@
+/* pamwipeout.c - read a bitmap and replace it with a gradient between two
+** edges
+**
+** Copyright (C) 2011 by 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.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
+
+
+enum Direction {DIR_LR, DIR_TB};
+
+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 */
+    enum Direction direction;  /* top-bottom or left-right */
+};
+
+
+
+static void
+parseCommandLine(int                        argc, 
+                 const char **              argv,
+                 struct cmdlineInfo * const cmdlineP ) {
+/*----------------------------------------------------------------------------
+   Parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int lr, tb;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "lr",     OPT_FLAG,   NULL,                  
+            &lr,       0);
+    OPTENT3(0, "tb",     OPT_FLAG,   NULL,                  
+            &tb,       0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!lr && !tb)
+        pm_error("You must specify either -lr or -tb");
+    else if (lr && tb)
+        pm_error("You may not specify both -lr and -tb");
+    else 
+        cmdlineP->direction = lr ? DIR_LR : DIR_TB;
+
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+        if (argc-1 > 1)
+            pm_error("Too many arguments: %u.  "
+                     "The only possible argument is the "
+                     "optional input file name", argc-1);
+    }
+}
+
+
+
+
+static void 
+wipeImgByRow (struct pam const inpam, 
+              tuple **   const tuples) {
+
+    double const h = (double) inpam.height;
+
+    unsigned int row;
+    unsigned int col;
+
+    for (row = 0; row < inpam.height; ++row)  {
+        double const y = (double) row;
+        for (col = 0; col < inpam.width; ++col)  {
+            unsigned int i;
+            for (i = 0; i < inpam.depth; ++i) {
+                sample const top = tuples[0][col][i];
+                sample const bot = tuples[inpam.height - 1][col][i];
+                tuples[row][col][i] = (int)
+                    floor(((h - y) / h)
+                          * (double) top + (y / h)
+                          * (double) bot);
+            }
+        }
+    }
+}
+
+
+
+static void 
+wipeRowByCol(struct pam const inpam, 
+             tuple **   const tuples, 
+             tuple *    const tuplerow) {
+
+    double const w = (double) inpam.width;
+
+    unsigned int col;
+
+    for (col = 0; col < inpam.width; ++col)  {
+        double const x = (double) col;
+        unsigned int i;
+        for (i = 0; i < inpam.depth; ++i) {
+            sample const lft = tuplerow[0][i];
+            sample const rgt = tuplerow[inpam.width - 1][i];
+            tuplerow[col][i] = (int)
+                floor( ((w - x) / w)
+                       * (double) lft + (x / w)
+                       * (double) rgt );
+        }
+    }
+}
+
+
+
+static void
+wipeoutTb(FILE * const ifP,
+          FILE * const ofP) {
+
+    /* top-bottom we have to read the full image */
+
+    struct pam inpam, outpam;
+    tuple ** tuples;
+    
+    tuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    outpam = inpam; 
+    outpam.file = ofP;
+
+    wipeImgByRow(inpam, tuples);
+
+    pnm_writepam(&outpam, tuples);
+       
+    pnm_freepamarray(tuples, &inpam);
+}
+
+
+
+static void
+wipeoutLr(FILE * const ifP,
+          FILE * const ofP) {
+    
+    /* left-right we can read row-by-row */
+
+    struct pam inpam, outpam;
+    tuple ** tuples;
+    tuple * tuplerow;
+    unsigned int row;
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    outpam = inpam;
+    outpam.file = ofP;
+
+    pnm_writepaminit(&outpam);
+
+    tuplerow = pnm_allocpamrow(&inpam);
+
+    for (row = 0; row < inpam.height; ++row) {
+        pnm_readpamrow(&inpam, tuplerow);
+
+        wipeRowByCol(inpam, tuples, tuplerow);
+
+        pnm_writepamrow(&outpam, tuplerow);
+    }
+
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    switch (cmdline.direction) {
+    case DIR_TB:
+        wipeoutTb(ifP, stdout);
+        break;
+    case DIR_LR:
+        wipeoutLr(ifP, stdout);
+        break;
+    }
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/editor/pbmclean.c b/editor/pbmclean.c
index 65b53e1c..46e7dee6 100644
--- a/editor/pbmclean.c
+++ b/editor/pbmclean.c
@@ -1,99 +1,98 @@
-/* pbmclean.c - pixel cleaning. Remove pixel if less than n connected
- *              identical neighbours, n=1 default.
- * AJCD 20/9/90
- * stern, Fri Oct 19 00:10:38 MET DST 2001
- *     add '-white/-black' flags to restrict operation to given blobs
- */
+/*=============================================================================
+                                 pbmclean
+===============================================================================
+  Pixel cleaner:   Remove pixel if less than N connected identical neighbors
 
+=============================================================================*/
+#include <assert.h>
 #include <stdio.h>
 
 #include "pm_c_util.h"
-#include "pbm.h"
+#include "mallocvar.h"
 #include "shhopt.h"
+#include "pbm.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;  /* Filespecs of input files */
+    const char * inputFileName;  /* File name of input file */
     bool flipWhite;
     bool flipBlack;
-    unsigned int connect;
+    unsigned int minneighbors;
     unsigned int verbose;
+    unsigned int extended;
 };
 
-#define PBM_INVERT(p) ((p) == PBM_WHITE ? PBM_BLACK : PBM_WHITE)
-
-/* input bitmap size and storage */
-static bit *inrow[3] ;
-
-#define THISROW (1)
 
-enum compass_heading {
-    WEST=0,
-    NORTHWEST=1,
-    NORTH=2,
-    NORTHEAST=3,
-    EAST=4,
-    SOUTHEAST=5,
-    SOUTH=6,
-    SOUTHWEST=7
-};
-/* compass directions from west clockwise.  Indexed by enum compass_heading */
-int const xd[] = { -1, -1,  0,  1, 1, 1, 0, -1 } ;
-int const yd[] = {  0, -1, -1, -1, 0, 1, 1,  1 } ;
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optStruct3 opt;  /* set by OPTENT3 */
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
     unsigned int option_def_index;
 
     unsigned int black, white;
     unsigned int minneighborsSpec;
 
+    MALLOCARRAY(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "verbose", OPT_FLAG, NULL, &cmdlineP->verbose, 0);
-    OPTENT3(0,   "black", OPT_FLAG, NULL, &black, 0);
-    OPTENT3(0,   "white", OPT_FLAG, NULL, &white, 0);
-    OPTENT3(0,   "minneighbors", OPT_UINT, &cmdlineP->connect, 
+    OPTENT3(0,   "verbose",          OPT_FLAG, NULL, &cmdlineP->verbose, 0);
+    OPTENT3(0,   "black",            OPT_FLAG, NULL, &black, 0);
+    OPTENT3(0,   "white",            OPT_FLAG, NULL, &white, 0);
+    OPTENT3(0,   "minneighbors",     OPT_UINT, &cmdlineP->minneighbors, 
             &minneighborsSpec, 0);
+    OPTENT3(0,   "extended",         OPT_FLAG, NULL, &cmdlineP->extended, 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = TRUE;  /* We sort of allow negative numbers as parms */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (!black && !white) {
-        cmdlineP->flipBlack = TRUE;
-        cmdlineP->flipWhite = TRUE;
+    free(option_def);
+
+    if (cmdlineP->extended) {
+        if (black && white)
+            pm_error("With -extended, you cannot specify both "
+                     "-black and -white");
+        else if (!black & !white) {
+            cmdlineP->flipBlack = TRUE;
+            cmdlineP->flipWhite = FALSE;
+        } else {
+            cmdlineP->flipBlack = !!black;
+            cmdlineP->flipWhite = !!white;
+        }
     } else {
-        cmdlineP->flipBlack = !!black;
-        cmdlineP->flipWhite = !!white;
-    }    
-
-
+        if (!black && !white) {
+            cmdlineP->flipBlack = TRUE;
+            cmdlineP->flipWhite = TRUE;
+        } else {
+            cmdlineP->flipBlack = !!black;
+            cmdlineP->flipWhite = !!white;
+        }    
+    }
     if (!minneighborsSpec) {
         /* Now we do a sleazy tour through the parameters to see if
            one is -N where N is a positive integer.  That's for
            backward compatibility, since Pbmclean used to have
            unconventional syntax where a -N option was used instead of
            the current -minneighbors option.  The only reason -N didn't
-           get processed by pm_optParseOptions3() is that it looked
+           get processed by pm_pm_optParseOptions3() is that it looked
            like a negative number parameter instead of an option.  
            If we find a -N, we make like it was a -minneighbors=N option.
         */
         int i;
         bool foundNegative;
 
-        cmdlineP->connect = 1;  /* default */
+        cmdlineP->minneighbors = 1;  /* default */
         foundNegative = FALSE;
 
         for (i = 1; i < argc; ++i) {
@@ -101,7 +100,7 @@ parseCommandLine(int argc, char ** argv,
                 argv[i-1] = argv[i];
             else {
                 if (atoi(argv[i]) < 0) {
-                    cmdlineP->connect = - atoi(argv[i]);
+                    cmdlineP->minneighbors = - atoi(argv[i]);
                     foundNegative = TRUE;
                 }
             }
@@ -111,9 +110,9 @@ parseCommandLine(int argc, char ** argv,
     }
 
     if (argc-1 < 1) 
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
     else if (argc-1 == 1)
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
     else
         pm_error("You specified too many arguments (%d).  The only "
                  "argument is the optional input file specification.",
@@ -122,120 +121,698 @@ parseCommandLine(int argc, char ** argv,
 
 
 
+static unsigned int
+bitpop8(unsigned char const x) {
+/*----------------------------------------------------------------------------
+   Return the number of 1 bits in 'x'
+-----------------------------------------------------------------------------*/
+static unsigned int const p[256] = {
+    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 };
+
+    return p[x];
+}
+
 
 
-static void 
-nextrow(FILE * const ifd,
-        int    const row,
-        int    const cols,
-        int    const rows,
-        int    const format) {
+static unsigned int
+bitpop24(uint32_t const w){
 /*----------------------------------------------------------------------------
-   Advance one row in the input.
+   Return the number of 1 bits in the lower 24 bits of 'w'
+   A GCC builtin, __builtin_popcountl(), is available, but it
+   emits a call to an external function instead of inlining (GCC 4.4.3).
 
-   'row' is the row number that will be the current row.
+   This table lookup method is faster.
 -----------------------------------------------------------------------------*/
-    bit * shuffle;
+    return (bitpop8((w >> 16) & 0xff) +
+            bitpop8((w >>  8) & 0xff) +
+            bitpop8((w >>  0) & 0xff));  
+}
 
-    /* First, get the "next" row in inrow[2] if this is the very first
-       call to nextrow().
-    */
-    if (inrow[2] == NULL && row < rows) {
-        inrow[2] = pbm_allocrow(cols);
-        pbm_readpbmrow(ifd, inrow[2], cols, format);
-    }
-    /* Now advance the inrow[] window, rotating the buffer that now holds
-       the "previous" row to use it for the new "next" row.
-    */
-    shuffle = inrow[0];
-
-    inrow[0] = inrow[1];
-    inrow[1] = inrow[2];
-    inrow[2] = shuffle ;
-    if (row+1 < rows) {
-        /* Read the "next" row in from the file.  Allocate buffer if needed */
-        if (inrow[2] == NULL)
-            inrow[2] = pbm_allocrow(cols);
-        pbm_readpbmrow(ifd, inrow[2], cols, format);
-    } else {
-        /* There is no next row */
-        if (inrow[2]) {
-            pbm_freerow(inrow[2]);
-            inrow[2] = NULL; 
+
+
+/*----------------------------------------------------------------------------
+Fast algorithm for counting friendly neighbor pixels
+
+In this program both input and output rows are in raw (packed) PBM format.
+
+We handle input rows in groups of three, named "prevrow", "thisrow",
+"nextrow" and scan from left to right.  At every byte boundary, 10 bits
+are read from each of the three rows and placed into a temporary storage
+we call "sample".
+
+prevrow: ... ... _______M NNNNNNNN O_______ ...
+thisrow: ... ... _______W cCCCCCCC E_______ ...
+nextrow: ... ... _______R SSSSSSSS T_______ ...
+
+sample : xxMNNNNNNNNOWcCCCCCCCERSSSSSSST
+
+We count bits by taking the logical and of "sample" and a bit-mask called
+"selection", and feeding the result to a table-based bit-population counter.
+
+For example, the bits around the leftmost bit of the byte ("c") are selected
+like this:
+
+sample :       xxMNNNNNNNNOWcCCCCCCCERSSSSSSST
+selection: & | __111_______1_1_______111______
+
+(In the actual process, "sample" is shifted right and anded against a
+ constant "selection" mask.)
+
+The above reports one bits.  For the zero (white) bits we replace "sample"
+with its inverse.
+
+If the friendly neighbor count is below a threshold (default 1), we record
+that as a one bit in "flipmask".  Bits are flipped in units of eight
+and written to outrow at the byte boundary.
+-----------------------------------------------------------------------------*/
+
+
+
+static unsigned int
+likeNeighbors(uint32_t     const blackSample, 
+              unsigned int const offset) {
+
+    bool const thispoint = ( blackSample >> (18-offset) ) & 0x01;
+    uint32_t const sample = (thispoint == PBM_BLACK ) 
+                          ?   blackSample
+                          : ~ blackSample ;
+    uint32_t const selection = 0x701407;
+
+    return (bitpop24((sample >> (7-offset)) & selection));
+}
+
+
+
+static uint32_t
+setSample(const bit *  const prevrow,
+          const bit *  const thisrow,
+          const bit *  const nextrow,
+          unsigned int const col){
+
+    int const col8 = col/8;
+
+    uint32_t sample;
+
+    sample =
+        ((prevrow[col8 - 1]       )  << 29) |
+        ((prevrow[col8]           )  << 21) |
+        ((prevrow[col8 + 1] & 0x80)  << 13) |
+        ((thisrow[col8 - 1] & 0x01)  << 19) |
+        ((thisrow[col8]           )  << 11) |
+        ((thisrow[col8 + 1] & 0x80)  <<  3) |
+        ((nextrow[col8 - 1] & 0x01)  <<  9) |
+        ((nextrow[col8]           )  <<  1) |
+        ((nextrow[col8 + 1] & 0x80)  >>  7);
+    
+    return sample;
+}
+
+
+
+static unsigned char
+setTestmask(unsigned char const whiteTestmask,
+            bool          const testWhite,
+            bool          const testBlack) {
+/* -----------------------------------------------------------------------
+  Make a byte pattern of what bits should be tested within a given "thisrow"
+  (current inrow) byte.  0 means test, 1 means skip.
+-------------------------------------------------------------------------- */
+    if (testWhite == testBlack) {
+        assert(testWhite); assert(testBlack);
+        return 0x00;
+    } else if (testWhite == TRUE) {
+        assert(!testBlack);
+        return whiteTestmask;
+    } else
+        return ~whiteTestmask;
+}
+
+
+
+static void
+cleanrow(const bit *    const prevrow,
+         const bit *    const thisrow,
+         const bit *    const nextrow,
+         bit *          const outrow,
+         unsigned int   const cols,
+         unsigned int   const threshold,
+         bool           const flipWhite,
+         bool           const flipBlack,
+         unsigned int * const nFlippedP) {
+/* ----------------------------------------------------------------------
+  Work through row, scanning for bits that require flipping, and write
+  the results to 'outrow'.
+  
+  Returns the number of bits flipped within this one row as *nFlippedP.
+-------------------------------------------------------------------------*/
+    uint32_t sample;
+    unsigned char testmask;
+    unsigned char flipmask;
+    unsigned int col;
+    unsigned int nFlipped;
+
+    flipmask = 0x00;  /* initial value */
+    nFlipped = 0;     /* initial value */
+
+    for (col=0 ; col < cols ; ++col) {
+        unsigned int const col8 = col / 8;
+        unsigned int const offset = col % 8;
+
+        if (offset == 0) {
+            if (flipmask != 0x00) {
+                /* Some bits have to be flipped */
+                outrow[col8 -1] = thisrow [col8 -1] ^ flipmask;
+                nFlipped += bitpop8(flipmask);
+                flipmask = 0x00;
+            } else if (col8 > 0)
+                outrow[col8 -1] = thisrow [col8 -1];
+
+            sample = setSample(prevrow, thisrow, nextrow, col);
+            testmask = setTestmask(thisrow[col8], flipWhite, flipBlack);
+        }
+
+        if (((testmask << offset) & 0x80 ) ==0) {
+            if (likeNeighbors(sample, offset ) < threshold)
+                flipmask |= (0x80 >> offset);
         }
     }
+
+    {
+        /* Write out last byte */
+        unsigned int const col8Last = pbm_packed_bytes(cols) -1;
+
+        if (flipmask != 0x00) {
+            outrow[col8Last] = thisrow[col8Last] ^ flipmask;
+            nFlipped += bitpop8(flipmask);
+        } else
+            outrow[col8Last] = thisrow[col8Last];
+    }
+    *nFlippedP = nFlipped;
+}
+
+
+
+static void
+setupInputBuffers(FILE *       const ifP,
+                  unsigned int const cols,
+                  int          const format,
+                  bit ***      const bufferP,
+                  bit **       const edgeRowP,
+                  bit **       const thisRowP,
+                  bit **       const nextRowP) {
+/*----------------------------------------------------------------------------
+  Initialize input buffers.
+  We add a margin of 8 bits each on the left and right of the rows.
+
+  On the top and bottom of the image we place an imaginary blank row
+  ("edgerow") to facilitate the process.
+-----------------------------------------------------------------------------*/
+    bit ** const buffer  = pbm_allocarray_packed(cols+16, 3);
+    bit *  const edgeRow = pbm_allocrow_packed(cols+16);
+
+    bit * nextRow;
+    unsigned int i;
+
+    for (i = 0; i < pbm_packed_bytes(cols+16); ++i)
+        edgeRow[i] = 0x00;
+        
+    for (i = 0; i < 3; ++i) {
+        /* Add blank (all white) bytes beside the edges */ 
+        buffer[i][0] = buffer[i][ pbm_packed_bytes( cols +16 ) - 1] = 0x00;
+    }
+    nextRow = &buffer[0][1];
+
+    /* Read the top line into nextrow and clean the right end. */
+
+    pbm_readpbmrow_packed(ifP, nextRow, cols, format);
+
+    pbm_cleanrowend_packed(nextRow, cols);
+
+    *bufferP  = buffer;
+    *edgeRowP = edgeRow;
+    *thisRowP = &edgeRow[1];
+    *nextRowP = nextRow;
+}
+
+
+
+static void
+cleanSimple(FILE *             const ifP,
+            FILE *             const ofP,
+            struct cmdlineInfo const cmdline,
+            double *           const nFlippedP) {
+/*----------------------------------------------------------------------------
+   Do the traditional clean where you look only at the immediate neighboring
+   pixels of a subject pixel to determine whether to erase that pixel.
+-----------------------------------------------------------------------------*/
+    bit ** buffer;
+        /* The rows of the input relevant to our current processing:
+           the current row and the one above and below it.
+        */
+    bit * edgeRow;
+        /* A blank (all white) row.  Constant */
+    bit * prevRow;
+    bit * thisRow;
+    bit * nextRow;
+    bit * outRow;
+    int cols, rows, format;
+    unsigned int row;
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    setupInputBuffers(ifP, cols, format, &buffer, &edgeRow,
+                      &thisRow, &nextRow);
+
+    outRow = pbm_allocrow(cols);
+
+    pbm_writepbminit(ofP, cols, rows, 0) ;
+
+    *nFlippedP = 0;  /* none flipped yet */
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int nFlipped;
+
+        prevRow = thisRow;  /* Slide up the input row window */
+        thisRow = nextRow;
+        if (row < rows -1){
+            nextRow = &buffer[(row+1)%3][1];
+            /* We take the address directly instead of shuffling the rows
+               with the help of a temporary.  This provision is for proper 
+               handling of the initial edgerow.
+            */
+            pbm_readpbmrow_packed(ifP, nextRow, cols, format);
+            pbm_cleanrowend_packed(nextRow, cols);
+
+        } else  /* Bottom of image.  */
+            nextRow = &edgeRow[1];
+
+        cleanrow(prevRow, thisRow, nextRow, outRow, cols, cmdline.minneighbors,
+                 cmdline.flipWhite, cmdline.flipBlack, &nFlipped);
+        
+        *nFlippedP += nFlipped;
+
+        pbm_writepbmrow_packed(ofP, outRow, cols, 0) ;
+    }
+
+    pbm_freearray(buffer, 3);
+    pbm_freerow(edgeRow);
+    pbm_freerow(outRow);
+}
+
+
+
+struct PixQueueElt {
+    struct PixQueueElt * nextP;
+    pm_pixelcoord        coord;
+};
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   A queue of pixel locations.
+-----------------------------------------------------------------------------*/
+    unsigned int size;
+    
+    struct PixQueueElt * headP;
+    struct PixQueueElt * tailP;
+} PixQueue;
+
+
+
+static void
+pixQueue_init(PixQueue * const queueP) {
+
+    queueP->size = 0;
+    queueP->headP = NULL;
+    queueP->tailP = NULL;
 }
 
 
 
 static unsigned int
-likeNeighbors(bit *        const inrow[3], 
-              unsigned int const col, 
-              unsigned int const cols) {
+pixQueue_size(PixQueue * const queueP) {
+
+    return queueP->size;
+}
+
+
+
+static bool
+pixQueue_isEmpty(PixQueue * const queueP) {
+
+    return !queueP->headP;
+}
+
+
+
+static void
+pixQueue_push(PixQueue *    const queueP,
+              pm_pixelcoord const newValue) {
+
+    struct PixQueueElt * newEltP;
+
+    MALLOCVAR(newEltP);
+
+    if (!newEltP)
+        pm_error("Out of memory putting a pixel on a queue");
+
+    newEltP->coord = newValue;
+
+    newEltP->nextP = NULL;
+    if (queueP->tailP)
+        queueP->tailP->nextP = newEltP;
+    
+    queueP->tailP = newEltP;
+
+    if (!queueP->headP)
+        queueP->headP = newEltP;
+
+    ++queueP->size;
+}
+
+
+
+static pm_pixelcoord
+pixQueue_pop(PixQueue * const queueP) {
+/*----------------------------------------------------------------------------
+   Pop and return the pixel location at the head of queue *queueP.
+-----------------------------------------------------------------------------*/
+    struct PixQueueElt * const newHeadP = queueP->headP->nextP;
+
+    pm_pixelcoord retval;
+
+    assert(queueP->headP);
+
+    retval = queueP->headP->coord;
+
+    if (queueP->tailP == queueP->headP)
+        queueP->tailP = NULL;
+
+    free(queueP->headP);
+
+    queueP->headP = newHeadP;
+
+    --queueP->size;
+
+    return retval;
+}
+
+
+
+static void
+pixQueue_term(PixQueue * const queueP) {
+
+    struct PixQueueElt * p;
+    struct PixQueueElt * nextP;
     
-    int const point = inrow[THISROW][col];
-    enum compass_heading heading;
-    int joined;
-
-    joined = 0;  /* initial value */
-    for (heading = WEST; heading <= SOUTHWEST; ++heading) {
-        int x = col + xd[heading] ;
-        int y = THISROW + yd[heading] ;
-        if (x < 0 || x >= cols || !inrow[y]) {
-            if (point == PBM_WHITE) joined++;
-        } else if (inrow[y][x] == point) joined++ ;
+    for (p = queueP->headP; p; p = nextP) {
+        nextP = p->nextP;
+        free(p);
     }
-    return joined;
 }
 
 
 
-int
-main(int argc, char *argv[]) {
+static void
+queueNeighbors(pm_pixelcoord const center,
+               bit **        const pixels,
+               unsigned int  const cols,
+               unsigned int  const rows,
+               bool **       const visited,
+               PixQueue *    const queueP) {
+/*----------------------------------------------------------------------------
+   Add to queue *queueP all the pixels in 'pixels' that touch 'center' and are
+   the same color as 'center'.
 
-    struct cmdlineInfo cmdline;
-    FILE *ifp;
-    bit *outrow;
-    int cols, rows, format;
-    unsigned int row;
-    unsigned int nFlipped;  /* Number of pixels we have flipped so far */
+   But ignore pixels that 'visited' says have already been queued and
+   mark everything we queue as visited.
+-----------------------------------------------------------------------------*/
+    bit const blobColor = pixels[center.row][center.col];
 
-    pbm_init( &argc, argv );
+    int row;
+        /* Row number of a neighbor; might be off the canvas; even negative */
 
-    parseCommandLine(argc, argv, &cmdline);
+    /* Note that we consider the center pixel here, but it has necessarily
+       already been visited, so we don't queue it.
+    */
+
+    for (row = (int)center.row - 1; row <= (int)center.row + 1; ++row) {
+        int col;  /* Analogous to 'row' */
+
+        for (col = (int)center.col - 1; col <= (int)center.col + 1; ++col) {
+            if (row < 0 || row >= rows || col < 0 || col >= cols) {
+                /* It's off the canvas; nothing to queue */
+            } else {
+                if (pixels[row][col] == blobColor) {
+                    if (visited[row][col]) {
+                        /* We've already explored this one */
+                    } else {
+                        /* Queue it! */
+                        pm_pixelcoord neighbor;
+                        neighbor.row = row;
+                        neighbor.col = col;
+                        pixQueue_push(queueP, neighbor);
+                        visited[row][col] = true;
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+
+static void
+setColor(PixQueue * const blobP,
+         bit **     const pixels,
+         bit        const newColor) {
+/*----------------------------------------------------------------------------
+   Change all the pixels in (blobP) to 'newColor'.  More precisely, change
+   the pixels in 'pixels' that are listed in *blobP.
+-----------------------------------------------------------------------------*/
+    while (!pixQueue_isEmpty(blobP)) {
+        pm_pixelcoord const thisPix = pixQueue_pop(blobP);
+
+        pixels[thisPix.row][thisPix.col] = newColor;
+    }
+}
+
+
+
+static void
+processBlob(pm_pixelcoord const start,
+            bit **        const pixels,
+            unsigned int  const cols,
+            unsigned int  const rows,
+            unsigned int  const trivialSize,
+            bool **       const visited,
+            double *      const nFlippedP) {
+/*----------------------------------------------------------------------------
+   Process the blob that contains the pixel at 'start'.
+
+   That pixel is part of a blob.  A blob is a maximal set of contiguous
+   pixels of the same color.
+
+   None of the blob is marked visited in visited[][].
+
+   If the blob has fewer than 'trivialSize' pixels, erase it (flip its color).
+
+   Update visited[][] to flag all pixels of the blob as visited.
+
+   Return as *nFlippedP how many pixels we flipped (i.e. either zero or the
+   size of the blob).
+-----------------------------------------------------------------------------*/
+    /* In addition to putting output in it, we use visited[][] for working
+       memory.  It indicates pixels of the blob that we've queued for
+       processing so far.
+    */
+    PixQueue toExplore;
+    PixQueue blob;
+
+    pixQueue_init(&toExplore);
+    pixQueue_init(&blob);
+
+    pixQueue_push(&toExplore, start);
+    visited[start.row][start.col] = true;
+
+    while (!pixQueue_isEmpty(&toExplore)) {
+        pm_pixelcoord const thisPix = pixQueue_pop(&toExplore);
+
+        pixQueue_push(&blob, thisPix);
+
+        queueNeighbors(thisPix, pixels, cols, rows, visited, &toExplore);
+    }
+
+    if (pixQueue_size(&blob) <= trivialSize) {
+        bit const blobColor = pixels[start.row][start.col];
+
+        *nFlippedP = pixQueue_size(&blob);
+
+        setColor(&blob, pixels,
+                 blobColor == PBM_WHITE ? PBM_BLACK : PBM_WHITE);
+    } else
+        *nFlippedP = 0;
 
-    ifp = pm_openr(cmdline.inputFilespec);
+    pixQueue_term(&blob);
+    pixQueue_term(&toExplore);
+}
 
-    inrow[0] = inrow[1] = inrow[2] = NULL;
-    pbm_readpbminit(ifp, &cols, &rows, &format);
 
-    outrow = pbm_allocrow(cols);
 
-    pbm_writepbminit(stdout, cols, rows, 0) ;
+static void
+setAllNotVisited(bool **      const visited,
+                 unsigned int const cols,
+                 unsigned int const rows)  {
 
-    nFlipped = 0;  /* No pixels flipped yet */
+    unsigned int row;
     for (row = 0; row < rows; ++row) {
         unsigned int col;
-        nextrow(ifp, row, cols, rows, format);
-        for (col = 0; col < cols; ++col) {
-            bit const thispoint = inrow[THISROW][col];
-            if ((cmdline.flipWhite && thispoint == PBM_WHITE) ||
-                (cmdline.flipBlack && thispoint == PBM_BLACK)) {
-                if (likeNeighbors(inrow, col, cols) < cmdline.connect) {
-                    outrow[col] = PBM_INVERT(thispoint);
-                    ++nFlipped;
-                } else
-                    outrow[col] = thispoint;
-            } else 
-                outrow[col] = thispoint;
+        for (col = 0; col < cols; ++col)
+            visited[row][col] = false;
+    }
+}
+
+
+
+static void
+cleanPixels(bit **       const pixels,
+            unsigned int const cols,
+            unsigned int const rows,
+            bit          const foregroundColor,
+            unsigned int const trivialSize,
+            double *     const nFlippedP) {
+/*----------------------------------------------------------------------------
+   Same as cleanExtended(), except we work on the pixels 'pixels' instead
+   of input and output files.
+-----------------------------------------------------------------------------*/
+    pm_pixelcoord thisPix;
+
+    bool ** visited;  /* malloced */
+        /* visited[row][col] means we have processed the pixel at (row, col)
+           and flipped it if it needed to be flipped.
+        */
+
+    MALLOCARRAY2(visited, rows, cols);
+
+    if (!visited)
+        pm_error("Could not allocate a %u x %u array for visited flags",
+                 rows, cols);
+
+    setAllNotVisited(visited, cols, rows);
+
+    *nFlippedP = 0;  /* initial value */
+
+    for (thisPix.row = 0; thisPix.row < rows; ++thisPix.row) {
+        for (thisPix.col = 0; thisPix.col < cols; ++thisPix.col) {
+            if (pixels[thisPix.row][thisPix.col] == foregroundColor
+                && !visited[thisPix.row][thisPix.col]) {
+                
+                double nFlipped;
+                
+                processBlob(thisPix, pixels, cols, rows, trivialSize,
+                            visited, &nFlipped);
+
+                *nFlippedP += nFlipped;
+            } else
+                visited[thisPix.row][thisPix.col] = true;
         }
-        pbm_writepbmrow(stdout, outrow, cols, 0) ;
     }
-    pbm_freerow(outrow);
-    pm_close(ifp);
+
+    pm_freearray2((void **)visited);
+}
+
+
+
+static void
+cleanExtended(FILE *             const ifP,
+              FILE *             const ofP,
+              bit                const foregroundColor,
+              unsigned int       const trivialSize,
+              double *           const nFlippedP) {
+/*----------------------------------------------------------------------------
+   Clean the image on *ifP and write the result to *ofP.
+
+   Look at arbitrarily shaped and sized blobs to determine what to erase.
+
+   A blob is a contiguous set of pixels of the foreground color
+   ('foregroundColor') which is not contiguous with any other pixels of that
+   color.
+
+   We erase (flip) every pixel in every trivial blob.  A trivial blob is
+   one with 'trivialSize' pixels or fewer.
+-----------------------------------------------------------------------------*/
+    bit ** pixels;
+    int cols, rows;
+
+    pixels = pbm_readpbm(ifP, &cols, &rows);
+
+	cleanPixels(pixels, cols, rows, foregroundColor, trivialSize, nFlippedP);
+
+    pbm_writepbm(ofP, pixels, cols, rows, 0);
+
+    pbm_freearray(pixels, rows);
+}
+
+
+
+static void
+pbmclean(FILE *             const ifP,
+         FILE *             const ofP,
+         struct cmdlineInfo const cmdline,
+         double *           const nFlippedP) {
+
+    if (cmdline.extended) {
+        bit const foregroundColor = cmdline.flipWhite ? PBM_WHITE : PBM_BLACK;
+
+        assert(cmdline.flipWhite + cmdline.flipBlack == 1);
+
+        cleanExtended(ifP, ofP, foregroundColor, cmdline.minneighbors,
+                      nFlippedP);
+    } else
+        cleanSimple(ifP, ofP, cmdline, nFlippedP);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    double nFlipped;
+        /* Number of pixels we have flipped so far.  Use type double to
+           prevent overflow.
+        */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pbmclean(ifP, stdout, cmdline, &nFlipped);
 
     if (cmdline.verbose)
-        pm_message("%d pixels flipped", nFlipped);
+        pm_message("%f pixels flipped", nFlipped);
+
+    pm_close(ifP);
 
     return 0;
 }
+
+
+
diff --git a/editor/pbmpscale.c b/editor/pbmpscale.c
index 2e24f3cd..9ab89350 100644
--- a/editor/pbmpscale.c
+++ b/editor/pbmpscale.c
@@ -3,22 +3,63 @@
  */
 
 #include <stdio.h>
-#include "pbm.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pbm.h"
+#include "bitarith.h"
 
-/* prototypes */
-void nextrow_pscale ARGS((FILE *ifd, int row));
-int corner ARGS((int pat));
+#define LEFTBITS pm_byteLeftBits
+#define RIGHTBITS pm_byteRightBits
 
-/* input bitmap size and storage */
-int rows, columns, format ;
-bit *inrow[3] ;
+/* Table for translating bit pattern into "corners" flag element */ 
 
-#define thisrow (1)
+unsigned char const
+transTable[512] = {
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04,
+     0x00, 0x00, 0x04, 0x04, 0xaa, 0xa2, 0x82, 0x82, 0x8a, 0x82, 0x82, 0x82,
+     0xa0, 0xa0, 0x40, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x10, 0x10,
+     0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x28, 0x28, 0x00, 0x00, 0x28, 0x28,
+     0x0a, 0x03, 0x01, 0x03, 0x0a, 0x03, 0x01, 0x03, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x04, 0xa8, 0xa0, 0xc0, 0xc0,
+     0x88, 0x80, 0x80, 0x80, 0xa0, 0xa0, 0xc0, 0xc0, 0x80, 0x80, 0x80, 0x80,
+     0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x28, 0x28,
+     0x00, 0x00, 0x28, 0x28, 0x0c, 0xff, 0xff, 0xff, 0x08, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x0a, 0x0a, 0x01, 0x01, 0x0a, 0x0a,
+     0x28, 0x30, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x30, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0xa0, 0xa0,
+     0x82, 0x82, 0xff, 0xff, 0x82, 0x82, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00,
+     0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x0a, 0x0a,
+     0x01, 0x01, 0x0a, 0x0a, 0x28, 0x30, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+     0x10, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0xa0, 0xa0,
+     0x40, 0x40, 0xa0, 0xa0, 0x82, 0x82, 0xff, 0xff, 0x82, 0x82, 0xff, 0xff,
+     0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x04, 0x2a, 0x22, 0x03, 0x02,
+     0x0a, 0x02, 0x03, 0x02, 0x30, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x28, 0x28,
+     0x00, 0x00, 0x28, 0x28, 0x0a, 0x02, 0x03, 0x02, 0x0a, 0x02, 0x03, 0x02,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04, 0x04,
+     0x28, 0x20, 0xff, 0xff, 0x08, 0xff, 0xff, 0xff, 0x30, 0x20, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10,
+     0x00, 0x00, 0x28, 0x28, 0x00, 0x00, 0x28, 0x28, 0x0c, 0xff, 0xff, 0xff,
+     0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, 0x0a, 0x0a,
+     0x01, 0x01, 0x0a, 0x0a, 0x28, 0x20, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+     0x30, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0xa0, 0xa0,
+     0x40, 0x40, 0xa0, 0xa0, 0x82, 0x82, 0xff, 0xff, 0x82, 0x82, 0xff, 0xff,
+     0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+     0x01, 0x01, 0x0a, 0x0a, 0x01, 0x01, 0x0a, 0x0a, 0x28, 0x20, 0x00, 0x00,
+     0x08, 0x00, 0x00, 0x00, 0x30, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+     0x40, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0xa0, 0xa0, 0x82, 0x82, 0xff, 0xff,
+     0x82, 0x82, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  };
 
-/* compass directions from west clockwise */
-int xd_pscale[] = { -1, -1,  0,  1, 1, 1, 0, -1 } ;
-int yd_pscale[] = {  0, -1, -1, -1, 0, 1, 1,  1 } ;
 
 /* starting positions for corners */
 #define NE(f) ((f) & 3)
@@ -26,180 +67,389 @@ int yd_pscale[] = {  0, -1, -1, -1, 0, 1, 1,  1 } ;
 #define SW(f) (((f) >> 4) & 3)
 #define NW(f) (((f) >> 6) & 3)
 
-typedef unsigned short sixteenbits ;
 
-/* list of corner patterns; bit 7 is current color, bits 0-6 are squares
- * around (excluding square behind), going clockwise.
- * The high byte of the patterns is a mask, which determines which bits are
- * not ignored.
- */
 
-sixteenbits patterns[] = { 0x0000, 0xd555,         /* no corner */
-                           0x0001, 0xffc1, 0xd514, /* normal corner */
-                           0x0002, 0xd554, 0xd515, /* reduced corners */
-                           0xbea2, 0xdfc0, 0xfd81,
-                           0xfd80, 0xdf80,
-                           0x0003, 0xbfa1, 0xfec2 /* reduced if > 1 */
-                           };
-
-/* search for corner patterns, return type of corner found:
- *  0 = no corner,
- *  1 = normal corner,
- *  2 = reduced corner,
- *  3 = reduced if cutoff > 1
- */
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    unsigned int scale;
+    const char * inputFileName;  /* File name of input file */
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+
+    OPTENTINIT;
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-int corner(pat)
-     int pat;
-{
-   register int i, r=0;
-   for (i = 0; i < sizeof(patterns)/sizeof(sixteenbits); i++)
-      if (patterns[i] < 0x100)
-         r = patterns[i];
-      else if ((pat & (patterns[i] >> 8)) ==
-               (patterns[i] & (patterns[i] >> 8)))
-         return r;
-   return 0;
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 < 1)
+        pm_error("You must specify the scale factor as an argument");
+    else {
+        int const scale = atoi(argv[1]);
+        if (scale < 1)
+            pm_error("Scale argument must be at least one.  You specified %d",
+                     scale);
+        else
+            cmdlineP->scale = scale;
+
+        if (argc-1 < 2)
+            cmdlineP->inputFileName = "-";
+        else {
+            cmdlineP->inputFileName = argv[2];
+
+            if (argc-1 > 2)
+                pm_error("Too many arguments.  The only arguments are the "
+                         "scale factor and optional input file name.  "
+                         "You specified %u", argc-1);
+        }
+    }
+    free(option_def);
 }
 
-/* get a new row
- */
 
-void nextrow_pscale(ifd, row)
-     FILE *ifd;
-     int row;
-{
-   bit *shuffle = inrow[0] ;
-   inrow[0] = inrow[1];
-   inrow[1] = inrow[2];
-   inrow[2] = shuffle ;
-   if (row < rows) {
-      if (shuffle == NULL)
-         inrow[2] = shuffle = pbm_allocrow(columns);
-      pbm_readpbmrow(ifd, inrow[2], columns, format) ;
-   } else inrow[2] = NULL; /* discard storage */
 
+static void
+validateComputableDimensions(unsigned int const width,
+                             unsigned int const height,
+                             unsigned int const scaleFactor) {
+/*----------------------------------------------------------------------------
+   Make sure that multiplication for output image width and height do not
+   overflow.
+   See validateComputetableSize() in libpam.c
+   and pbm_readpbminitrest() in libpbm2.c
+-----------------------------------------------------------------------------*/
+    unsigned int const maxWidthHeight = INT_MAX - 2;
+    unsigned int const maxScaleFactor = maxWidthHeight / MAX(height, width);
+
+    if (scaleFactor > maxScaleFactor)
+       pm_error("Scale factor '%u' too large.  "
+                "The maximum for this %u x %u input image is %u.",
+                scaleFactor, width, height, maxScaleFactor);
 }
 
 
 
-int
-main(int argc, char ** argv) {
+static void
+writeBitSpan(unsigned char * const packedBitrow,
+             int             const cols,
+             int             const offset,
+             int             const color) {
+/*----------------------------------------------------------------------------
+   Write white (color="0") or black (="1") bits into packedBitrow[],
+   starting at 'offset', length 'cols'.
+-----------------------------------------------------------------------------*/
+    unsigned char * const dest     = &packedBitrow[offset/8];
+    unsigned int    const rs       = offset % 8;
+    unsigned int    const trs      = (cols + rs) % 8;
+    unsigned int    const colBytes = pbm_packed_bytes(cols + rs);
+    unsigned int    const last     = colBytes - 1;
 
-    FILE * ifP;
-    bit * outrow;
-    unsigned int row;
-    int scale, cutoff, ucutoff ;
-    unsigned char *flags;
+    unsigned char const origHead = dest[0];
+    unsigned char const origEnd =  dest[last];
 
-    pbm_init( &argc, argv );
+    unsigned int i;
 
-    if (argc < 2)
-        pm_usage("scale [pbmfile]");
+    for (i = 0; i < colBytes; ++i)
+        dest[i] = color * 0xff;
 
-    scale = atoi(argv[1]);
-    if (scale < 1)
-        pm_error("Scale argument must be at least one.  You specified '%s'",
-                 argv[1]);
+    if (rs > 0)
+        dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs);
 
-    if (argc == 3)
-        ifP = pm_openr(argv[2]);
-    else
-        ifP = stdin ;
+    if (trs > 0)
+        dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs);
+}
 
-    inrow[0] = inrow[1] = inrow[2] = NULL;
-    pbm_readpbminit(ifP, &columns, &rows, &format) ;
 
-    outrow = pbm_allocrow(columns*scale) ;
-    MALLOCARRAY(flags, columns);
-    if (flags == NULL) 
-        pm_error("out of memory") ;
 
-    pbm_writepbminit(stdout, columns*scale, rows*scale, 0) ;
+static void
+setFlags(const bit *     const prevrow,
+         const bit *     const thisrow,
+         const bit *     const nextrow,
+         unsigned char * const flags,
+         unsigned int    const cols ) {
+/*----------------------------------------------------------------------------
+   Scan one row, examining the row above and row below, and determine 
+   whether there are "corners" for each pixel.  Feed a 9 bit sample into 
+   pre-calculated array transTable[512] to calculate all four corner statuses
+   at once.
 
-    cutoff = scale / 2;
-    ucutoff = scale - 1 - cutoff;
-    nextrow_pscale(ifP, 0);
-    for (row = 0; row < rows; ++row) {
-        unsigned int col;
-        unsigned int i;
-        nextrow_pscale(ifP, row+1);
-        for (col = 0; col < columns; ++col) {
-            unsigned int i;
-            flags[col] = 0 ;
-            for (i = 0; i != 8; i += 2) {
-                int vec = inrow[thisrow][col] != PBM_WHITE;
-                unsigned int k;
-                for (k = 0; k < 7; ++k) {
-                    int x = col + xd_pscale[(k+i)&7] ;
-                    int y = thisrow + yd_pscale[(k+i)&7] ;
-                    vec <<= 1;
-                    if (x >=0 && x < columns && inrow[y])
-                        vec |= (inrow[y][x] != PBM_WHITE) ;
-                }
-                flags[col] |= corner(vec)<<i ;
-            }
+   Bits in the 9 bit sample represent the current pixel and neighbors:
+       NW : N : NE : W: Current : E : SW : S : SE
+
+   Bits in flag are divided into 4 fields of width 2 each:
+       NW : SW : SE : NE
+
+   Code 0xff is an exception.  It is a variation of 0x00.
+     0x00 : no corners, no color change from above row (Current == N)
+     0xff : no corners, but color changed (Current != N)
+
+   Most transTable[] entries are "no corners".
+   0x00 appears 180 times, 0xff 109 times.
+-----------------------------------------------------------------------------*/
+
+#if 0
+    /* The following code is from the previous version, which examined
+       the corners one by one:
+    */
+
+    /* list of corner patterns; bit 7 is current color, bits 0-6 are squares
+       around (excluding square behind), going clockwise.
+       The high byte of the patterns is a mask, which determines which bits are
+       not ignored.
+    */
+    uint16_t const patterns[] 
+        = { 0x0000,   0xd555,            /* no corner */
+            0x0001,   0xffc1, 0xd514,    /* normal corner */
+            0x0002,   0xd554, 0xd515, 0xbea2, 0xdfc0, 0xfd81, 0xfd80, 0xdf80,
+            /* reduced corners */
+            0x0003,   0xbfa1, 0xfec2 };  /* reduced if cutoff > 1 */
+
+    /*
+      For example, the NE corner is examined with the following 8 bit sample:
+      Current : W : NW : N : NE : E : SE : S
+      (SW is the "square behind") 
+      */
+#endif
+
+    uint32_t prevrow24, thisrow24, nextrow24;
+    unsigned int col;
+
+    /* higher bits are set to 0 */
+    prevrow24 = prevrow[0];  /* initial value */
+    thisrow24 = thisrow[0];  /* initial value */
+    nextrow24 = nextrow[0];  /* initial value */
+
+    for (col = 0; col < cols; ++col) {
+        unsigned int const col8   = col / 8;
+        unsigned int const offset = col % 8;
+
+        unsigned int sample;
+
+        if (offset == 0) {
+            prevrow24 = prevrow24 << 8 | prevrow[col8 + 1];
+            thisrow24 = thisrow24 << 8 | thisrow[col8 + 1];
+            nextrow24 = nextrow24 << 8 | nextrow[col8 + 1];
         }
-        for (i = 0; i < scale; i++) {
-            bit *ptr = outrow ;
-            int zone = (i > ucutoff) - (i < cutoff) ;
-            int cut = (zone < 0) ? (cutoff - i) :
-                (zone > 0) ? (i - ucutoff) : 0 ;
-
-            for (col = 0; col < columns; ++col) {
-                int pix = inrow[thisrow][col] ;
-                int flag = flags[col] ;
-                int cutl, cutr;
-                unsigned int k;
 
+        sample = ( ( prevrow24 >> ( 8 -offset) ) & 0x01c0 )
+            | ( ( thisrow24 >> (11 -offset) ) & 0x0038 )
+            | ( ( nextrow24 >> (14 -offset) ) & 0x0007 );
+        
+        flags[col] =  transTable[sample];
+    }
+}
+
+
+
+static void
+expandRow(const bit *     const thisrow,
+          const bit *     const prevrow,
+          bit *           const outrow,
+          unsigned char * const flags,
+          unsigned int    const cols,
+          int             const scale,
+          int             const cutoff,
+          int             const ucutoff) {
+/*----------------------------------------------------------------------------
+  Process one row, using flags array as reference.  If pixel has no corners
+  output a NxN square of the given color, otherwise output with the 
+  specified corner area(s) clipped off.
+-----------------------------------------------------------------------------*/
+    unsigned int const outcols = cols * scale;
+
+    unsigned int i;
+    unsigned int col;
+    
+    for (i = 0; i < scale; ++i) {
+        int const zone = (i > ucutoff) - (i < cutoff);
+        int const cut1 =
+            (zone < 0) ? (cutoff - i) : (zone > 0) ? (i - ucutoff) : 0;
+
+        unsigned int outcol;
+        int cut[4];
+
+        outcol = 0; /* initial value */
+
+        cut[0] = 0;
+        cut[1] = cut1;
+        cut[2] = cut1 ? cut1 - 1 : 0;
+        cut[3] = (cut1 && cutoff > 1) ? cut1 - 1 : cut1;
+      
+        for (col = 0; col < cols; ++col) {
+            unsigned int const col8 = col / 8;
+            unsigned int const offset = col % 8;
+            int const pix = (thisrow[col8] >> (7-offset) ) & 0x01;
+            int const flag = flags[col];
+
+            int cutl, cutr;
+
+            if (flag == 0x00) {
+                /* There are no corners, no color change */
+                outcol += scale;
+            } else { 
                 switch (zone) {
                 case -1:
-                    switch (NW(flag)) {
-                    case 0: cutl = 0; break;
-                    case 1: cutl = cut; break;
-                    case 2: cutl = cut ? cut-1 : 0; break;
-                    case 3: cutl = (cut && cutoff > 1) ? cut-1 : cut; break;
-                    default: cutl = 0;  /* Should never reach here */
-                    }
-                    switch (NE(flag)) {
-                    case 0: cutr = 0; break;
-                    case 1: cutr = cut; break;
-                    case 2: cutr = cut ? cut-1 : 0; break;
-                    case 3: cutr = (cut && cutoff > 1) ? cut-1 : cut; break;
-                    default: cutr = 0;  /* Should never reach here */
+                    if (i==0 && flag == 0xff) {
+                        /* No corners, color changed */ 
+                        cutl = cutr = 0;
+                        flags[col] = 0x00;
+                            /* Use above skip procedure next cycle */
+                    } else {
+                        cutl = cut[NW(flag)];
+                        cutr = cut[NE(flag)];
                     }
                     break;
                 case 0:
                     cutl = cutr = 0;
                     break ;
                 case 1:
-                    switch (SW(flag)) {
-                    case 0: cutl = 0; break;
-                    case 1: cutl = cut; break;
-                    case 2: cutl = cut ? cut-1 : 0; break;
-                    case 3: cutl = (cut && cutoff > 1) ? cut-1 : cut; break;
-                    default: cutl = 0;  /* should never reach here */
-                    }
-                    switch (SE(flag)) {
-                    case 0: cutr = 0; break;
-                    case 1: cutr = cut; break;
-                    case 2: cutr = cut ? cut-1 : 0; break;
-                    case 3: cutr = (cut && cutoff > 1) ? cut-1 : cut; break;
-                    default: cutr = 0;  /* should never reach here */
-                    }
+                    cutl = cut[SW(flag)];
+                    cutr = cut[SE(flag)];
                     break;
-                default: cutl = 0; cutr = 0;  /* Should never reach here */
                 }
-                for (k = 0; k < cutl; ++k) /* left part */
-                    *ptr++ = !pix ;
-                for (k = 0; k < scale-cutl-cutr; ++k)  /* center part */
-                    *ptr++ = pix ;
-                for (k = 0; k < cutr; ++k) /* right part */
-                    *ptr++ = !pix ;
+                
+                if (cutl > 0) {
+                    writeBitSpan(outrow, cutl, outcol, !pix);
+                    outcol += cutl;
+                }
+                {
+                    unsigned int const center = scale - cutl - cutr;
+
+                    if (center > 0) {
+                        writeBitSpan(outrow, center, outcol,  pix);
+                        outcol += center;
+                    }
+                }
+                if (cutr > 0) {
+                    writeBitSpan(outrow, cutr, outcol, !pix);
+                    outcol += cutr;
+                }
             }
-            pbm_writepbmrow(stdout, outrow, scale*columns, 0) ;
         }
+        pbm_writepbmrow_packed(stdout, outrow, outcols, 0) ;
+    }
+}
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    bit ** buffer;
+    bit * prevrow;
+    bit * thisrow;
+    bit * nextrow;
+    bit * edgerow;
+    bit * outrow;
+    unsigned int row;
+    unsigned int i;
+    int cols, rows;
+    int format;
+    unsigned int outcols;
+    unsigned int outrows;
+    int cutoff;
+    int ucutoff ;
+    unsigned char * flags;  /* malloc'ed */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format) ;
+
+    validateComputableDimensions(cols, rows, cmdline.scale); 
+
+    outcols = cols * cmdline.scale;
+    outrows = rows * cmdline.scale; 
+
+    /* Initialize input buffers.
+       We add a margin of 8 bits on the right of the three rows.
+
+       On the top and bottom of the image we place an imaginary blank row
+       ("edgerow") to facilitate the process.
+    */
+
+    buffer  = pbm_allocarray_packed(cols + 8, 3);
+    edgerow = pbm_allocrow_packed(cols + 8);
+
+    for (i = 0; i < pbm_packed_bytes(cols + 8); ++i)
+        edgerow[i] = 0x00;
+
+    /* Add blank bytes at right edges */ 
+    for (i = 0; i < 3; ++i)
+        buffer[i][pbm_packed_bytes(cols + 8) - 1] = 0x00;
+
+    thisrow = edgerow;
+    nextrow = buffer[0];
+
+    /* Read the top line into nextrow and clean the right end. */
+
+    pbm_readpbmrow_packed(ifP, nextrow, cols, format);
+    pbm_cleanrowend_packed(nextrow, cols);
+
+    outrow = pbm_allocrow_packed(outcols);
+    for (i = 0; i < pbm_packed_bytes(outcols); ++i)
+        outrow[i] = 0x00;
+
+    MALLOCARRAY(flags, cols);
+    if (flags == NULL)
+        pm_error("Couldn't get memory for %u columns of flags", cols);
+
+    pbm_writepbminit(stdout, outcols, outrows, 0) ;
+
+    cutoff = cmdline.scale / 2;
+    ucutoff = cmdline.scale - 1 - cutoff;
+
+    for (row = 0; row < rows; ++row) {
+        prevrow = thisrow;  /* Slide up the input row window */
+        thisrow = nextrow;
+        if (row < rows - 1) {
+            nextrow = buffer[(row + 1) % 3];
+            /* We take the address directly instead of shuffling the rows.
+               This provision is for proper handling of the initial edgerow.
+            */
+            pbm_readpbmrow_packed(ifP, nextrow, cols, format);
+            pbm_cleanrowend_packed(nextrow, cols);
+        } else
+            /* Bottom of image.  */
+            nextrow = edgerow;
+
+        setFlags(prevrow, thisrow, nextrow, flags, cols);
+
+        expandRow(thisrow, prevrow, outrow, flags, cols, cmdline.scale,
+                  cutoff, ucutoff);
     }
+    pbm_freearray(buffer,3);
+    pbm_freerow(edgerow);
+    pbm_freerow(outrow);
+    free (flags);
     pm_close(ifP);
     return 0;
 }
diff --git a/editor/pgmdeshadow.c b/editor/pgmdeshadow.c
index 948c6cba..2c3a90f8 100644
--- a/editor/pgmdeshadow.c
+++ b/editor/pgmdeshadow.c
@@ -21,7 +21,7 @@
 */
 
 /*
- * Algorithm reference: Luc Vincent, "Morphological Grayscale Reruction
+ * Algorithm reference: Luc Vincent, "Morphological Grayscale Reconstruction
  * in Image Analysis: Applications and Efficient Algorithms," IEEE
  * Transactions on Image Processing, vol. 2, no. 2, April 1993, pp. 176-201.
  *
@@ -75,7 +75,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -91,7 +91,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 < 1)
diff --git a/editor/pgmmedian.c b/editor/pgmmedian.c
index f911475d..9d90b6b3 100644
--- a/editor/pgmmedian.c
+++ b/editor/pgmmedian.c
@@ -66,7 +66,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -91,7 +91,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!widthSpec)
diff --git a/editor/pnmcat.c b/editor/pnmcat.c
index 3cf443bb..a26dcf3e 100644
--- a/editor/pnmcat.c
+++ b/editor/pnmcat.c
@@ -16,6 +16,7 @@
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "bitarith.h"
+#include "nstring.h"
 #include "pnm.h"
 
 #define LEFTBITS pm_byteLeftBits
@@ -89,9 +90,11 @@ parseCommandLine(int argc, const char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
+    free(option_def);
+
     if (leftright + topbottom > 1)
         pm_error("You may specify only one of -topbottom (-tb) and "
                  "-leftright (-lr)");
@@ -153,12 +156,20 @@ parseCommandLine(int argc, const char ** const argv,
         cmdlineP->nfiles = 1;
     } else {
         unsigned int i;
+        unsigned int stdinCt;
+            /* Number of input files user specified as Standard Input */
 
         MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, argc-1);
 
-        for (i = 0; i < argc-1; ++i)
+        for (i = 0, stdinCt = 0; i < argc-1; ++i) {
             cmdlineP->inputFilespec[i] = argv[1+i];
+            if (streq(argv[1+i], "-"))
+                ++stdinCt;
+        }
         cmdlineP->nfiles = argc-1;
+        if (stdinCt > 1)
+            pm_error("At most one input image can come from Standard Input.  "
+                     "You specified %u", stdinCt);
     }
 }
 
@@ -418,6 +429,8 @@ concatenateLeftRightPbm(FILE *             const ofP,
 
     getPbmImageInfo(img, nfiles, newrows, justification, backcolor, &img2);
 
+    outrow[pbm_packed_bytes(newcols)-1] = 0x00;
+
     for (row = 0; row < newrows; ++row) {
         unsigned int i;
 
@@ -556,7 +569,7 @@ concatenateTopBottomPbm(FILE *             const ofP,
 
         backgroundPrev = background;
     }
-    free(outrow);
+    pbm_freerow_packed(outrow);
 }
 
 
@@ -851,6 +864,7 @@ main(int           argc,
     for (i = 0; i < cmdline.nfiles; ++i)
         pm_close(img[i].ifP);
     free(cmdline.inputFilespec);
+    free(img);
     pm_close(stdout);
 
     return 0;
diff --git a/editor/pnmcomp.c b/editor/pnmcomp.c
deleted file mode 100644
index 04bb5365..00000000
--- a/editor/pnmcomp.c
+++ /dev/null
@@ -1,460 +0,0 @@
-/* +-------------------------------------------------------------------+ */
-/* | Copyright 1992, David Koblas.                                     | */
-/* |   Permission to use, copy, modify, and distribute this software   | */
-/* |   and its documentation for any purpose and without fee is hereby | */
-/* |   granted, provided that the above copyright notice appear in all | */
-/* |   copies and that both that copyright notice and this permission  | */
-/* |   notice appear in supporting documentation.  This software is    | */
-/* |   provided "as is" without express or implied warranty.           | */
-/* +-------------------------------------------------------------------+ */
-
-/*
-
-    DON'T ADD NEW FUNCTION TO THIS PROGRAM.  ADD IT TO pamcomp.c INSTEAD.
-
-*/
-
-
-
-#define _BSD_SOURCE    /* Make sure strcasecmp() is in string.h */
-#include <string.h>
-
-#include "pm_c_util.h"
-#include "pnm.h"
-#include "shhopt.h"
-#include "mallocvar.h"
-
-enum horizPos {BEYONDLEFT, LEFT, CENTER, RIGHT, BEYONDRIGHT};
-enum vertPos {ABOVE, TOP, MIDDLE, BOTTOM, BELOW};
-
-
-struct cmdlineInfo {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    const char *underlyingFilespec;  /* '-' if stdin */
-    const char *overlayFilespec;
-    const char *alphaFilespec;
-    const char *outputFilespec;  /* '-' if stdout */
-    int xoff, yoff;   /* value of xoff, yoff options */
-    float opacity;
-    unsigned int alphaInvert;
-    enum horizPos align;
-    enum vertPos valign;
-};
-
-
-
-
-static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
-/*----------------------------------------------------------------------------
-   parse program command line described in Unix standard form by argc
-   and argv.  Return the information in the options as *cmdlineP.  
-
-   If command line is internally inconsistent (invalid options, etc.),
-   issue error message to stderr and abort program.
-
-   Note that the strings we return are stored in the storage that
-   was passed to us as the argv array.  We also trash *argv.
------------------------------------------------------------------------------*/
-    optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
-         */
-    optStruct3 opt;
-
-    unsigned int option_def_index;
-
-    char *align, *valign;
-    unsigned int xoffSpec, yoffSpec, alignSpec, valignSpec, opacitySpec,
-        alphaSpec;
-
-    MALLOCARRAY_NOFAIL(option_def, 100);
-
-    option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "invert",     OPT_FLAG,   NULL,                  
-            &cmdlineP->alphaInvert,       0 );
-    OPTENT3(0, "xoff",       OPT_INT,    &cmdlineP->xoff,       
-            &xoffSpec,       0 );
-    OPTENT3(0, "yoff",       OPT_INT,    &cmdlineP->yoff,       
-            &yoffSpec,       0 );
-    OPTENT3(0, "opacity",    OPT_FLOAT, &cmdlineP->opacity,
-            &opacitySpec,       0 );
-    OPTENT3(0, "alpha",      OPT_STRING, &cmdlineP->alphaFilespec,
-            &alphaSpec,  0 );
-    OPTENT3(0, "align",      OPT_STRING, &align,
-            &alignSpec,  0 );
-    OPTENT3(0, "valign",     OPT_STRING, &valign,
-            &valignSpec,  0 );
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
-
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
-        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
-
-
-    if (!xoffSpec)
-        cmdlineP->xoff = 0;
-    if (!yoffSpec)
-        cmdlineP->yoff = 0;
-    if (!alphaSpec)
-        cmdlineP->alphaFilespec = NULL;
-
-    if (alignSpec) {
-        if (strcasecmp(align, "BEYONDLEFT") == 0)
-            cmdlineP->align = BEYONDLEFT;
-        else if (strcasecmp(align, "LEFT") == 0)
-            cmdlineP->align = LEFT;
-        else if (strcasecmp(align, "CENTER") == 0)
-            cmdlineP->align = CENTER;
-        else if (strcasecmp(align, "RIGHT") == 0)
-            cmdlineP->align = RIGHT;
-        else if (strcasecmp(align, "BEYONDRIGHT") == 0)
-            cmdlineP->align = BEYONDRIGHT;
-        else
-            pm_error("Invalid value for align option: '%s'.  Only LEFT, "
-                     "RIGHT, CENTER, BEYONDLEFT, and BEYONDRIGHT are valid.", 
-                     align);
-    } else 
-        cmdlineP->align = LEFT;
-
-    if (valignSpec) {
-        if (strcasecmp(valign, "ABOVE") == 0)
-            cmdlineP->valign = ABOVE;
-        else if (strcasecmp(valign, "TOP") == 0)
-            cmdlineP->valign = TOP;
-        else if (strcasecmp(valign, "MIDDLE") == 0)
-            cmdlineP->valign = MIDDLE;
-        else if (strcasecmp(valign, "BOTTOM") == 0)
-            cmdlineP->valign = BOTTOM;
-        else if (strcasecmp(valign, "BELOW") == 0)
-            cmdlineP->valign = BELOW;
-        else
-            pm_error("Invalid value for valign option: '%s'.  Only TOP, "
-                     "BOTTOM, MIDDLE, ABOVE, and BELOW are valid.", 
-                     align);
-    } else 
-        cmdlineP->valign = TOP;
-
-    if (!opacitySpec) 
-        cmdlineP->opacity = 1.0;
-
-    if (argc-1 < 1)
-        pm_error("Need at least one argument: file specification of the "
-                 "overlay image.");
-
-    cmdlineP->overlayFilespec = argv[1];
-
-    if (argc-1 >= 2)
-        cmdlineP->underlyingFilespec = argv[2];
-    else
-        cmdlineP->underlyingFilespec = "-";
-
-    if (argc-1 >= 3)
-        cmdlineP->outputFilespec = argv[3];
-    else
-        cmdlineP->outputFilespec = "-";
-
-    if (argc-1 > 3)
-        pm_error("Too many arguments.  Only acceptable arguments are: "
-                 "overlay image, underlying image, output image");
-}
-
-
-
-
-static void
-warnOutOfFrame( int const originLeft,
-                int const originTop, 
-                int const overCols,
-                int const overRows,
-                int const underCols,
-                int const underRows ) {
-    if (originLeft >= underCols)
-        pm_message("WARNING: the overlay is entirely off the right edge "
-                   "of the underlying image.  "
-                   "It will not be visible in the result.  The horizontal "
-                   "overlay position you selected is %d, "
-                   "and the underlying image "
-                   "is only %d pixels wide.", originLeft, underCols );
-    else if (originLeft + overCols <= 0)
-        pm_message("WARNING: the overlay is entirely off the left edge "
-                   "of the underlying image.  "
-                   "It will not be visible in the result.  The horizontal "
-                   "overlay position you selected is %d and the overlay is "
-                   "only %d pixels wide.", originLeft, overCols);
-    else if (originTop >= underRows)
-        pm_message("WARNING: the overlay is entirely off the bottom edge "
-                   "of the underlying image.  "
-                   "It will not be visible in the result.  The vertical "
-                   "overlay position you selected is %d, "
-                   "and the underlying image "
-                   "is only %d pixels high.", originTop, underRows );
-    else if (originTop + overRows <= 0)
-        pm_message("WARNING: the overlay is entirely off the top edge "
-                   "of the underlying image.  "
-                   "It will not be visible in the result.  The vertical "
-                   "overlay position you selected is %d and the overlay is "
-                   "only %d pixels high.", originTop, overRows);
-}
-
-
-
-static void
-computeOverlayPosition(const int underCols, const int underRows,
-                       const int overCols, const int overRows,
-                       const struct cmdlineInfo cmdline, 
-                       int * const originLeftP,
-                       int * const originTopP) {
-/*----------------------------------------------------------------------------
-   Determine where to overlay the overlay image, based on the options the
-   user specified and the realities of the image dimensions.
-
-   The origin may be outside the underlying image (so e.g. *originLeftP may
-   be negative or > image width).  That means not all of the overlay image
-   actually gets used.  In fact, there may be no overlap at all.
------------------------------------------------------------------------------*/
-    int xalign, yalign;
-
-    switch (cmdline.align) {
-    case BEYONDLEFT:  xalign = -overCols;              break;
-    case LEFT:        xalign = 0;                      break;
-    case CENTER:      xalign = (underCols-overCols)/2; break;
-    case RIGHT:       xalign = underCols - overCols;   break;
-    case BEYONDRIGHT: xalign = underCols;              break;
-    }
-    switch (cmdline.valign) {
-    case ABOVE:       yalign = -overRows;              break;
-    case TOP:         yalign = 0;                      break;
-    case MIDDLE:      yalign = (underRows-overRows)/2; break;
-    case BOTTOM:      yalign = underRows - overRows;   break;
-    case BELOW:       yalign = underRows;              break;
-    }
-    *originLeftP = xalign + cmdline.xoff;
-    *originTopP  = yalign + cmdline.yoff;
-
-    warnOutOfFrame( *originLeftP, *originTopP, 
-                    overCols, overRows, underCols, underRows );    
-}
-
-
-
-static pixval
-composeComponents(pixval const compA, 
-                  pixval const compB,
-                  float  const distrib,
-                  pixval const maxval) {
-/*----------------------------------------------------------------------------
-  Compose a single component of each of two pixels, with 'distrib' being
-  the fraction of 'compA' in the result, 1-distrib the fraction of 'compB'.
-  
-  Both inputs are based on a maxval of 'maxval', and so is our result.
-  
-  Note that while 'distrib' in the straightforward case is always in
-  [0,1], it can in fact be negative or greater than 1.  We clip the
-  result as required to return a legal pixval.
------------------------------------------------------------------------------*/
-    return MIN(maxval, MAX(0, (int)compA * distrib +
-                              (int)compB * (1.0 - distrib) + 
-                              0.5
-                          )
-              );
-}
-
-
-
-static pixel
-composePixels(pixel  const pixelA,
-              pixel  const pixelB,
-              float  const distrib,
-              pixval const maxval) {
-/*----------------------------------------------------------------------------
-  Compose two pixels 'pixelA' and 'pixelB', with 'distrib' being the
-  fraction of 'pixelA' in the result, 1-distrib the fraction of 'pixelB'.
-
-  Both inputs are based on a maxval of 'maxval', and so is our result.
-  
-  Note that while 'distrib' in the straightforward case is always in
-  [0,1], it can in fact be negative or greater than 1.  We clip the
-  result as required to return a legal pixval.
------------------------------------------------------------------------------*/
-    pixel retval;
-
-    pixval const red = 
-        composeComponents(PPM_GETR(pixelA), PPM_GETR(pixelB), distrib, maxval);
-    pixval const grn =
-        composeComponents(PPM_GETG(pixelA), PPM_GETG(pixelB), distrib, maxval);
-    pixval const blu = 
-        composeComponents(PPM_GETB(pixelA), PPM_GETB(pixelB), distrib, maxval);
-
-    PPM_ASSIGN(retval, red, grn, blu);
-
-    return retval;
-}
-
-
-
-static void
-composite(int      const originleft, 
-          int      const origintop, 
-          pixel ** const overlayImage, 
-          int      const overlayCols, 
-          int      const overlayRows,
-          xelval   const overlayMaxval, 
-          int      const overlayType,
-          int      const cols, 
-          int      const rows, 
-          xelval   const maxval, 
-          int      const type,
-          gray **  const alpha, 
-          gray     const alphaMax, 
-          bool     const invertAlpha,
-          float    const opacity,
-          FILE *   const ifp, 
-          FILE *   const ofp) {
-/*----------------------------------------------------------------------------
-   Overlay the overlay image 'overlayImage' onto the underlying image
-   which is in file 'ifp', and output the composite to file 'ofp'.
-
-   The underlying image file 'ifp' is positioned after its header.  The
-   width, height, format, and maxval of the underlying image are 'cols',
-   'rows', 'type', and 'maxval'.
-
-   The width, height, format, and maxval of the overlay image are
-   overlayCols, overlayRows, overlayType and overlayMaxval.
-
-   'originleft' and 'origintop' are the coordinates in the underlying
-   image plane where the top left corner of the overlay image is
-   to go.  It is not necessarily inside the underlying image (in fact,
-   may be negative).  Only the part of the overlay that actually intersects
-   the underlying image, if any, gets into the output.
-
-   Note that we modify the overlay image 'overlayImage' to change its
-   format and maxval to the format and maxval of the output.
------------------------------------------------------------------------------*/
-    /* otype and oxmaxv are the type and maxval for the composed (output)
-       image, and are derived from that of the underlying and overlay
-       images.
-    */
-    int    const otype = (overlayType < type) ? type : overlayType;
-    xelval const omaxv = pm_lcm(maxval, overlayMaxval, 1, PNM_OVERALLMAXVAL);
-
-    int     row;
-    xel     *pixelrow;
-
-    pixelrow = pnm_allocrow(cols);
-
-    if (overlayType != otype || overlayMaxval != omaxv) {
-        pnm_promoteformat(overlayImage, overlayCols, overlayRows,
-                          overlayMaxval, overlayType, omaxv, otype);
-    }
-
-    pnm_writepnminit(ofp, cols, rows, omaxv, otype, 0);
-
-    for (row = 0; row < rows; ++row) {
-        int col;
-
-        /* Read a row and convert it to the output type */
-        pnm_readpnmrow(ifp, pixelrow, cols, maxval, type);
-
-        if (type != otype || maxval != omaxv)
-            pnm_promoteformatrow(pixelrow, cols, maxval, type, omaxv, otype);
-
-        /* Now overlay the overlay with alpha (if defined) */
-        for (col = 0; col < cols; ++col) {
-            int const ovlcol = col - originleft;
-            int const ovlrow = row - origintop;
-
-            double overlayWeight;
-
-            if (ovlcol >= 0 && ovlcol < overlayCols &&
-                ovlrow >= 0 && ovlrow < overlayRows) {
-
-                if (alpha == NULL) {
-                    overlayWeight = opacity;
-                } else {
-                    double alphaval;
-                    alphaval = 
-                        (double)alpha[ovlrow][ovlcol] / (double)alphaMax;
-                    if (invertAlpha)
-                        alphaval = 1.0 - alphaval;
-                    overlayWeight = alphaval * opacity;
-                }
-
-                pixelrow[col] = composePixels(overlayImage[ovlrow][ovlcol],
-                                              pixelrow[col], 
-                                              overlayWeight, omaxv);
-            }
-        }
-        pnm_writepnmrow(ofp, pixelrow, cols, omaxv, otype, 0);
-    }
-    pnm_freerow(pixelrow);
-}
-
-
-
-int
-main(int argc, char *argv[]) {
-
-    FILE    *ifp, *ofp;
-    pixel   **image;
-    int     imageCols, imageRows, imageType;
-    xelval  imageMax;
-    int     cols, rows, type;
-    xelval  maxval;
-    gray    **alpha;
-    int     alphaCols, alphaRows;
-    xelval  alphaMax;
-    struct cmdlineInfo cmdline;
-    int originLeft, originTop;
-
-    pnm_init(&argc, argv);
-
-    parseCommandLine(argc, argv, &cmdline);
-        
-    { /* Read the overlay image into 'image' */
-        FILE *fp;
-        fp = pm_openr(cmdline.overlayFilespec);
-        image = 
-            pnm_readpnm(fp, &imageCols, &imageRows, &imageMax, &imageType);
-        pm_close(fp);
-    }
-    if (cmdline.alphaFilespec) {
-        /* Read the alpha mask file into 'alpha' */
-        FILE *fp = pm_openr(cmdline.alphaFilespec);
-        alpha = pgm_readpgm(fp, &alphaCols, &alphaRows, &alphaMax);
-        pm_close(fp);
-            
-        if (imageCols != alphaCols || imageRows != alphaRows)
-            pm_error("Alpha map and overlay image are not the same size");
-    } else
-        alpha = NULL;
-
-    ifp = pm_openr(cmdline.underlyingFilespec);
-
-    ofp = pm_openw(cmdline.outputFilespec);
-
-    pnm_readpnminit(ifp, &cols, &rows, &maxval, &type);
-
-    computeOverlayPosition(cols, rows, imageCols, imageRows, 
-                           cmdline, &originLeft, &originTop);
-
-    composite(originLeft, originTop,
-              image, imageCols, imageRows, imageMax, imageType, 
-              cols, rows, maxval, type, 
-              alpha, alphaMax, cmdline.alphaInvert, cmdline.opacity,
-              ifp, ofp);
-
-    pm_close(ifp);
-    pm_close(ofp);
-
-    /* If the program failed, it previously aborted with nonzero completion
-       code, via various function calls.
-    */
-    return 0;
-}
-
-
diff --git a/editor/pnmconvol.c b/editor/pnmconvol.c
index 2033a1a3..8d9bb83a 100644
--- a/editor/pnmconvol.c
+++ b/editor/pnmconvol.c
@@ -1,6 +1,4 @@
-/* pnmconvol.c - general MxN convolution on a PNM image
-**
-** Version 2.0.1 January 30, 1995
+/* pnmconvol.c - general MxN convolution on a Netpbm image
 **
 ** Major rewriting by Mike Burns
 ** Copyright (C) 1994, 1995 by Mike Burns (burns@chem.psu.edu)
@@ -17,22 +15,297 @@
 
 /* A change history is at the bottom */
 
+#include <stdlib.h>
 #include <assert.h>
 
 #include "pm_c_util.h"
-#include "pnm.h"
-#include "shhopt.h"
 #include "mallocvar.h"
+#include "nstring.h"
+#include "token.h"
+#include "io.h"
+#include "shhopt.h"
+#include "pam.h"
+
+
+
+static sample const
+clipSample(sample const unclipped,
+           sample const maxval) {
+
+    return MIN(maxval, unclipped);
+}
+
+
+
+static sample const
+makeSample(float  const arg,
+           sample const maxval) {
+/*----------------------------------------------------------------------------
+   From a tentative sample value that could be fractional or negative,
+   produce an actual sample value by rounding and clipping.
+-----------------------------------------------------------------------------*/
+    return MIN(maxval, ROUNDU(MAX(0.0, arg)));
+}
+
+
+
+static void
+validateKernelDimensions(unsigned int const width,
+                         unsigned int const height) {
+
+    if (height == 0)
+        pm_error("Convolution matrix height is zero");
+    if (width == 0)
+        pm_error("Convolution matrix width is zero");
+
+    if (height % 2 != 1)
+        pm_error("The convolution matrix must have an odd number of rows.  "
+                 "Yours has %u", height);
+
+    if (width % 2 != 1)
+        pm_error("The convolution matrix must have an odd number of columns.  "
+                 "Yours has %u", width);
+}
+
+
+
+struct matrixOpt {
+    unsigned int width;
+    unsigned int height;
+    float ** weight;
+};
+
+
+
 
 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 *kernelFilespec;
+    const char * inputFileName;  /* '-' if stdin */
+    const char * pnmMatrixFileName;
     unsigned int nooffset;
+    const char ** matrixfile;
+    unsigned int matrixSpec;
+    struct matrixOpt matrix;
+    unsigned int normalize;
+    unsigned int bias;
 };
 
+
+
+static void
+countMatrixOptColumns(const char *   const rowString,
+                      unsigned int * const colCtP) {
+
+    const char * cursor;
+    unsigned int colCt;
+
+    for (cursor = &rowString[0], colCt = 0; *cursor; ) {
+        const char * colString;
+        const char * next;
+        const char * error;
+
+        pm_gettoken(cursor, ',', &colString, &next, &error);
+
+        if (error) {
+            pm_error("Unable to parse -matrix value row '%s'.  %s",
+                     rowString, error);
+            pm_strfree(error);
+        } else {
+            ++colCt;
+
+            cursor = next;
+            if (*cursor) {
+                assert(*cursor == ',');
+                ++cursor;  /* advance over comma to next column */
+            }
+            pm_strfree(colString);
+        }
+    }
+    *colCtP = colCt;
+}
+
+
+
+static void
+getMatrixOptDimensions(const char *   const matrixOptString,
+                       unsigned int * const widthP,
+                       unsigned int * const heightP) {
+/*----------------------------------------------------------------------------
+   Given the value of a -matrix option, 'matrixOptString', return the
+   height and width of the matrix it describes.
+
+   If it's not valid enough to determine that (e.g. it has rows of different
+   widths), abort.
+
+   An example of 'matrixOptString':
+
+     ".04,.15,.04;.15,.24,.15;.04,.15,.04"
+-----------------------------------------------------------------------------*/
+    unsigned int rowCt;
+    const char * cursor;
+
+    for (cursor = &matrixOptString[0], rowCt = 0; *cursor; ) {
+        const char * rowString;
+        const char * next;
+        const char * error;
+
+        pm_gettoken(cursor, ';', &rowString, &next, &error);
+
+        if (error) {
+            pm_error("Unable to parse -matrix value '%s'.  %s",
+                     matrixOptString, error);
+            pm_strfree(error);
+        } else {
+            unsigned int colCt;
+            ++rowCt;
+
+            countMatrixOptColumns(rowString, &colCt);
+
+            if (rowCt == 1)
+                *widthP = colCt;
+            else {
+                if (colCt != *widthP)
+                pm_error("-matrix option value contains rows of different "
+                         "widths: %u and %u", *widthP, colCt);
+            }            
+            pm_strfree(rowString);
+            cursor = next;
+
+            if (*cursor) {
+                assert(*cursor == ';');
+                ++cursor;  /* advance cursor over semicolon to next row */
+            }
+        }
+    }
+    *heightP = rowCt;
+}
+
+
+
+static void
+parseMatrixRow(const char * const matrixOptRowString,
+               unsigned int const width,
+               float *      const weight) {
+
+    unsigned int col;
+    const char * cursor;
+
+    for (col = 0, cursor = &matrixOptRowString[0]; col < width; ++col) {
+        const char * colString;
+        const char * next;
+        const char * error;
+
+        pm_gettoken(cursor, ',', &colString, &next, &error);
+
+        if (error) {
+            pm_error("Failed parsing a row in the -matrix value.  %s", error);
+            pm_strfree(error);
+        } else {
+            if (colString[0] == '\0')
+                pm_error("The Column %u element of the row '%s' in the "
+                         "-matrix value is a null string", col,
+                         matrixOptRowString);
+            else {
+                char * trailingJunk;
+                weight[col] = strtod(colString, &trailingJunk);
+
+                if (*trailingJunk != '\0') 
+                    pm_error("The Column %u element of the row '%s' in the "
+                             "-matrix value is not a valid floating point "
+                             "number", col, matrixOptRowString);
+            }
+            pm_strfree(colString);
+
+            cursor = next;
+
+            if (*cursor) {
+                assert(*cursor == ',');
+                ++cursor;  /* advance over comma to next column */
+            }
+        }
+    }
+}
+
+
+
+static void
+parseMatrixOptWithDimensions(const char * const matrixOptString,
+                             unsigned int const width,
+                             unsigned int const height,
+                             float **     const weight) {
+    
+    unsigned int row;
+    const char * cursor;
+
+    for (row = 0, cursor = &matrixOptString[0]; row < height; ++row) {
+        const char * rowString;
+        const char * next;
+        const char * error;
+
+        pm_gettoken(cursor, ';', &rowString, &next, &error);
+
+        if (error) {
+            pm_error("Failed parsing -matrix value.  %s", error);
+            pm_strfree(error);
+        } else {
+            parseMatrixRow(rowString, width, weight[row]);
+
+            pm_strfree(rowString);
+
+            cursor = next;
+
+            if (*cursor) {
+                assert(*cursor == ';');
+                ++cursor;  /* advance over semicolon to next row */
+            }
+        }
+    }
+}    
+
+
+
+static void
+parseMatrixOpt(const char *         const matrixOptString,
+               struct matrixOpt *   const matrixOptP) {
+/*----------------------------------------------------------------------------
+   An example of 'matrixOptString':
+
+     ".04,.15,.04;.15,.24,.15;.04,.15,.04"
+
+-----------------------------------------------------------------------------*/
+    unsigned int width, height;
+
+    getMatrixOptDimensions(matrixOptString, &width, &height);
+
+    validateKernelDimensions(width, height);
+
+    matrixOptP->height = height;
+    matrixOptP->width  = width;
+
+    {
+        unsigned int row;
+        MALLOCARRAY_NOFAIL(matrixOptP->weight, height);
+        for (row = 0; row < height; ++row)
+            MALLOCARRAY_NOFAIL(matrixOptP->weight[row], width);
+    }
+    parseMatrixOptWithDimensions(matrixOptString, width, height,
+                                 matrixOptP->weight);
+}
+
+
+
+static void
+validateMatrixfileOpt(const char ** const matrixFileOpt) {
+
+    if (matrixFileOpt[0] == NULL)
+        pm_error("You specified an empty string as the value of "
+                 "-matrixfile.  You must specify at least one file name");
+}
+
+
+
 static void
 parseCommandLine(int argc, char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
@@ -47,1855 +320,1929 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
     unsigned int option_def_index;
+    unsigned int matrixfileSpec;
+    const char * matrixOpt;
+    unsigned int biasSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "matrix",       OPT_STRING, &matrixOpt,
+            &cmdlineP->matrixSpec,     0)
+    OPTENT3(0, "matrixfile",   OPT_STRINGLIST, &cmdlineP->matrixfile,
+            &matrixfileSpec,           0)
     OPTENT3(0, "nooffset",     OPT_FLAG,   NULL,                  
-            &cmdlineP->nooffset,       0 );
+            &cmdlineP->nooffset,       0);
+    OPTENT3(0, "normalize",    OPT_FLAG,   NULL,                  
+            &cmdlineP->normalize,      0);
+    OPTENT3(0, "bias",         OPT_UINT,   &cmdlineP->bias,
+            &biasSpec,                 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (argc-1 < 1)
-        pm_error("Need at least one argument: file specification of the "
-                 "convolution kernel image.");
+    if (!biasSpec)
+        cmdlineP->bias = 0;
 
-    cmdlineP->kernelFilespec = argv[1];
+    if (matrixfileSpec && cmdlineP->matrixSpec)
+        pm_error("You can't specify by -matrix and -matrixfile");
 
-    if (argc-1 >= 2)
-        cmdlineP->inputFilespec = argv[2];
+    if (cmdlineP->matrixSpec)
+        parseMatrixOpt(matrixOpt, &cmdlineP->matrix);
+
+    if (matrixfileSpec)
+        validateMatrixfileOpt(cmdlineP->matrixfile);
     else
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->matrixfile = NULL;
+
+    if (matrixfileSpec || cmdlineP->matrixSpec) {
+        if (cmdlineP->nooffset)
+            pm_error("-nooffset is meaningless and not allowed with "
+                     "-matrix or -matrixfile");
+
+        cmdlineP->pnmMatrixFileName = NULL;
 
-    if (argc-1 > 2)
-        pm_error("Too many arguments.  Only acceptable arguments are: "
-                 "convolution file name and input file name");
+        if (argc-1 >= 1)
+            cmdlineP->inputFileName = argv[1];
+        else
+            cmdlineP->inputFileName = "-";
+
+        if (argc-1 > 1)
+            pm_error("Too many arguments.  When you specify -matrix "
+                     "or -matrixfile, the only allowable non-option "
+                     "argument is the input file name");
+    } else {
+        /* It's an old style invocation we accept for backward compatibility */
+        
+        if (argc-1 < 1)
+            pm_error("You must specify either -matrix or -matrixfile "
+                     "at least one argument which names an old-style PGM "
+                     "convolution matrix file.");
+        else {
+            cmdlineP->pnmMatrixFileName = argv[1];
+
+            if (argc-1 >= 2)
+                cmdlineP->inputFileName = argv[2];
+            else
+                cmdlineP->inputFileName = "-";
+            
+            if (argc-1 > 2)
+                pm_error("Too many arguments.  Only acceptable arguments are: "
+                         "convolution matrix file name and input file name");
+        }
+    }
 }
 
 
-/* Macros to verify that r,g,b values are within proper range */
 
-#define CHECK_GRAY \
-    if (tempgsum < 0L) g = 0; \
-    else if (tempgsum > maxval) g = maxval; \
-    else g = tempgsum;
+struct ConvKernel {
+    unsigned int cols;
+        /* Width of the convolution window */
+    unsigned int rows;
+        /* Height of the convolution window */
+    unsigned int planes;
+        /* Depth of the kernel -- this had better be the same as the
+           depth of the image being convolved.
+        */
+    float ** weight[3];
+        /* weight[PLANE][ROW][COL] is the weight to give to Plane PLANE
+           of the pixel at row ROW, column COL within the convolution window.
+           
+           One means full weight.
+
+           It can have magnitude greater than or less than one.  It can be
+           positive or negative.  
+        */
+    unsigned int bias;
+        /* The amount to be added to the linear combination of sample values.
+           We take a little liberty with the term "convolution kernel" to
+           include this value, since convolution per se does not involve any
+           such biasing.
+        */
+};
 
-#define CHECK_RED \
-    if (temprsum < 0L) r = 0; \
-    else if (temprsum > maxval) r = maxval; \
-    else r = temprsum;
 
-#define CHECK_GREEN \
-    if (tempgsum < 0L) g = 0; \
-    else if (tempgsum > maxval) g = maxval; \
-    else g = tempgsum;
 
-#define CHECK_BLUE \
-    if (tempbsum < 0L) b = 0; \
-    else if (tempbsum > maxval) b = maxval; \
-    else b = tempbsum;
+static void
+warnBadKernel(struct ConvKernel * const convKernelP) {
 
-struct convolveType {
-    void (*ppmConvolver)(const float ** const rweights,
-                         const float ** const gweights,
-                         const float ** const bweights);
-    void (*pgmConvolver)(const float ** const weights);
-};
+    float sum[3];
+    unsigned int plane;
+    unsigned int row;
 
-static FILE * ifp;
-static int crows, ccols, ccolso2, crowso2;
-static int cols, rows;
-static xelval maxval;
-static int format, newformat;
+    for (plane = 0; plane < convKernelP->planes; ++plane)
+        sum[plane] = 0.0; /* initial value */
+    
+    for (row = 0; row < convKernelP->rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < convKernelP->cols; ++col) {
+            unsigned int plane;
+            for (plane = 0; plane < convKernelP->planes; ++plane)
+                sum[plane] += convKernelP->weight[plane][row][col];
+        }
+    }
+
+    if (convKernelP->planes == 3) {
+        unsigned int plane;
+        bool biased, negative;
+        for (plane = 0, biased = false, negative = false;
+             plane < convKernelP->planes;
+             ++plane) {
+            if (sum[plane] < 0.9 || sum[plane] > 1.1)
+                biased = true;
+            if (sum[plane] < 0.0)
+                negative = true;
+        }
+    
+        if (biased) {
+            pm_message("WARNING - this convolution matrix is biased.  " 
+                       "red, green, and blue average weights: %f, %f, %f "
+                       "(unbiased would be 1).",
+                       sum[PAM_RED_PLANE],
+                       sum[PAM_GRN_PLANE],
+                       sum[PAM_BLU_PLANE]);
+
+            if (negative)
+                pm_message("Maybe you want the -nooffset option?");
+        }
+    } else if (convKernelP->planes == 1) {
+        if (sum[0] < 0.9 || sum[0] > 1.1)
+            pm_message("WARNING - this convolution matrix is biased.  "
+                       "average weight = %f (unbiased would be 1)",
+                       sum[0]);
+        if (sum[0] < 0.0)
+            pm_message("Maybe you want the -nooffset option?");
+    }
+}
 
 
 
 static void
-computeWeights(xel * const *   const cxels, 
-               int             const ccols, 
-               int             const crows,
-               int             const cformat, 
-               xelval          const cmaxval,
-               bool            const offsetPgm,
-               float ***       const rweightsP,
-               float ***       const gweightsP,
-               float ***       const bweightsP) {
+convKernelCreatePnm(struct pam *         const cpamP,
+                    tuple * const *      const ctuples, 
+                    unsigned int         const depth,
+                    bool                 const offsetPnm,
+                    struct ConvKernel ** const convKernelPP) {
 /*----------------------------------------------------------------------------
-   Compute the convolution matrix in normalized form from the PGM
-   form.  Each element of the output matrix is the actual weight we give an
-   input pixel -- i.e. the thing by which we multiple a value from the
-   input image.
-
-   'offsetPgm' means the PGM convolution matrix is defined in offset form so
+   Compute the convolution matrix in normalized form from the PGM form
+   'ctuples'/'cpamP'.  Each element of the output matrix is the actual weight
+   we give an input pixel -- i.e. the thing by which we multiple a value from
+   the input image.
+
+   'depth' is the required number of planes in the kernel.  If 'ctuples' has
+   fewer planes than that, we duplicate as necessary.  E.g. if 'ctuples' is
+   from a PGM input file and we're convolving a PPM image, we'll make a
+   3-plane convolution kernel by repeating the one plane in 'ctuples'.  If
+   'ctuples' has more planes than specified, we ignore the higher numbered
+   ones.
+
+   'offsetPnm' means the PNM convolution matrix is defined in offset form so
    that it can represent negative values.  E.g. with maxval 100, 50 means
    0, 100 means 50, and 0 means -50.  If 'offsetPgm' is false, 0 means 0
    and there are no negative weights.
 -----------------------------------------------------------------------------*/
-    double const scale = (offsetPgm ? 2.0 : 1.0) / cmaxval;
-    double const offset = offsetPgm ? - 1.0 : 0.0;
+    double const scale = (offsetPnm ? 2.0 : 1.0) / cpamP->maxval;
+    double const offset = offsetPnm ? - 1.0 : 0.0;
+    unsigned int const planes = MIN(3, depth);
 
-    float** rweights;
-    float** gweights;
-    float** bweights;
+    struct ConvKernel * convKernelP;
+    unsigned int plane;
 
-    float rsum, gsum, bsum;
+    MALLOCVAR_NOFAIL(convKernelP);
 
-    unsigned int crow;
+    convKernelP->cols   = cpamP->width;
+    convKernelP->rows   = cpamP->height;
+    convKernelP->planes = planes;
 
-    /* Set up the normalized weights. */
-    rweights = (float**) pm_allocarray(ccols, crows, sizeof(float));
-    gweights = (float**) pm_allocarray(ccols, crows, sizeof(float));
-    bweights = (float**) pm_allocarray(ccols, crows, sizeof(float));
+    for (plane = 0; plane < planes; ++plane) {
+        unsigned int row;
 
-    rsum = gsum = bsum = 0.0;  /* initial value */
+        MALLOCARRAY_NOFAIL(convKernelP->weight[plane], cpamP->height);
     
-    for (crow = 0; crow < crows; ++crow) {
-        unsigned int ccol;
-        for (ccol = 0; ccol < ccols; ++ccol) {
-            switch (PNM_FORMAT_TYPE(cformat)) {
-            case PPM_TYPE:
-                rsum += rweights[crow][ccol] =
-                    (PPM_GETR(cxels[crow][ccol]) * scale + offset);
-                gsum += gweights[crow][ccol] =
-                    (PPM_GETG(cxels[crow][ccol]) * scale + offset);
-                bsum += bweights[crow][ccol] =
-                    (PPM_GETB(cxels[crow][ccol]) * scale + offset);
-                break;
-                
-            default:
-                gsum += gweights[crow][ccol] =
-                    (PNM_GET1(cxels[crow][ccol]) * scale + offset);
-                break;
+        for (row = 0; row < cpamP->height; ++row) {
+            unsigned int col;
+
+            MALLOCARRAY_NOFAIL(convKernelP->weight[plane][row], cpamP->width);
+
+            for (col = 0; col < cpamP->width; ++col) {
+                sample const inValue = plane < cpamP->depth ?
+                    ctuples[row][col][plane] : ctuples[row][col][0];
+
+                convKernelP->weight[plane][row][col] =
+                    inValue * scale + offset;
             }
         }
     }
-    *rweightsP = rweights;
-    *gweightsP = gweights;
-    *bweightsP = bweights;
-
-    switch (PNM_FORMAT_TYPE(format)) {
-    case PPM_TYPE:
-        if (rsum < 0.9 || rsum > 1.1 || gsum < 0.9 || gsum > 1.1 ||
-            bsum < 0.9 || bsum > 1.1) {
-            pm_message("WARNING - this convolution matrix is biased.  " 
-                       "red, green, and blue average weights: %f, %f, %f "
-                       "(unbiased would be 1).",
-                       rsum, gsum, bsum);
+    convKernelP->bias = 0;
 
-            if (rsum < 0 && gsum < 0 && bsum < 0)
-                pm_message("Maybe you want the -nooffset option?");
-        }
-        break;
+    *convKernelPP = convKernelP;
+}
 
-    default:
-        if (gsum < 0.9 || gsum > 1.1)
-            pm_message("WARNING - this convolution matrix is biased.  "
-                       "average weight = %f (unbiased would be 1)",
-                       gsum);
-        break;
+
+
+static void
+convKernelDestroy(struct ConvKernel * const convKernelP) {
+
+    unsigned int plane;
+
+    for (plane = 0; plane < convKernelP->planes; ++plane) {
+        unsigned int row;
+
+        for (row = 0; row < convKernelP->rows; ++row)
+            free(convKernelP->weight[plane][row]);
+
+        free(convKernelP->weight[plane]);
     }
+    free(convKernelP);
 }
 
 
 
-/* General PGM Convolution
-**
-** No useful redundancy in convolution matrix.
-*/
-
 static void
-pgm_general_convolve(const float ** const weights) {
-    xel** xelbuf;
-    xel* outputrow;
-    xelval g;
-    int row;
-    xel **rowptr, *temprptr;
-    int toprow, temprow;
-    int i, irow;
-    long tempgsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-       a row output buffer.
-    */
-    xelbuf = pnm_allocarray(cols, crows);
-    outputrow = pnm_allocrow(cols);
-
-    /* Allocate array of pointers to xelbuf */
-    rowptr = (xel **) pnm_allocarray(1, crows);
-
-    pnm_writepnminit(stdout, cols, rows, maxval, newformat, 0);
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for (row = 0; row < crows - 1; ++row) {
-        pnm_readpnmrow(ifp, xelbuf[row], cols, maxval, format);
-        if (PNM_FORMAT_TYPE(format) != newformat)
-            pnm_promoteformatrow(xelbuf[row], cols, maxval, format, 
-                                 maxval, newformat);
-        /* Write out just the part we're not going to convolve. */
-        if (row < crowso2)
-            pnm_writepnmrow(stdout, xelbuf[row], cols, maxval, newformat, 0);
+normalizeKernelPlane(struct ConvKernel * const convKernelP,
+                     unsigned int        const plane) {
+
+    unsigned int row;
+    float sum;
+
+    for (row = 0, sum = 0.0; row < convKernelP->rows; ++row) {
+        unsigned int col;
+            
+        for (col = 0; col < convKernelP->cols; ++col) {
+                
+            sum += convKernelP->weight[plane][row][col];
+        }
     }
 
-    /* Now the rest of the image - read in the row at the end of
-       xelbuf, and convolve and write out the row in the middle.
-    */
-    for (; row < rows; ++row) {
-        int col;
-        toprow = row + 1;
-        temprow = row % crows;
-        pnm_readpnmrow(ifp, xelbuf[temprow], cols, maxval, format);
-        if (PNM_FORMAT_TYPE(format) != newformat)
-            pnm_promoteformatrow(xelbuf[temprow], cols, maxval, format, 
-                                 maxval, newformat);
-
-        /* Arrange rowptr to eliminate the use of mod function to determine
-           which row of xelbuf is 0...crows.  Mod function can be very costly.
-        */
-        temprow = toprow % crows;
-        i = 0;
-        for (irow = temprow; irow < crows; ++i, ++irow)
-            rowptr[i] = xelbuf[irow];
-        for (irow = 0; irow < temprow; ++irow, ++i)
-            rowptr[i] = xelbuf[irow];
-
-        for (col = 0; col < cols; ++col) {
-            if (col < ccolso2 || col >= cols - ccolso2)
-                outputrow[col] = rowptr[crowso2][col];
-            else {
-                int const leftcol = col - ccolso2;
-                int crow;
-                float gsum;
-                gsum = 0.0;
-                for (crow = 0; crow < crows; ++crow) {
-                    int ccol;
-                    temprptr = rowptr[crow] + leftcol;
-                    for (ccol = 0; ccol < ccols; ++ccol)
-                        gsum += PNM_GET1(*(temprptr + ccol))
-                            * weights[crow][ccol];
-                }
-                tempgsum = gsum + 0.5;
-            CHECK_GRAY;
-            PNM_ASSIGN1( outputrow[col], g );
-            }
+    {
+        float const scaler = 1.0/sum;
+
+        unsigned int row;
+
+        for (row = 0; row < convKernelP->rows; ++row) {
+            unsigned int col;
+                
+            for (col = 0; col < convKernelP->cols; ++col)
+                convKernelP->weight[plane][row][col] *= scaler;
         }
-        pnm_writepnmrow(stdout, outputrow, cols, maxval, newformat, 0);
     }
+}
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for (irow = crowso2 + 1; irow < crows; ++irow)
-        pnm_writepnmrow(stdout, rowptr[irow], cols, maxval, newformat, 0 );
+
+
+static void
+normalizeKernel(struct ConvKernel * const convKernelP) {
+/*----------------------------------------------------------------------------
+   Modify *convKernelP by scaling every weight in a plane by the same factor
+   such that the weights in the plane all add up to 1.
+-----------------------------------------------------------------------------*/
+    unsigned int plane;
+
+    for (plane = 0; plane < convKernelP->planes; ++plane)
+        normalizeKernelPlane(convKernelP, plane);
 }
 
 
 
-/* PGM Mean Convolution
-**
-** This is the common case where you just want the target pixel replaced with
-** the average value of its neighbors.  This can work much faster than the
-** general case because you can reduce the number of floating point operations
-** that are required since all the weights are the same.  You will only need
-** to multiply by the weight once, not for every pixel in the convolution
-** matrix.
-**
-** This algorithm works by creating sums for each column of crows height for
-** the whole width of the image.  Then add ccols column sums together to obtain
-** the total sum of the neighbors and multiply that sum by the weight.  As you
-** move right to left to calculate the next pixel, take the total sum you just
-** generated, add in the value of the next column and subtract the value of the
-** leftmost column.  Multiply that by the weight and that's it.  As you move
-** down a row, calculate new column sums by using previous sum for that column
-** and adding in pixel on current row and subtracting pixel in top row.
-**
-*/
+static void
+getKernelPnm(const char *         const fileName,
+             unsigned int         const depth,
+             bool                 const offset,
+             struct ConvKernel ** const convKernelPP) {
+/*----------------------------------------------------------------------------
+   Get the convolution kernel from the PNM file named 'fileName'.
+   'offset' means the PNM convolution matrix is defined in offset form so
+   that it can represent negative values.  E.g. with maxval 100, 50 means
+   0, 100 means 50, and 0 means -50.  If 'offsetPgm' is false, 0 means 0
+   and there are no negative weights.
+
+   Make the kernel suitable for convolving an image of depth 'depth'.
+
+   Return the kernel as *convKernelPP.
+-----------------------------------------------------------------------------*/
+    struct pam cpam;
+    FILE * cifP;
+    tuple ** ctuples;
+
+    cifP = pm_openr(fileName);
+
+    /* Read in the convolution matrix. */
+    ctuples = pnm_readpam(cifP, &cpam, PAM_STRUCT_SIZE(tuple_type));
+    pm_close(cifP);
+    
+    validateKernelDimensions(cpam.width, cpam.height);
+
+    convKernelCreatePnm(&cpam, ctuples, depth, offset, convKernelPP);
+}
+
 
 
 static void
-pgm_mean_convolve(const float ** const weights) {
-    float const gmeanweight = weights[0][0];
-
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval g;
-    int row, crow;
-    xel **rowptr, *temprptr;
-    int leftcol;
-    int i, irow;
-    int toprow, temprow;
-    int subrow, addrow;
-    int subcol, addcol;
-    long gisum;
-    int tempcol, crowsp1;
-    long tempgsum;
-    long *gcolumnsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer.  MEAN uses an extra row. */
-    xelbuf = pnm_allocarray( cols, crows + 1 );
-    outputrow = pnm_allocrow( cols );
-
-    /* Allocate array of pointers to xelbuf. MEAN uses an extra row. */
-    rowptr = (xel **) pnm_allocarray( 1, crows + 1);
-
-    /* Allocate space for intermediate column sums */
-    gcolumnsum = (long *) pm_allocrow( cols, sizeof(long) );
-    for ( col = 0; col < cols; ++col )
-    gcolumnsum[col] = 0L;
-
-    pnm_writepnminit( stdout, cols, rows, maxval, newformat, 0 );
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for ( row = 0; row < crows - 1; ++row )
-    {
-    pnm_readpnmrow( ifp, xelbuf[row], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[row], cols, maxval, format, maxval, newformat );
-    /* Write out just the part we're not going to convolve. */
-    if ( row < crowso2 )
-        pnm_writepnmrow( stdout, xelbuf[row], cols, maxval, newformat, 0 );
-    }
+convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
+                          bool                 const normalize,
+                          unsigned int         const depth,
+                          unsigned int         const bias,
+                          struct ConvKernel ** const convKernelPP) {
+/*----------------------------------------------------------------------------
+   Create a convolution kernel as described by a -matrix command line
+   option.
+   
+   The option value is 'matrixOpt'.
+
+   If 'normalize' is true, we normalize whatever numbers the option specifies
+   so that they add up to one; otherwise, we take the numbers as we find them,
+   so they may form a biased matrix -- i.e. one which brightens or dims the
+   image overall.
+-----------------------------------------------------------------------------*/
+    struct ConvKernel * convKernelP;
+    unsigned int plane;
 
-    /* Do first real row only */
-    subrow = crows;
-    addrow = crows - 1;
-    toprow = row + 1;
-    temprow = row % crows;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-    pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
-
-    temprow = toprow % crows;
-    i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-    rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-    rowptr[i] = xelbuf[irow];
+    MALLOCVAR(convKernelP);
 
-    gisum = 0L;
-    for ( col = 0; col < cols; ++col )
-    {
-    if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-    else if ( col == ccolso2 )
-        {
-        leftcol = col - ccolso2;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        temprptr = rowptr[crow] + leftcol;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            gcolumnsum[leftcol + ccol] += 
-            PNM_GET1( *(temprptr + ccol) );
-        }
-        for ( ccol = 0; ccol < ccols; ++ccol)
-        gisum += gcolumnsum[leftcol + ccol];
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-    else
-        {
-        /* Column numbers to subtract or add to isum */
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;  
-        for ( crow = 0; crow < crows; ++crow )
-        gcolumnsum[addcol] += PNM_GET1( rowptr[crow][addcol] );
-        gisum = gisum - gcolumnsum[subcol] + gcolumnsum[addcol];
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-    }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
+    convKernelP->cols = matrixOpt.width;
+    convKernelP->rows = matrixOpt.height;
+    convKernelP->planes = depth;
 
-    ++row;
-    /* For all subsequent rows do it this way as the columnsums have been
-    ** generated.  Now we can use them to reduce further calculations.
-    */
-    crowsp1 = crows + 1;
-    for ( ; row < rows; ++row )
-    {
-    toprow = row + 1;
-    temprow = row % (crows + 1);
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
-
-    /* This rearrangement using crows+1 rowptrs and xelbufs will cause
-    ** rowptr[0..crows-1] to always hold active xelbufs and for 
-    ** rowptr[crows] to always hold the oldest (top most) xelbuf.
-    */
-    temprow = (toprow + 1) % crowsp1;
-    i = 0;
-    for (irow = temprow; irow < crowsp1; ++i, ++irow)
-        rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        rowptr[i] = xelbuf[irow];
-
-    gisum = 0L;
-    for ( col = 0; col < cols; ++col )
-        {
-        if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-        else if ( col == ccolso2 )
-        {
-        leftcol = col - ccolso2;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            {
-            tempcol = leftcol + ccol;
-            gcolumnsum[tempcol] = gcolumnsum[tempcol]
-            - PNM_GET1( rowptr[subrow][ccol] )
-            + PNM_GET1( rowptr[addrow][ccol] );
-            gisum += gcolumnsum[tempcol];
-            }
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-        else
-        {
-        /* Column numbers to subtract or add to isum */
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;  
-        gcolumnsum[addcol] = gcolumnsum[addcol]
-            - PNM_GET1( rowptr[subrow][addcol] )
-            + PNM_GET1( rowptr[addrow][addcol] );
-        gisum = gisum - gcolumnsum[subcol] + gcolumnsum[addcol];
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
+    for (plane = 0; plane < depth; ++plane) {
+        unsigned int row;
+        MALLOCARRAY_NOFAIL(convKernelP->weight[plane], matrixOpt.height);
+
+        for (row = 0; row < matrixOpt.height; ++row) {
+            unsigned int col;
+
+            MALLOCARRAY_NOFAIL(convKernelP->weight[plane][row],
+                               matrixOpt.width);
+    
+            for (col = 0; col < matrixOpt.width; ++col)
+                convKernelP->weight[plane][row][col] =
+                    matrixOpt.weight[row][col];
         }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
     }
+    if (normalize)
+        normalizeKernel(convKernelP);
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for ( irow = crowso2 + 1; irow < crows; ++irow )
-    pnm_writepnmrow(
-            stdout, rowptr[irow], cols, maxval, newformat, 0 );
+    convKernelP->bias = bias;
 
-    }
+    *convKernelPP = convKernelP;
+}
 
 
-/* PGM Horizontal Convolution
-**
-** Similar idea to using columnsums of the Mean and Vertical convolution,
-** but uses temporary sums of row values.  Need to multiply by weights crows
-** number of times.  Each time a new line is started, must recalculate the
-** initials rowsums for the newest row only.  Uses queue to still access
-** previous row sums.
-**
-*/
 
 static void
-pgm_horizontal_convolve(const float ** const weights) {
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval g;
-    int row, crow;
-    xel **rowptr, *temprptr;
-    int leftcol;
-    int i, irow;
-    int temprow;
-    int subcol, addcol;
-    float gsum;
-    int addrow, subrow;
-    long **growsum, **growsumptr;
-    int crowsp1;
-    long tempgsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer. */
-    xelbuf = pnm_allocarray( cols, crows + 1 );
-    outputrow = pnm_allocrow( cols );
-
-    /* Allocate array of pointers to xelbuf */
-    rowptr = (xel **) pnm_allocarray( 1, crows + 1);
-
-    /* Allocate intermediate row sums.  HORIZONTAL uses an extra row. */
-    /* crows current rows and 1 extra for newest added row.           */
-    growsum = (long **) pm_allocarray( cols, crows + 1, sizeof(long) );
-    growsumptr = (long **) pnm_allocarray( 1, crows + 1);
-
-    pnm_writepnminit( stdout, cols, rows, maxval, newformat, 0 );
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for ( row = 0; row < crows - 1; ++row )
-    {
-    pnm_readpnmrow( ifp, xelbuf[row], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[row], cols, maxval, format, maxval, newformat );
-    /* Write out just the part we're not going to convolve. */
-    if ( row < crowso2 )
-        pnm_writepnmrow( stdout, xelbuf[row], cols, maxval, newformat, 0 );
-    }
+parsePlaneFileLine(const char *   const line,
+                   unsigned int * const widthP,
+                   float **       const weightP) {
 
-    /* First row only */
-    temprow = row % crows;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-    pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
+    unsigned int colCt;
+    const char * error;
+    float * weight;
+    const char * cursor;
 
-    temprow = (row + 1) % crows;
-    i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-    rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-    rowptr[i] = xelbuf[irow];
+    colCt = 0;  /* initial value */
+    weight = NULL;
 
-    for ( crow = 0; crow < crows; ++crow )
-    growsumptr[crow] = growsum[crow];
- 
-    for ( col = 0; col < cols; ++col )
-    {
-    if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-    else if ( col == ccolso2 )
-        {
-        leftcol = col - ccolso2;
-        gsum = 0.0;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        temprptr = rowptr[crow] + leftcol;
-        growsumptr[crow][leftcol] = 0L;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            growsumptr[crow][leftcol] += 
-                PNM_GET1( *(temprptr + ccol) );
-        gsum += growsumptr[crow][leftcol] * weights[crow][0];
-        }
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-    else
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        growsumptr[crow][leftcol] = growsumptr[crow][subcol]
-            - PNM_GET1( rowptr[crow][subcol] )
-            + PNM_GET1( rowptr[crow][addcol] );
-        gsum += growsumptr[crow][leftcol] * weights[crow][0];
-        }
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-        }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
+    for (cursor = &line[0]; *cursor; ) {
+        const char * token;
+        const char * next;
 
+        REALLOCARRAY(weight, colCt + 1);
 
-    /* For all subsequent rows */
+        pm_gettoken(cursor, ' ', &token, &next, &error);
 
-    subrow = crows;
-    addrow = crows - 1;
-    crowsp1 = crows + 1;
-    ++row;
-    for ( ; row < rows; ++row )
-    {
-    temprow = row % crowsp1;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
+        if (error)
+            pm_error("Invalid format of line in convolution matrix file: "
+                     "'%s'.  %s", line, error);
 
-    temprow = (row + 2) % crowsp1;
-    i = 0;
-    for (irow = temprow; irow < crowsp1; ++i, ++irow)
-        {
-        rowptr[i] = xelbuf[irow];
-        growsumptr[i] = growsum[irow];
-        }
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        {
-        rowptr[i] = xelbuf[irow];
-        growsumptr[i] = growsum[irow];
-        }
+        cursor = next;
 
-    for ( col = 0; col < cols; ++col )
-        {
-        if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-        else if ( col == ccolso2 )
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        growsumptr[addrow][leftcol] = 0L;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            growsumptr[addrow][leftcol] += 
-            PNM_GET1( rowptr[addrow][leftcol + ccol] );
-        for ( crow = 0; crow < crows; ++crow )
-            gsum += growsumptr[crow][leftcol] * weights[crow][0];
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-        else
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;  
-        growsumptr[addrow][leftcol] = growsumptr[addrow][subcol]
-            - PNM_GET1( rowptr[addrow][subcol] )
-            + PNM_GET1( rowptr[addrow][addcol] );
-        for ( crow = 0; crow < crows; ++crow )
-            gsum += growsumptr[crow][leftcol] * weights[crow][0];
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
+        if (*cursor) {
+            assert(*next == ' ');
+            ++cursor;  /* advance over space */
         }
+        if (strlen(token) == 0)
+            pm_error("Column %u value in line '%s' of convolution matrix file "
+                     "is null string.", colCt, line);
+        else {
+            char * trailingJunk;
+            weight[colCt] = strtod(token, &trailingJunk);
+            if (*trailingJunk != '\0') 
+                pm_error("The Column %u element of the row '%s' in the "
+                         "-matrix value is not a valid floating point "
+                         "number", colCt, line);
+
+                ++colCt;
         }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
+        pm_strfree(token);
     }
+    *weightP = weight;
+    *widthP = colCt;
+}
+
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for ( irow = crowso2 + 1; irow < crows; ++irow )
-    pnm_writepnmrow(
-            stdout, rowptr[irow], cols, maxval, newformat, 0 );
 
+static void
+readPlaneFile(FILE *         const ifP, 
+              float ***      const weightP,
+              unsigned int * const widthP,
+              unsigned int * const heightP) {
+/*----------------------------------------------------------------------------
+   Read weights of one plane from a file.
+
+   The file is a simple matrix, one line per row, with columns separated
+   by a single space.
+
+   Each column is a floating point decimal ASCII number, positive zero,
+   or negative, with any magnitude.
+
+   If the rows don't all have the same number of columns, we abort.
+
+   Return the dimensions seen in the file as *widthP and *heightP.
+-----------------------------------------------------------------------------*/
+    unsigned int rowCt;
+    float ** weight;
+    unsigned int width;
+    bool eof;
+
+    weight = NULL;  /* initial value */
+
+    for (eof = false, rowCt = 0; !eof; ) {
+        const char * error;
+        const char * line;
+
+        pm_freadline(ifP, &line, &error);
+
+        if (error)
+            pm_error("Failed to read row %u "
+                     "from the convolutionmatrix file.  %s",
+                     rowCt, error);
+        else {
+            if (line == NULL)
+                eof = true;
+            else {
+                REALLOCARRAY(weight, rowCt + 1);
+            
+                if (weight == NULL)
+                    pm_error("Unable to allocate memory for "
+                             "convolution matrix");
+                else {
+                    unsigned int thisWidth;
+
+                    parsePlaneFileLine(line, &thisWidth, &weight[rowCt]);
+
+                    if (rowCt == 0)
+                        width = thisWidth;
+                    else {
+                        if (thisWidth != width)
+                            pm_error("Multiple row widths in the convolution "
+                                     "matrix file: %u columns and %u columns.",
+                                     width, thisWidth);
+                    }                    
+                    ++rowCt;
+                }
+                pm_strfree(line);
+            }
+        }
     }
+    validateKernelDimensions(width, rowCt);
 
+    *weightP = weight;
+    *heightP = rowCt;
+    *widthP = width;
+}
 
-/* PGM Vertical Convolution
-**
-** Uses column sums as in Mean Convolution.
-**
-*/
 
 
 static void
-pgm_vertical_convolve(const float ** const weights) {
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval g;
-    int row, crow;
-    xel **rowptr, *temprptr;
-    int leftcol;
-    int i, irow;
-    int toprow, temprow;
-    int subrow, addrow;
-    int tempcol;
-    float gsum;
-    long *gcolumnsum;
-    int crowsp1;
-    int addcol;
-    long tempgsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer. VERTICAL uses an extra row. */
-    xelbuf = pnm_allocarray( cols, crows + 1 );
-    outputrow = pnm_allocrow( cols );
-
-    /* Allocate array of pointers to xelbuf */
-    rowptr = (xel **) pnm_allocarray( 1, crows + 1 );
-
-    /* Allocate space for intermediate column sums */
-    gcolumnsum = (long *) pm_allocrow( cols, sizeof(long) );
-    for ( col = 0; col < cols; ++col )
-    gcolumnsum[col] = 0L;
-
-    pnm_writepnminit( stdout, cols, rows, maxval, newformat, 0 );
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for ( row = 0; row < crows - 1; ++row )
-    {
-    pnm_readpnmrow( ifp, xelbuf[row], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[row], cols, maxval, format, maxval, newformat );
-    /* Write out just the part we're not going to convolve. */
-    if ( row < crowso2 )
-        pnm_writepnmrow( stdout, xelbuf[row], cols, maxval, newformat, 0 );
-    }
+copyWeight(float **       const srcWeight,
+           unsigned int   const width,
+           unsigned int   const height, 
+           float ***      const dstWeightP) {
+/*----------------------------------------------------------------------------
+   Make a copy, in dynamically allocated memory, of the weight matrix
+   'srcWeight', whose dimensions are 'width' by 'height'.  Return the
+   new matrix as *dstWeightP.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+    float ** dstWeight;
 
-    /* Now the rest of the image - read in the row at the end of
-    ** xelbuf, and convolve and write out the row in the middle.
-    */
-    /* For first row only */
+    MALLOCARRAY(dstWeight, height);
 
-    toprow = row + 1;
-    temprow = row % crows;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-    pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
+    if (dstWeight == NULL)
+        pm_error("Could not allocate memory for convolution matrix");
+   
+    for (row = 0; row < height; ++row) {
+        unsigned int col;
 
-    /* Arrange rowptr to eliminate the use of mod function to determine
-    ** which row of xelbuf is 0...crows.  Mod function can be very costly.
-    */
-    temprow = toprow % crows;
-    i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-    rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-    rowptr[i] = xelbuf[irow];
+        MALLOCARRAY(dstWeight[row], width);
 
-    for ( col = 0; col < cols; ++col )
-    {
-    if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-    else if ( col == ccolso2 )
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        temprptr = rowptr[crow] + leftcol;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            gcolumnsum[leftcol + ccol] += 
-            PNM_GET1( *(temprptr + ccol) );
-        }
-        for ( ccol = 0; ccol < ccols; ++ccol)
-        gsum += gcolumnsum[leftcol + ccol] * weights[0][ccol];
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-    else
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        addcol = col + ccolso2;  
-        for ( crow = 0; crow < crows; ++crow )
-        gcolumnsum[addcol] += PNM_GET1( rowptr[crow][addcol] );
-        for ( ccol = 0; ccol < ccols; ++ccol )
-        gsum += gcolumnsum[leftcol + ccol] * weights[0][ccol];
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
+        if (dstWeight[row] == NULL)
+            pm_error("Could not allocation memory for a "
+                     "convolution matrix row");
+
+        for (col = 0; col < width; ++col) {
+            dstWeight[row][col] = srcWeight[row][col];
         }
     }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
-
-    /* For all subsequent rows */
-    subrow = crows;
-    addrow = crows - 1;
-    crowsp1 = crows + 1;
-    ++row;
-    for ( ; row < rows; ++row )
-    {
-    toprow = row + 1;
-    temprow = row % (crows +1);
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
-
-    /* Arrange rowptr to eliminate the use of mod function to determine
-    ** which row of xelbuf is 0...crows.  Mod function can be very costly.
-    */
-    temprow = (toprow + 1) % crowsp1;
-    i = 0;
-    for (irow = temprow; irow < crowsp1; ++i, ++irow)
-        rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        rowptr[i] = xelbuf[irow];
-
-    for ( col = 0; col < cols; ++col )
-        {
-        if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-        else if ( col == ccolso2 )
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            {
-            tempcol = leftcol + ccol;
-            gcolumnsum[tempcol] = gcolumnsum[tempcol] 
-            - PNM_GET1( rowptr[subrow][ccol] )
-            + PNM_GET1( rowptr[addrow][ccol] );
-            gsum = gsum + gcolumnsum[tempcol] * weights[0][ccol];
+    *dstWeightP = dstWeight;
+}
+
+
+
+static void
+convKernelCreateSimpleFile(const char **        const fileNameList,
+                           bool                 const normalize,
+                           unsigned int         const depth,
+                           unsigned int         const bias,
+                           struct ConvKernel ** const convKernelPP) {
+/*----------------------------------------------------------------------------
+   Create a convolution kernel as described by a convolution matrix file.
+   This is the simple file with floating point numbers in it, not the
+   legacy pseudo-PNM thing.
+
+   The name of the file is 'fileNameList'.
+
+   If 'normalize' is true, we normalize whatever numbers we find in the file
+   so that they add up to one; otherwise, we take the numbers as we find them,
+   so they may form a biased matrix -- i.e. one which brightens or dims the
+   image overall.
+-----------------------------------------------------------------------------*/
+    struct ConvKernel * convKernelP;
+    unsigned int fileCt;
+    unsigned int planeCt;
+    unsigned int plane;
+    unsigned int width, height;
+
+    fileCt = 0;
+    while (fileNameList[fileCt])
+        ++fileCt;
+    assert(fileCt > 0);
+
+    planeCt = MIN(3, depth);
+
+    MALLOCVAR_NOFAIL(convKernelP);
+
+    convKernelP->planes = planeCt;
+
+    for (plane = 0; plane < planeCt; ++plane) {
+        if (plane < fileCt) {
+            const char * const fileName = fileNameList[plane];
+
+            FILE * ifP;
+            unsigned int thisWidth, thisHeight;
+
+            ifP = pm_openr(fileName);
+
+            readPlaneFile(ifP, &convKernelP->weight[plane],
+                          &thisWidth, &thisHeight);
+
+            if (plane == 0) {
+                width = thisWidth;
+                height = thisHeight;
+            } else {
+                if (thisWidth != width)
+                    pm_error("Convolution matrix files show two different "
+                             "widths: %u and %u", width, thisWidth);
+                if (thisHeight != height)
+                    pm_error("Convolution matrix files show two different "
+                             "heights: %u and %u", height, thisHeight);
             }
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
-        else
-        {
-        gsum = 0.0;
-        leftcol = col - ccolso2;
-        addcol = col + ccolso2;
-        gcolumnsum[addcol] = gcolumnsum[addcol]
-            - PNM_GET1( rowptr[subrow][addcol] )
-            + PNM_GET1( rowptr[addrow][addcol] );
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            gsum += gcolumnsum[leftcol + ccol] * weights[0][ccol];
-        tempgsum = gsum + 0.5;
-        CHECK_GRAY;
-        PNM_ASSIGN1( outputrow[col], g );
-        }
+            pm_close(ifP);
+        } else {
+            assert(plane > 0);
+            copyWeight(convKernelP->weight[0], width, height,
+                       &convKernelP->weight[plane]);
         }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
     }
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for ( irow = crowso2 + 1; irow < crows; ++irow )
-    pnm_writepnmrow(
-            stdout, rowptr[irow], cols, maxval, newformat, 0 );
+    if (normalize)
+        normalizeKernel(convKernelP);
 
-    }
+    convKernelP->cols = width;
+    convKernelP->rows = height;
+    convKernelP->bias = bias;
 
+    *convKernelPP = convKernelP;
+}
 
 
 
-/* PPM General Convolution Algorithm
-**
-** No redundancy in convolution matrix.  Just use brute force.
-** See pgm_general_convolve() for more details.
-*/
+static void
+getKernel(struct cmdlineInfo   const cmdline,
+          unsigned int         const depth,
+          struct ConvKernel ** const convKernelPP) {
+/*----------------------------------------------------------------------------
+   Figure out what the convolution kernel is.  It can come from various
+   sources in various forms, as described on the command line, represented
+   by 'cmdline'.
+
+   We generate a kernel object in standard form (free of any indication of
+   where it came from) and return a handle to it as *convKernelPP.
+----------------------------------------------------------------------------*/
+    struct ConvKernel * convKernelP;
+
+    if (cmdline.pnmMatrixFileName)
+        getKernelPnm(cmdline.pnmMatrixFileName, depth, !cmdline.nooffset,
+                     &convKernelP);
+    else if (cmdline.matrixfile)
+        convKernelCreateSimpleFile(cmdline.matrixfile, cmdline.normalize,
+                                   depth, cmdline.bias, &convKernelP);
+    else if (cmdline.matrixSpec)
+        convKernelCreateMatrixOpt(cmdline.matrix, cmdline.normalize,
+                                  depth, cmdline.bias, &convKernelP);
+
+    warnBadKernel(convKernelP);
+
+    *convKernelPP = convKernelP;
+}
+
+
 
 static void
-ppm_general_convolve(const float ** const rweights,
-                     const float ** const gweights,
-                     const float ** const bweights) {
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval r, g, b;
-    int row, crow;
-    float rsum, gsum, bsum;
-    xel **rowptr, *temprptr;
-    int toprow, temprow;
-    int i, irow;
-    int leftcol;
-    long temprsum, tempgsum, tempbsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer. */
-    xelbuf = pnm_allocarray( cols, crows );
-    outputrow = pnm_allocrow( cols );
-
-    /* Allocate array of pointers to xelbuf */
-    rowptr = (xel **) pnm_allocarray( 1, crows );
-
-    pnm_writepnminit( stdout, cols, rows, maxval, newformat, 0 );
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for ( row = 0; row < crows - 1; ++row )
-    {
-    pnm_readpnmrow( ifp, xelbuf[row], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[row], cols, maxval, format, maxval, newformat );
-    /* Write out just the part we're not going to convolve. */
-    if ( row < crowso2 )
-        pnm_writepnmrow( stdout, xelbuf[row], cols, maxval, newformat, 0 );
+validateEnoughImageToConvolve(const struct pam *        const inpamP,
+                              const struct ConvKernel * const convKernelP) {
+/*----------------------------------------------------------------------------
+   Abort program if the image isn't big enough in both directions to have
+   at least one convolved pixel.
+
+   The program could theoretically operate with an image smaller than that by
+   simply outputting the input unchanged (like it does with the edges of an
+   image anyway), but we're too lazy to write code for this special case.  The
+   simple code expects the unconvolved edges to exist full-size and some of it
+   convolves the first convolveable row and/or column specially and expects it
+   to exist.
+-----------------------------------------------------------------------------*/
+
+    if (inpamP->height < convKernelP->rows + 1)
+        pm_error("Image is too short (%u rows) to convolve with this "
+                 "%u-row convolution kernel.",
+                 inpamP->height, convKernelP->rows);
+    
+    if (inpamP->width < convKernelP->cols + 1)
+        pm_error("Image is too narrow (%u columns) to convolve with this "
+                 "%u-column convolution kernel.",
+                 inpamP->width, convKernelP->cols);
+}
+
+
+
+static tuple **
+allocRowbuf(struct pam * const pamP,
+            unsigned int const height) {
+
+    tuple ** rowbuf;
+
+    MALLOCARRAY(rowbuf, height);
+
+    if (rowbuf == NULL)
+        pm_error("Failed to allocate %u-row buffer", height);
+    else {
+        unsigned int row;
+    
+        for (row = 0; row < height; ++row)
+            rowbuf[row] = pnm_allocpamrow(pamP);
     }
 
-    /* Now the rest of the image - read in the row at the end of
-    ** xelbuf, and convolve and write out the row in the middle.
-    */
-    for ( ; row < rows; ++row )
-    {
-    toprow = row + 1;
-    temprow = row % crows;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
-
-    /* Arrange rowptr to eliminate the use of mod function to determine
-    ** which row of xelbuf is 0...crows.  Mod function can be very costly.
-    */
-    temprow = toprow % crows;
-    i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-        rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        rowptr[i] = xelbuf[irow];
-
-    for ( col = 0; col < cols; ++col )
-        {
-        if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-        else
-        {
-        leftcol = col - ccolso2;
-        rsum = gsum = bsum = 0.0;
-        for ( crow = 0; crow < crows; ++crow )
-            {
-            temprptr = rowptr[crow] + leftcol;
-            for ( ccol = 0; ccol < ccols; ++ccol )
-            {
-            rsum += PPM_GETR( *(temprptr + ccol) )
-                * rweights[crow][ccol];
-            gsum += PPM_GETG( *(temprptr + ccol) )
-                * gweights[crow][ccol];
-            bsum += PPM_GETB( *(temprptr + ccol) )
-                * bweights[crow][ccol];
-            }
+    return rowbuf;
+}
+
+
+
+static void
+freeRowbuf(tuple **     const rowbuf,
+           unsigned int const height) {
+
+    unsigned int row;
+
+    for (row = 0; row < height; ++row)
+        pnm_freepamrow(rowbuf[row]);
+
+    free(rowbuf);
+}
+
+
+
+static void
+readAndScaleRow(struct pam * const inpamP,
+                tuple *      const inrow,
+                sample       const newMaxval,
+                unsigned int const newDepth) {
+
+    pnm_readpamrow(inpamP, inrow);
+
+    if (newMaxval != inpamP->maxval)
+        pnm_scaletuplerow(inpamP, inrow, inrow, newMaxval);
+
+    if (newDepth == 3 && inpamP->depth == 1)
+        pnm_makerowrgb(inpamP, inrow);
+}
+
+
+
+static void
+readAndScaleRows(struct pam *              const inpamP,
+                 unsigned int              const count,
+                 tuple **                  const rowbuf,
+                 sample                    const outputMaxval,
+                 unsigned int              const outputDepth) {
+/*----------------------------------------------------------------------------
+  Read in 'count' rows into rowbuf[].
+  
+  Scale the contents to maxval 'outputMaxval' and expand to depth
+  'outputDepth'.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    for (row = 0; row < count; ++row)
+        readAndScaleRow(inpamP, rowbuf[row], outputMaxval, outputDepth);
+}
+
+
+
+static void
+writePamRowBiased(struct pam * const outpamP,
+                  tuple *      const row,
+                  unsigned int const bias) {
+/*----------------------------------------------------------------------------
+   Write row[] to the output file according to *outpamP, but with
+   'bias' added to each sample value, clipped to maxval.
+-----------------------------------------------------------------------------*/
+    if (bias == 0)
+        pnm_writepamrow(outpamP, row);
+    else {
+        unsigned int col;
+
+        tuple * const outrow = pnm_allocpamrow(outpamP);
+
+        for (col = 0; col < outpamP->width; ++col) {
+            unsigned int plane;
+
+            for (plane = 0; plane < outpamP->depth; ++plane) {
+                outrow[col][plane] =
+                    MIN(outpamP->maxval, bias + row[col][plane]);
             }
-            temprsum = rsum + 0.5;
-            tempgsum = gsum + 0.5;
-            tempbsum = bsum + 0.5;
-            CHECK_RED;
-            CHECK_GREEN;
-            CHECK_BLUE;
-            PPM_ASSIGN( outputrow[col], r, g, b );
         }
-        }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
+        pnm_writepamrow(outpamP, outrow);
+
+        pnm_freepamrow(outrow);
     }
+}
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for ( irow = crowso2 + 1; irow < crows; ++irow )
-    pnm_writepnmrow(
-            stdout, rowptr[irow], cols, maxval, newformat, 0 );
 
-    }
+
+static void
+writeUnconvolvedTop(struct pam *              const outpamP,
+                    const struct ConvKernel * const convKernelP,
+                    tuple **                  const rowbuf) {
+/*----------------------------------------------------------------------------
+   Write out the top part that we can't convolve because the convolution
+   kernel runs off the top of the image.
+
+   Assume those rows are in the window rowbuf[], with the top row of the
+   image as the first row in rowbuf[].
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    for (row = 0; row < convKernelP->rows/2; ++row)
+        writePamRowBiased(outpamP, rowbuf[row], convKernelP->bias);
+}
 
 
-/* PPM Mean Convolution
-**
-** Same as pgm_mean_convolve() but for PPM.
-**
-*/
 
 static void
-ppm_mean_convolve(const float ** const rweights,
-                  const float ** const gweights,
-                  const float ** const bweights) {
-    /* All weights of a single color are the same so just grab any one
-       of them.  
-    */
-    float const rmeanweight = rweights[0][0];
-    float const gmeanweight = gweights[0][0];
-    float const bmeanweight = bweights[0][0];
-
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval r, g, b;
-    int row, crow;
-    xel **rowptr, *temprptr;
-    int leftcol;
-    int i, irow;
-    int toprow, temprow;
-    int subrow, addrow;
-    int subcol, addcol;
-    long risum, gisum, bisum;
-    long temprsum, tempgsum, tempbsum;
-    int tempcol, crowsp1;
-    long *rcolumnsum, *gcolumnsum, *bcolumnsum;
-
-
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer.  MEAN uses an extra row. */
-    xelbuf = pnm_allocarray( cols, crows + 1 );
-    outputrow = pnm_allocrow( cols );
-
-    /* Allocate array of pointers to xelbuf. MEAN uses an extra row. */
-    rowptr = (xel **) pnm_allocarray( 1, crows + 1);
-
-    /* Allocate space for intermediate column sums */
-    rcolumnsum = (long *) pm_allocrow( cols, sizeof(long) );
-    gcolumnsum = (long *) pm_allocrow( cols, sizeof(long) );
-    bcolumnsum = (long *) pm_allocrow( cols, sizeof(long) );
-    for ( col = 0; col < cols; ++col )
-    {
-    rcolumnsum[col] = 0L;
-    gcolumnsum[col] = 0L;
-    bcolumnsum[col] = 0L;
-    }
+writeUnconvolvedBottom(struct pam *              const outpamP,
+                       const struct ConvKernel * const convKernelP,
+                       unsigned int              const windowHeight,
+                       tuple **                  const circMap) {
+/*----------------------------------------------------------------------------
+  Write out the bottom part that we can't convolve because the convolution
+  kernel runs off the bottom of the image.
 
-    pnm_writepnminit( stdout, cols, rows, maxval, newformat, 0 );
+  Assume the 'windowHeight' rows at the bottom of the image are in the row
+  buffer, mapped by 'circMap' such that the top of the window is circMap[0].
+-----------------------------------------------------------------------------*/
+    unsigned int row;
 
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for ( row = 0; row < crows - 1; ++row )
-    {
-    pnm_readpnmrow( ifp, xelbuf[row], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[row], cols, maxval, format, maxval, newformat );
-    /* Write out just the part we're not going to convolve. */
-    if ( row < crowso2 )
-        pnm_writepnmrow( stdout, xelbuf[row], cols, maxval, newformat, 0 );
+    for (row = windowHeight - convKernelP->rows / 2;
+         row < windowHeight;
+         ++row) {
+
+        writePamRowBiased(outpamP, circMap[row], convKernelP->bias);
     }
+}
+
+
+
+static void
+setupCircMap(tuple **     const circMap,
+             tuple **     const rowbuf,
+             unsigned int const windowHeight,
+             unsigned int const topRowbufRow) {
+/*----------------------------------------------------------------------------
+  Set up circMap[] to reflect the case that index 'topRowbufRow' of rowbuf[]
+  is for the topmost row in the window.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+    unsigned int i;
 
-    /* Do first real row only */
-    subrow = crows;
-    addrow = crows - 1;
-    toprow = row + 1;
-    temprow = row % crows;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-    pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
-
-    temprow = toprow % crows;
     i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-    rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-    rowptr[i] = xelbuf[irow];
-
-    risum = 0L;
-    gisum = 0L;
-    bisum = 0L;
-    for ( col = 0; col < cols; ++col )
-    {
-    if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-    else if ( col == ccolso2 )
-        {
-        leftcol = col - ccolso2;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        temprptr = rowptr[crow] + leftcol;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            {
-            rcolumnsum[leftcol + ccol] += 
-            PPM_GETR( *(temprptr + ccol) );
-            gcolumnsum[leftcol + ccol] += 
-            PPM_GETG( *(temprptr + ccol) );
-            bcolumnsum[leftcol + ccol] += 
-            PPM_GETB( *(temprptr + ccol) );
+
+    for (row = topRowbufRow; row < windowHeight; ++i, ++row)
+        circMap[i] = rowbuf[row];
+
+    for (row = 0; row < topRowbufRow; ++row, ++i)
+        circMap[i] = rowbuf[row];
+}
+
+
+
+static void
+convolveGeneralRowPlane(struct pam *              const pamP,
+                        tuple **                  const window,
+                        const struct ConvKernel * const convKernelP,
+                        unsigned int              const plane,
+                        tuple *                   const outputrow) {
+/*----------------------------------------------------------------------------
+   Given a window of input window[], where window[0] is the top row of the
+   window and the window is the height of the convolution kernel, convolve
+   Plane 'plane' of the row at the center of the window.
+
+   Return the convolved row as outputrow[].
+
+   *pamP describes the rows in window[] (but not the number of rows).
+
+   *convKernelP is the convolution kernel to use.
+-----------------------------------------------------------------------------*/
+    unsigned int const crowso2 = convKernelP->rows / 2;
+    unsigned int const ccolso2 = convKernelP->cols / 2;
+
+    unsigned int col;
+    
+    for (col = 0; col < pamP->width; ++col) {
+        if (col < ccolso2 || col >= pamP->width - ccolso2)
+            /* The unconvolved left or right edge */
+            outputrow[col][plane] =
+                clipSample(convKernelP->bias + window[crowso2][col][plane],
+                           pamP->maxval);
+        else {
+            unsigned int const leftcol = col - ccolso2;
+            unsigned int crow;
+            float sum;
+            sum = 0.0;
+            for (crow = 0; crow < convKernelP->rows; ++crow) {
+                const tuple * const leftrptr = &window[crow][leftcol];
+                unsigned int ccol;
+                for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                    sum += leftrptr[ccol][plane] *
+                        convKernelP->weight[plane][crow][ccol];
             }
-        }
-        for ( ccol = 0; ccol < ccols; ++ccol)
-        {
-        risum += rcolumnsum[leftcol + ccol];
-        gisum += gcolumnsum[leftcol + ccol];
-        bisum += bcolumnsum[leftcol + ccol];
-        }
-        temprsum = (float) risum * rmeanweight + 0.5;
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        tempbsum = (float) bisum * bmeanweight + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
-        }
-    else
-        {
-        /* Column numbers to subtract or add to isum */
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;  
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        rcolumnsum[addcol] += PPM_GETR( rowptr[crow][addcol] );
-        gcolumnsum[addcol] += PPM_GETG( rowptr[crow][addcol] );
-        bcolumnsum[addcol] += PPM_GETB( rowptr[crow][addcol] );
-        }
-        risum = risum - rcolumnsum[subcol] + rcolumnsum[addcol];
-        gisum = gisum - gcolumnsum[subcol] + gcolumnsum[addcol];
-        bisum = bisum - bcolumnsum[subcol] + bcolumnsum[addcol];
-        temprsum = (float) risum * rmeanweight + 0.5;
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        tempbsum = (float) bisum * bmeanweight + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
+            outputrow[col][plane] =
+                makeSample(convKernelP->bias + sum, pamP->maxval);
         }
     }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
+}
 
-    ++row;
-    /* For all subsequent rows do it this way as the columnsums have been
-    ** generated.  Now we can use them to reduce further calculations.
-    */
-    crowsp1 = crows + 1;
-    for ( ; row < rows; ++row )
-    {
-    toprow = row + 1;
-    temprow = row % (crows + 1);
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
-
-    /* This rearrangement using crows+1 rowptrs and xelbufs will cause
-    ** rowptr[0..crows-1] to always hold active xelbufs and for 
-    ** rowptr[crows] to always hold the oldest (top most) xelbuf.
+
+
+static void
+convolveGeneral(struct pam *              const inpamP,
+                struct pam *              const outpamP,
+                const struct ConvKernel * const convKernelP) {
+/*----------------------------------------------------------------------------
+   Do the convolution without taking advantage of any useful redundancy in the
+   convolution matrix.
+-----------------------------------------------------------------------------*/
+    tuple ** rowbuf;
+        /* A vertical window of the input image.  It holds as many rows as the
+           convolution kernel covers -- the rows we're currently using to
+           create output rows.  It is a circular buffer.
+        */
+    tuple ** circMap;
+        /* A map from image row number within window to element of rowbuf[].
+           E.g. if rowbuf[] if 5 rows high and rowbuf[2] contains the
+           topmost row, then circMap[0] == 2, circMap[1] = 3,
+           circMap[4] = 1.  You could calculate the same thing with a mod
+           function, but that is sometimes more expensive.
+        */
+    tuple * outputrow;
+        /* The convolved row to be output */
+    unsigned int row;
+        /* Row number of the bottom of the current convolution window;
+           i.e. the row to be read or just read from the input file.
+        */
+
+    rowbuf = allocRowbuf(outpamP, convKernelP->rows);
+    MALLOCARRAY_NOFAIL(circMap, convKernelP->rows);
+    outputrow = pnm_allocpamrow(outpamP);
+
+    pnm_writepaminit(outpamP);
+
+    assert(convKernelP->rows > 0);
+
+    readAndScaleRows(inpamP, convKernelP->rows - 1, rowbuf,
+                      outpamP->maxval, outpamP->depth);
+
+    writeUnconvolvedTop(outpamP, convKernelP, rowbuf);
+
+    /* Now the rest of the image - read in the row at the bottom of the
+       window, then convolve and write out the row in the middle of the
+       window.
     */
-    temprow = (toprow + 1) % crowsp1;
-    i = 0;
-    for (irow = temprow; irow < crowsp1; ++i, ++irow)
-        rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        rowptr[i] = xelbuf[irow];
-
-    risum = 0L;
-    gisum = 0L;
-    bisum = 0L;
-    for ( col = 0; col < cols; ++col )
-        {
-        if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-        else if ( col == ccolso2 )
-        {
-        leftcol = col - ccolso2;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            {
-            tempcol = leftcol + ccol;
-            rcolumnsum[tempcol] = rcolumnsum[tempcol]
-            - PPM_GETR( rowptr[subrow][ccol] )
-            + PPM_GETR( rowptr[addrow][ccol] );
-            risum += rcolumnsum[tempcol];
-            gcolumnsum[tempcol] = gcolumnsum[tempcol]
-            - PPM_GETG( rowptr[subrow][ccol] )
-            + PPM_GETG( rowptr[addrow][ccol] );
-            gisum += gcolumnsum[tempcol];
-            bcolumnsum[tempcol] = bcolumnsum[tempcol]
-            - PPM_GETB( rowptr[subrow][ccol] )
-            + PPM_GETB( rowptr[addrow][ccol] );
-            bisum += bcolumnsum[tempcol];
-            }
-        temprsum = (float) risum * rmeanweight + 0.5;
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        tempbsum = (float) bisum * bmeanweight + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
-        }
-        else
-        {
-        /* Column numbers to subtract or add to isum */
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;  
-        rcolumnsum[addcol] = rcolumnsum[addcol]
-            - PPM_GETR( rowptr[subrow][addcol] )
-            + PPM_GETR( rowptr[addrow][addcol] );
-        risum = risum - rcolumnsum[subcol] + rcolumnsum[addcol];
-        gcolumnsum[addcol] = gcolumnsum[addcol]
-            - PPM_GETG( rowptr[subrow][addcol] )
-            + PPM_GETG( rowptr[addrow][addcol] );
-        gisum = gisum - gcolumnsum[subcol] + gcolumnsum[addcol];
-        bcolumnsum[addcol] = bcolumnsum[addcol]
-            - PPM_GETB( rowptr[subrow][addcol] )
-            + PPM_GETB( rowptr[addrow][addcol] );
-        bisum = bisum - bcolumnsum[subcol] + bcolumnsum[addcol];
-        temprsum = (float) risum * rmeanweight + 0.5;
-        tempgsum = (float) gisum * gmeanweight + 0.5;
-        tempbsum = (float) bisum * bmeanweight + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
-        }
-        }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
+    for (row = convKernelP->rows - 1; row < inpamP->height; ++row) {
+        unsigned int const rowbufRow = row % convKernelP->rows;
+
+        unsigned int plane;
+
+        setupCircMap(circMap, rowbuf, convKernelP->rows,
+                     (row + 1) % convKernelP->rows);
+
+        readAndScaleRow(inpamP, rowbuf[rowbufRow],
+                        outpamP->maxval, outpamP->depth);
+
+        for (plane = 0; plane < outpamP->depth; ++plane)
+            convolveGeneralRowPlane(outpamP, circMap, convKernelP, plane,
+                                    outputrow);
+
+        pnm_writepamrow(outpamP, outputrow);
     }
+    writeUnconvolvedBottom(outpamP, convKernelP, convKernelP->rows, circMap);
+
+    freeRowbuf(rowbuf, convKernelP->rows);
+}
+
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for ( irow = crowso2 + 1; irow < crows; ++irow )
-    pnm_writepnmrow(
-            stdout, rowptr[irow], cols, maxval, newformat, 0 );
 
+static sample **
+allocSum(unsigned int const depth,
+         unsigned int const size) {
+
+    sample ** sum;
+
+    MALLOCARRAY(sum, depth);
+
+    if (!sum)
+        pm_error("Could not allocate memory for %u planes of sums", depth);
+    else {
+        unsigned int plane;
+
+        for (plane = 0; plane < depth; ++plane) {
+            MALLOCARRAY(sum[plane], size);
+            
+            if (!sum[plane])
+                pm_error("Could not allocate memory for %u sums", size);
+        }
     }
+    return sum;
+}
 
 
-/* PPM Horizontal Convolution
-**
-** Same as pgm_horizontal_convolve()
-**
-**/
 
 static void
-ppm_horizontal_convolve(const float ** const rweights,
-                        const float ** const gweights,
-                        const float ** const bweights) {
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval r, g, b;
-    int row, crow;
-    xel **rowptr, *temprptr;
-    int leftcol;
-    int i, irow;
-    int temprow;
-    int subcol, addcol;
-    float rsum, gsum, bsum;
-    int addrow, subrow;
-    long **rrowsum, **rrowsumptr;
-    long **growsum, **growsumptr;
-    long **browsum, **browsumptr;
-    int crowsp1;
-    long temprsum, tempgsum, tempbsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer. */
-    xelbuf = pnm_allocarray( cols, crows + 1 );
-    outputrow = pnm_allocrow( cols );
-
-    /* Allocate array of pointers to xelbuf */
-    rowptr = (xel **) pnm_allocarray( 1, crows + 1);
-
-    /* Allocate intermediate row sums.  HORIZONTAL uses an extra row */
-    rrowsum = (long **) pm_allocarray( cols, crows + 1, sizeof(long) );
-    rrowsumptr = (long **) pnm_allocarray( 1, crows + 1);
-    growsum = (long **) pm_allocarray( cols, crows + 1, sizeof(long) );
-    growsumptr = (long **) pnm_allocarray( 1, crows + 1);
-    browsum = (long **) pm_allocarray( cols, crows + 1, sizeof(long) );
-    browsumptr = (long **) pnm_allocarray( 1, crows + 1);
-
-    pnm_writepnminit( stdout, cols, rows, maxval, newformat, 0 );
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for ( row = 0; row < crows - 1; ++row )
-    {
-    pnm_readpnmrow( ifp, xelbuf[row], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[row], cols, maxval, format, maxval, newformat );
-    /* Write out just the part we're not going to convolve. */
-    if ( row < crowso2 )
-        pnm_writepnmrow( stdout, xelbuf[row], cols, maxval, newformat, 0 );
-    }
+freeSum(sample **    const sum,
+        unsigned int const depth) {
 
-    /* First row only */
-    temprow = row % crows;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-    pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
+    unsigned int plane;
 
-    temprow = (row + 1) % crows;
-    i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-    rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-    rowptr[i] = xelbuf[irow];
+    for (plane = 0; plane < depth; ++plane)
+        free(sum[plane]);
 
-    for ( crow = 0; crow < crows; ++crow )
-    {
-    rrowsumptr[crow] = rrowsum[crow];
-    growsumptr[crow] = growsum[crow];
-    browsumptr[crow] = browsum[crow];
+    free(sum);
+}
+
+
+
+static void
+computeInitialColumnSums(struct pam *              const pamP,
+                         tuple **                  const window,
+                         const struct ConvKernel * const convKernelP,
+                         sample **                 const convColumnSum) {
+/*----------------------------------------------------------------------------
+  Add up the sum of each column of window[][], whose rows are described
+  by *inpamP.  The window's height is that of tthe convolution kernel
+  *convKernelP.
+
+  Return it as convColumnSum[][].
+-----------------------------------------------------------------------------*/
+    unsigned int plane;
+
+    for (plane = 0; plane < pamP->depth; ++plane) {
+        unsigned int col;
+
+        for (col = 0; col < pamP->width; ++col) {
+            unsigned int row;
+            for (row = 0, convColumnSum[plane][col] = 0;
+                 row < convKernelP->rows;
+                 ++row)
+                convColumnSum[plane][col] += window[row][col][plane];
+        }            
     }
- 
-    for ( col = 0; col < cols; ++col )
-    {
-    if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-    else if ( col == ccolso2 )
-        {
-        leftcol = col - ccolso2;
-        rsum = 0.0;
-        gsum = 0.0;
-        bsum = 0.0;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        temprptr = rowptr[crow] + leftcol;
-        rrowsumptr[crow][leftcol] = 0L;
-        growsumptr[crow][leftcol] = 0L;
-        browsumptr[crow][leftcol] = 0L;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            {
-            rrowsumptr[crow][leftcol] += 
-                PPM_GETR( *(temprptr + ccol) );
-            growsumptr[crow][leftcol] += 
-                PPM_GETG( *(temprptr + ccol) );
-            browsumptr[crow][leftcol] += 
-                PPM_GETB( *(temprptr + ccol) );
+}
+
+
+
+static void
+convolveRowWithColumnSumsMean(const struct ConvKernel * const convKernelP,
+                              struct pam *              const pamP,
+                              tuple **                  const window,
+                              tuple *                   const outputrow,
+                              sample **                 const convColumnSum) {
+/*----------------------------------------------------------------------------
+  Convolve the rows in window[][] -- one convolution kernel's worth, where
+  window[0] is the top.  Put the result in outputrow[].
+
+  Use convColumnSum[][]: the sum of the pixels in each column over the
+  convolution window, where convColumnSum[P][C] is the sum for Plane P of
+  Column C.
+
+  Assume the convolution weight is the same everywhere within the convolution
+  matrix.  Ergo, we don't need any more information about the contents of a
+  column than the sum of its pixels.
+
+  Except that we need the individual input pixels for the edges (which can't
+  be convolved because the convolution window runs off the edge).
+-----------------------------------------------------------------------------*/
+    unsigned int plane;
+    
+    for (plane = 0; plane < pamP->depth; ++plane) {
+        unsigned int const crowso2 = convKernelP->rows / 2;
+        unsigned int const ccolso2 = convKernelP->cols / 2;
+        float const weight = convKernelP->weight[plane][0][0];
+
+        unsigned int col;
+        sample gisum;
+
+        for (col = 0, gisum = 0; col < pamP->width; ++col) {
+            if (col < ccolso2 || col >= pamP->width - ccolso2) {
+                /* The unconvolved left or right edge */
+                outputrow[col][plane] =
+                    clipSample(convKernelP->bias +
+                               window[crowso2][col][plane],
+                               pamP->maxval);
+            } else {
+                if (col == ccolso2) {
+                    unsigned int const leftcol = col - ccolso2;
+
+                    unsigned int ccol;
+
+                    for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                        gisum += convColumnSum[plane][leftcol + ccol];
+                } else {
+                    /* Column numbers to subtract or add to isum */
+                    unsigned int const subcol = col - ccolso2 - 1;
+                    unsigned int const addcol = col + ccolso2;  
+
+                    gisum -= convColumnSum[plane][subcol];
+                    gisum += convColumnSum[plane][addcol];
+                }
+                outputrow[col][plane] =
+                    makeSample(convKernelP->bias + gisum * weight,
+                               pamP->maxval);
             }
-        rsum += rrowsumptr[crow][leftcol] * rweights[crow][0];
-        gsum += growsumptr[crow][leftcol] * gweights[crow][0];
-        bsum += browsumptr[crow][leftcol] * bweights[crow][0];
-        }
-        temprsum = rsum + 0.5;
-        tempgsum = gsum + 0.5;
-        tempbsum = bsum + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
-        }
-    else
-        {
-        rsum = 0.0;
-        gsum = 0.0;
-        bsum = 0.0;
-        leftcol = col - ccolso2;
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;
-        for ( crow = 0; crow < crows; ++crow )
-        {
-        rrowsumptr[crow][leftcol] = rrowsumptr[crow][subcol]
-            - PPM_GETR( rowptr[crow][subcol] )
-            + PPM_GETR( rowptr[crow][addcol] );
-        rsum += rrowsumptr[crow][leftcol] * rweights[crow][0];
-        growsumptr[crow][leftcol] = growsumptr[crow][subcol]
-            - PPM_GETG( rowptr[crow][subcol] )
-            + PPM_GETG( rowptr[crow][addcol] );
-        gsum += growsumptr[crow][leftcol] * gweights[crow][0];
-        browsumptr[crow][leftcol] = browsumptr[crow][subcol]
-            - PPM_GETB( rowptr[crow][subcol] )
-            + PPM_GETB( rowptr[crow][addcol] );
-        bsum += browsumptr[crow][leftcol] * bweights[crow][0];
-        }
-        temprsum = rsum + 0.5;
-        tempgsum = gsum + 0.5;
-        tempbsum = bsum + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
         }
-        }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
+    }
+}
 
 
-    /* For all subsequent rows */
 
-    subrow = crows;
-    addrow = crows - 1;
-    crowsp1 = crows + 1;
-    ++row;
-    for ( ; row < rows; ++row )
-    {
-    temprow = row % crowsp1;
-    pnm_readpnmrow( ifp, xelbuf[temprow], cols, maxval, format );
-    if ( PNM_FORMAT_TYPE(format) != newformat )
-        pnm_promoteformatrow(
-        xelbuf[temprow], cols, maxval, format, maxval, newformat );
+static void
+convolveRowWithColumnSumsVertical(
+    const struct ConvKernel * const convKernelP,
+    struct pam *              const pamP,
+    tuple **                  const window,
+    tuple *                   const outputrow,
+    sample **                 const convColumnSum) {
+/*----------------------------------------------------------------------------
+  Convolve the rows in window[][] -- one convolution kernel's worth, where
+  window[0] is the top.  Put the result in outputrow[].
 
-    temprow = (row + 2) % crowsp1;
-    i = 0;
-    for (irow = temprow; irow < crowsp1; ++i, ++irow)
-        {
-        rowptr[i] = xelbuf[irow];
-        rrowsumptr[i] = rrowsum[irow];
-        growsumptr[i] = growsum[irow];
-        browsumptr[i] = browsum[irow];
-        }
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        {
-        rowptr[i] = xelbuf[irow];
-        rrowsumptr[i] = rrowsum[irow];
-        growsumptr[i] = growsum[irow];
-        browsumptr[i] = browsum[irow];
-        }
+  Use convColumnSum[][]: the sum of the pixels in each column over the
+  convolution window, where convColumnSum[P][C] is the sum for Plane P of
+  Column C.
 
-    for ( col = 0; col < cols; ++col )
-        {
-        if ( col < ccolso2 || col >= cols - ccolso2 )
-        outputrow[col] = rowptr[crowso2][col];
-        else if ( col == ccolso2 )
-        {
-        rsum = 0.0;
-        gsum = 0.0;
-        bsum = 0.0;
-        leftcol = col - ccolso2;
-        rrowsumptr[addrow][leftcol] = 0L;
-        growsumptr[addrow][leftcol] = 0L;
-        browsumptr[addrow][leftcol] = 0L;
-        for ( ccol = 0; ccol < ccols; ++ccol )
-            {
-            rrowsumptr[addrow][leftcol] += 
-            PPM_GETR( rowptr[addrow][leftcol + ccol] );
-            growsumptr[addrow][leftcol] += 
-            PPM_GETG( rowptr[addrow][leftcol + ccol] );
-            browsumptr[addrow][leftcol] += 
-            PPM_GETB( rowptr[addrow][leftcol + ccol] );
-            }
-        for ( crow = 0; crow < crows; ++crow )
-            {
-            rsum += rrowsumptr[crow][leftcol] * rweights[crow][0];
-            gsum += growsumptr[crow][leftcol] * gweights[crow][0];
-            bsum += browsumptr[crow][leftcol] * bweights[crow][0];
+  Assume the convolution weight is the same everywhere within a column.  Ergo,
+  we don't need any more information about the contents of a column than the
+  sum of its pixels.
+
+  Except that we need the individual input pixels for the edges (which can't
+  be convolved because the convolution window runs off the edge).
+-----------------------------------------------------------------------------*/
+    unsigned int const crowso2 = convKernelP->rows / 2;
+    unsigned int const ccolso2 = convKernelP->cols / 2;
+
+    unsigned int plane;
+
+    for (plane = 0; plane < pamP->depth; ++plane) {
+        unsigned int col;
+    
+        for (col = 0; col < pamP->width; ++col) {
+            if (col < ccolso2 || col >= pamP->width - ccolso2) {
+                /* The unconvolved left or right edge */
+                outputrow[col][plane] =
+                    clipSample(convKernelP->bias +
+                               window[crowso2][col][plane],
+                               pamP->maxval);
+            } else {
+                unsigned int const leftcol = col - ccolso2;
+                unsigned int ccol;
+                float sum;
+
+                sum = 0.0;
+
+                for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                    sum += convColumnSum[plane][leftcol + ccol] *
+                        convKernelP->weight[plane][0][ccol];
+
+                outputrow[col][plane] =
+                    makeSample(convKernelP->bias + sum, pamP->maxval);
             }
-        temprsum = rsum + 0.5;
-        tempgsum = gsum + 0.5;
-        tempbsum = bsum + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
         }
-        else
-        {
-        rsum = 0.0;
-        gsum = 0.0;
-        bsum = 0.0;
-        leftcol = col - ccolso2;
-        subcol = col - ccolso2 - 1;
-        addcol = col + ccolso2;  
-        rrowsumptr[addrow][leftcol] = rrowsumptr[addrow][subcol]
-            - PPM_GETR( rowptr[addrow][subcol] )
-            + PPM_GETR( rowptr[addrow][addcol] );
-        growsumptr[addrow][leftcol] = growsumptr[addrow][subcol]
-            - PPM_GETG( rowptr[addrow][subcol] )
-            + PPM_GETG( rowptr[addrow][addcol] );
-        browsumptr[addrow][leftcol] = browsumptr[addrow][subcol]
-            - PPM_GETB( rowptr[addrow][subcol] )
-            + PPM_GETB( rowptr[addrow][addcol] );
-        for ( crow = 0; crow < crows; ++crow )
-            {
-            rsum += rrowsumptr[crow][leftcol] * rweights[crow][0];
-            gsum += growsumptr[crow][leftcol] * gweights[crow][0];
-            bsum += browsumptr[crow][leftcol] * bweights[crow][0];
+    }
+}
+
+
+
+static void
+convolveMeanRowPlane(struct pam *              const pamP,
+                     tuple **                  const window,
+                     const struct ConvKernel * const convKernelP,
+                     unsigned int              const plane,
+                     tuple *                   const outputrow,
+                     sample *                  const convColumnSum) {
+/*----------------------------------------------------------------------------
+  Convolve plane 'plane' of one row of the image.  window[] is a vertical
+  window of the input image, one convolution kernel plus one row high.  The
+  top row (window[0] is the row that just passed out of the convolution
+  window, whereas the bottom row is the row that just entered it.
+
+  *pamP describes the tuple rows in window[] and also 'outputrow' (they are
+  the same).
+
+  Return the convolution result as outputrow[].
+
+  We update convColumnSum[] for use in convolving later rows.
+-----------------------------------------------------------------------------*/
+    unsigned int const crowso2 = convKernelP->rows / 2;
+    unsigned int const ccolso2 = convKernelP->cols / 2;
+    float const weight = convKernelP->weight[plane][0][0];
+    unsigned int const subrow = 0;
+        /* Row just above convolution window -- what we subtract from
+           running sum
+        */
+    unsigned int const addrow = 1 + (convKernelP->rows - 1);
+        /* Bottom row of convolution window: What we add to running sum */
+
+    unsigned int col;
+    sample gisum;
+
+    for (col = 0, gisum = 0; col < pamP->width; ++col) {
+        if (col < ccolso2 || col >= pamP->width - ccolso2) {
+            /* The unconvolved left or right edge */
+            outputrow[col][plane] =
+                clipSample(convKernelP->bias + window[crowso2][col][plane],
+                           pamP->maxval);
+        } else {
+            if (col == ccolso2) {
+                unsigned int const leftcol = col - ccolso2;
+
+                unsigned int ccol;
+
+                for (ccol = 0; ccol < convKernelP->cols; ++ccol) {
+                    sample * const thisColumnSumP =
+                        &convColumnSum[leftcol + ccol];
+                    *thisColumnSumP = *thisColumnSumP
+                        - window[subrow][ccol][plane]
+                        + window[addrow][ccol][plane];
+                    gisum += *thisColumnSumP;
+                }
+            } else {
+                /* Column numbers to subtract or add to isum */
+                unsigned int const subcol = col - ccolso2 - 1;
+                unsigned int const addcol = col + ccolso2;  
+                
+                convColumnSum[addcol] = convColumnSum[addcol]
+                    - window[subrow][addcol][plane]
+                    + window[addrow][addcol][plane];
+                
+                gisum = gisum - convColumnSum[subcol] + convColumnSum[addcol];
             }
-        temprsum = rsum + 0.5;
-        tempgsum = gsum + 0.5;
-        tempbsum = bsum + 0.5;
-        CHECK_RED;
-        CHECK_GREEN;
-        CHECK_BLUE;
-        PPM_ASSIGN( outputrow[col], r, g, b );
-        }
+            outputrow[col][plane] =
+                makeSample(convKernelP->bias + gisum * weight, pamP->maxval);
         }
-    pnm_writepnmrow( stdout, outputrow, cols, maxval, newformat, 0 );
     }
+}
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for ( irow = crowso2 + 1; irow < crows; ++irow )
-    pnm_writepnmrow(
-            stdout, rowptr[irow], cols, maxval, newformat, 0 );
 
-    }
 
+typedef void convolver(struct pam *              const inpamP,
+                       struct pam *              const outpamP,
+                       const struct ConvKernel * const convKernelP);
 
-/* PPM Vertical Convolution
-**
-** Same as pgm_vertical_convolve()
-**
-*/
+
+
+static convolver convolveMean;
 
 static void
-ppm_vertical_convolve(const float ** const rweights,
-                      const float ** const gweights,
-                      const float ** const bweights) {
-    int ccol, col;
-    xel** xelbuf;
-    xel* outputrow;
-    xelval r, g, b;
-    int row, crow;
-    xel **rowptr, *temprptr;
-    int i, irow;
-    int toprow, temprow;
-    int subrow, addrow;
-    int tempcol;
-    long *rcolumnsum, *gcolumnsum, *bcolumnsum;
-    int crowsp1;
-    int addcol;
-    long temprsum, tempgsum, tempbsum;
-
-    /* Allocate space for one convolution-matrix's worth of rows, plus
-    ** a row output buffer. VERTICAL uses an extra row. */
-    xelbuf = pnm_allocarray(cols, crows + 1);
-    outputrow = pnm_allocrow(cols);
-
-    /* Allocate array of pointers to xelbuf */
-    rowptr = (xel **) pnm_allocarray(1, crows + 1);
-
-    /* Allocate space for intermediate column sums */
-    MALLOCARRAY_NOFAIL(rcolumnsum, cols);
-    MALLOCARRAY_NOFAIL(gcolumnsum, cols);
-    MALLOCARRAY_NOFAIL(bcolumnsum, cols);
-
-    for (col = 0; col < cols; ++col) {
-        rcolumnsum[col] = 0L;
-        gcolumnsum[col] = 0L;
-        bcolumnsum[col] = 0L;
-    }
+convolveMean(struct pam *              const inpamP,
+             struct pam *              const outpamP,
+             const struct ConvKernel * const convKernelP) {
+/*----------------------------------------------------------------------------
+  Mean Convolution
+
+  This is for the common case where you just want the target pixel replaced
+  with the average value of its neighbors.  This can work much faster than the
+  general case because you can reduce the number of floating point operations
+  that are required since all the weights are the same.  You will only need to
+  multiply by the weight once, not for every pixel in the convolution matrix.
+
+  This algorithm works as follows: At a certain vertical position in the
+  image, create sums for each column fragment of the convolution height all
+  the way across the image.  Then add those sums across the convolution width
+  to obtain the total sum over the convolution area and multiply that sum by
+  the weight.  As you move left to right, to calculate the next output pixel,
+  take the total sum you just generated, add in the value of the next column
+  and subtract the value of the leftmost column.  Multiply that by the weight
+  and that's it.  As you move down a row, calculate new column sums by using
+  previous sum for that column and adding in pixel on current row and
+  subtracting pixel in top row.
+
+  We assume the convolution kernel is uniform -- same weights everywhere.
+
+  We assume the output is PGM and the input is PGM or PBM.
+-----------------------------------------------------------------------------*/
+    unsigned int const windowHeight = convKernelP->rows + 1;
+        /* The height of the window we keep in the row buffer.  The buffer
+           contains the rows covered by the convolution kernel, plus the row
+           immediately above that.  The latter is there because to compute
+           the sliding mean, we need to subtract off the row that the
+           convolution kernel just slid past.
+        */
+    unsigned int const crowso2 = convKernelP->rows / 2;
+        /* Number of rows of the convolution window above/below the current
+           row.  Note that the convolution window is always an odd number
+           of rows, so this rounds down.
+        */
+    tuple ** rowbuf;
+        /* Same as in convolveGeneral */
+    tuple ** circMap;
+        /* Same as in convolveGeneral */
+    tuple * outputrow;
+        /* Same as in convolveGeneral */
+    unsigned int row;
+        /* Row number of the row currently being convolved; i.e. the row
+           at the center of the current convolution window and the row of
+           the output file to be output next.
+        */
+    sample ** convColumnSum;  /* Malloc'd */
+        /* convColumnSum[plane][col] is the sum of Plane 'plane' of all the
+           pixels in the Column 'col' of the image within the current vertical
+           convolution window.  I.e. if our convolution kernel is 5 rows high
+           and we're now looking at Rows 10-15, convColumn[0][3] is the sum of
+           Plane 0 of Column 3, Rows 10-15.
+        */
 
-    pnm_writepnminit(stdout, cols, rows, maxval, newformat, 0);
-
-    /* Read in one convolution-matrix's worth of image, less one row. */
-    for (row = 0; row < crows - 1; ++row) {
-        pnm_readpnmrow(ifp, xelbuf[row], cols, maxval, format);
-        if (PNM_FORMAT_TYPE(format) != newformat)
-            pnm_promoteformatrow(xelbuf[row], cols, maxval, format, 
-                                 maxval, newformat);
-        /* Write out just the part we're not going to convolve. */
-        if (row < crowso2)
-            pnm_writepnmrow(stdout, xelbuf[row], cols, maxval, newformat, 0);
-    }
+    rowbuf = allocRowbuf(outpamP, windowHeight);
+    MALLOCARRAY_NOFAIL(circMap, windowHeight);
+    outputrow = pnm_allocpamrow(outpamP);
 
-    /* Now the rest of the image - read in the row at the end of
-    ** xelbuf, and convolve and write out the row in the middle.
-    */
-    /* For first row only */
+    convColumnSum = allocSum(outpamP->depth, outpamP->width);
+
+    pnm_writepaminit(outpamP);
+
+    readAndScaleRows(inpamP, convKernelP->rows, rowbuf,
+                      outpamP->maxval, outpamP->depth);
 
-    toprow = row + 1;
-    temprow = row % crows;
-    pnm_readpnmrow(ifp, xelbuf[temprow], cols, maxval, format);
-    if (PNM_FORMAT_TYPE(format) != newformat)
-        pnm_promoteformatrow(xelbuf[temprow], cols, maxval, format, maxval, 
-                             newformat);
+    writeUnconvolvedTop(outpamP, convKernelP, rowbuf);
 
-    /* Arrange rowptr to eliminate the use of mod function to determine
-    ** which row of xelbuf is 0...crows.  Mod function can be very costly.
+    setupCircMap(circMap, rowbuf, windowHeight, 0);
+
+    /* Convolve the first window the long way */
+    computeInitialColumnSums(inpamP, circMap, convKernelP, convColumnSum);
+
+    convolveRowWithColumnSumsMean(convKernelP, outpamP, circMap,
+                                  outputrow, convColumnSum);
+
+    pnm_writepamrow(outpamP, outputrow);
+
+    /* For all subsequent rows do it this way as the columnsums have been
+       generated.  Now we can use them to reduce further calculations.  We
+       slide the window down a row at a time by reading a row into the bottom
+       of the circular buffer, adding it to the column sums, then subtracting
+       out the row at the top of the circular buffer.
     */
-    temprow = toprow % crows;
-    i = 0;
-    for (irow = temprow; irow < crows; ++i, ++irow)
-        rowptr[i] = xelbuf[irow];
-    for (irow = 0; irow < temprow; ++irow, ++i)
-        rowptr[i] = xelbuf[irow];
-
-    for (col = 0; col < cols; ++col) {
-        if (col < ccolso2 || col >= cols - ccolso2)
-            outputrow[col] = rowptr[crowso2][col];
-        else if (col == ccolso2) {
-            int const leftcol = col - ccolso2;
-            float rsum, gsum, bsum;
-            rsum = 0.0;
-            gsum = 0.0;
-            bsum = 0.0;
-            for (crow = 0; crow < crows; ++crow) {
-                temprptr = rowptr[crow] + leftcol;
-                for (ccol = 0; ccol < ccols; ++ccol) {
-                    rcolumnsum[leftcol + ccol] += 
-                        PPM_GETR(*(temprptr + ccol));
-                    gcolumnsum[leftcol + ccol] += 
-                        PPM_GETG(*(temprptr + ccol));
-                    bcolumnsum[leftcol + ccol] += 
-                        PPM_GETB(*(temprptr + ccol));
+    for (row = crowso2 + 1; row < inpamP->height - crowso2; ++row) {
+        unsigned int const windowBotRow = row + crowso2;
+            /* Row number of bottom-most row present in rowbuf[],
+               which is the bottom of the convolution window for the current
+               row.
+            */
+        unsigned int const windowTopRow = row - crowso2 - 1;
+            /* Row number of top-most row present in rowbuf[], which is
+               the top row of the convolution window for the previous row:
+               just above the convolution window for the current row.
+            */
+        unsigned int plane;
+
+        readAndScaleRow(inpamP, rowbuf[windowBotRow % windowHeight],
+                        outpamP->maxval, outpamP->depth);
+
+        setupCircMap(circMap, rowbuf, windowHeight,
+                     windowTopRow % windowHeight);
+
+        for (plane = 0; plane < outpamP->depth; ++plane)
+            convolveMeanRowPlane(outpamP, circMap, convKernelP, plane,
+                                 outputrow, convColumnSum[plane]);
+
+        pnm_writepamrow(outpamP, outputrow);
+    }
+    writeUnconvolvedBottom(outpamP, convKernelP, windowHeight, circMap);
+
+    freeSum(convColumnSum, outpamP->depth);
+    freeRowbuf(rowbuf, windowHeight);
+}
+
+
+
+static sample ***
+allocRowSum(unsigned int const depth,
+            unsigned int const height,
+            unsigned int const width) {
+
+    sample *** sum;
+
+    MALLOCARRAY(sum, depth);
+
+    if (!sum)
+        pm_error("Could not allocate memory for %u planes of sums", depth);
+    else {
+        unsigned int plane;
+
+        for (plane = 0; plane < depth; ++plane) {
+            MALLOCARRAY(sum[plane], height);
+            
+            if (!sum[plane])
+                pm_error("Could not allocate memory for %u rows of sums",
+                         height);
+            else {
+                unsigned int row;
+
+                for (row = 0; row < height; ++row) {
+                    MALLOCARRAY(sum[plane][row], width);
+                    
+                    if (!sum[plane][row])
+                        pm_error("Could not allocate memory "
+                                 "for a row of sums");
                 }
             }
-            for (ccol = 0; ccol < ccols; ++ccol) {
-                rsum += rcolumnsum[leftcol + ccol] * rweights[0][ccol];
-                gsum += gcolumnsum[leftcol + ccol] * gweights[0][ccol];
-                bsum += bcolumnsum[leftcol + ccol] * bweights[0][ccol];
-            }
-            temprsum = rsum + 0.5;
-            tempgsum = gsum + 0.5;
-            tempbsum = bsum + 0.5;
-            CHECK_RED;
-            CHECK_GREEN;
-            CHECK_BLUE;
-            PPM_ASSIGN(outputrow[col], r, g, b);
-        } else {
-            int const leftcol = col - ccolso2;
-            float rsum, gsum, bsum;
-            rsum = 0.0;
-            gsum = 0.0;
-            bsum = 0.0;
-            addcol = col + ccolso2;  
-            for (crow = 0; crow < crows; ++crow) {
-                rcolumnsum[addcol] += PPM_GETR( rowptr[crow][addcol]);
-                gcolumnsum[addcol] += PPM_GETG( rowptr[crow][addcol]);
-                bcolumnsum[addcol] += PPM_GETB( rowptr[crow][addcol]);
-            }
-            for (ccol = 0; ccol < ccols; ++ccol) {
-                rsum += rcolumnsum[leftcol + ccol] * rweights[0][ccol];
-                gsum += gcolumnsum[leftcol + ccol] * gweights[0][ccol];
-                bsum += bcolumnsum[leftcol + ccol] * bweights[0][ccol];
-            }
-            temprsum = rsum + 0.5;
-            tempgsum = gsum + 0.5;
-            tempbsum = bsum + 0.5;
-            CHECK_RED;
-            CHECK_GREEN;
-            CHECK_BLUE;
-            PPM_ASSIGN(outputrow[col], r, g, b);
         }
     }
-    pnm_writepnmrow(stdout, outputrow, cols, maxval, newformat, 0);
-    
-    /* For all subsequent rows */
-    subrow = crows;
-    addrow = crows - 1;
-    crowsp1 = crows + 1;
-    ++row;
-    for (; row < rows; ++row) {
-        toprow = row + 1;
-        temprow = row % (crows +1);
-        pnm_readpnmrow(ifp, xelbuf[temprow], cols, maxval, format);
-        if (PNM_FORMAT_TYPE(format) != newformat)
-            pnm_promoteformatrow(xelbuf[temprow], cols, maxval, format, 
-                                 maxval, newformat);
-
-        /* Arrange rowptr to eliminate the use of mod function to determine
-        ** which row of xelbuf is 0...crows.  Mod function can be very costly.
-        */
-        temprow = (toprow + 1) % crowsp1;
-        i = 0;
-        for (irow = temprow; irow < crowsp1; ++i, ++irow)
-            rowptr[i] = xelbuf[irow];
-        for (irow = 0; irow < temprow; ++irow, ++i)
-            rowptr[i] = xelbuf[irow];
-
-        for (col = 0; col < cols; ++col) {
-            if (col < ccolso2 || col >= cols - ccolso2)
-                outputrow[col] = rowptr[crowso2][col];
-            else if (col == ccolso2) {
-                int const leftcol = col - ccolso2;
-                float rsum, gsum, bsum;
-                rsum = 0.0;
-                gsum = 0.0;
-                bsum = 0.0;
-
-                for (ccol = 0; ccol < ccols; ++ccol) {
-                    tempcol = leftcol + ccol;
-                    rcolumnsum[tempcol] = rcolumnsum[tempcol] 
-                        - PPM_GETR(rowptr[subrow][ccol])
-                        + PPM_GETR(rowptr[addrow][ccol]);
-                    rsum = rsum + rcolumnsum[tempcol] * rweights[0][ccol];
-                    gcolumnsum[tempcol] = gcolumnsum[tempcol] 
-                        - PPM_GETG(rowptr[subrow][ccol])
-                        + PPM_GETG(rowptr[addrow][ccol]);
-                    gsum = gsum + gcolumnsum[tempcol] * gweights[0][ccol];
-                    bcolumnsum[tempcol] = bcolumnsum[tempcol] 
-                        - PPM_GETB(rowptr[subrow][ccol])
-                        + PPM_GETB(rowptr[addrow][ccol]);
-                    bsum = bsum + bcolumnsum[tempcol] * bweights[0][ccol];
+    return sum;
+}
+
+
+
+static void
+freeRowSum(sample ***   const sum,
+           unsigned int const depth,
+           unsigned int const height) {
+
+    unsigned int plane;
+
+    for (plane = 0; plane < depth; ++plane) {
+        unsigned int row;
+
+        for (row = 0; row < height; ++row)
+            free(sum[plane][row]);
+
+        free(sum[plane]);
+    }
+    free(sum);
+}
+
+
+
+static void
+convolveHorizontalRowPlane0(struct pam *              const outpamP,
+                            tuple **                  const window,
+                            const struct ConvKernel * const convKernelP,
+                            unsigned int              const plane,
+                            tuple *                   const outputrow,
+                            sample **                 const sumWindow) {
+/*----------------------------------------------------------------------------
+   Convolve the first convolvable row and generate the row sums from scratch.
+   (For subsequent rows, Caller can just incrementally modify the row sums).
+-----------------------------------------------------------------------------*/
+    unsigned int const crowso2 = convKernelP->rows / 2;
+    unsigned int const ccolso2 = convKernelP->cols / 2;
+
+    unsigned int col;
+
+    for (col = 0; col < outpamP->width; ++col) {
+        if (col < ccolso2 || col >= outpamP->width - ccolso2) {
+            /* The unconvolved left or right edge */
+            outputrow[col][plane] =
+                clipSample(convKernelP->bias + window[crowso2][col][plane],
+                           outpamP->maxval);
+        } else {
+            float matrixSum;
+
+            if (col == ccolso2) {
+                /* This is the first column for which the entire convolution
+                   kernel fits within the image horizontally.  I.e. the window
+                   starts at the left edge of the image.
+                */
+                unsigned int const leftcol = 0;
+            
+                unsigned int crow;
+
+                for (crow = 0, matrixSum = 0.0;
+                     crow < convKernelP->rows;
+                     ++crow) {
+                    tuple * const tuplesInWindow = &window[crow][leftcol];
+
+                    unsigned int ccol;
+                
+                    sumWindow[crow][col] = 0;
+                    for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                        sumWindow[crow][col] += tuplesInWindow[ccol][plane];
+                    matrixSum +=
+                        sumWindow[crow][col] *
+                        convKernelP->weight[plane][crow][0];
                 }
-                temprsum = rsum + 0.5;
-                tempgsum = gsum + 0.5;
-                tempbsum = bsum + 0.5;
-                CHECK_RED;
-                CHECK_GREEN;
-                CHECK_BLUE;
-                PPM_ASSIGN(outputrow[col], r, g, b);
             } else {
-                int const leftcol = col - ccolso2;
-                float rsum, gsum, bsum;
-                rsum = 0.0;
-                gsum = 0.0;
-                bsum = 0.0;
-                addcol = col + ccolso2;
-                rcolumnsum[addcol] = rcolumnsum[addcol]
-                    - PPM_GETR(rowptr[subrow][addcol])
-                    + PPM_GETR(rowptr[addrow][addcol]);
-                gcolumnsum[addcol] = gcolumnsum[addcol]
-                    - PPM_GETG(rowptr[subrow][addcol])
-                    + PPM_GETG(rowptr[addrow][addcol]);
-                bcolumnsum[addcol] = bcolumnsum[addcol]
-                    - PPM_GETB(rowptr[subrow][addcol])
-                    + PPM_GETB(rowptr[addrow][addcol]);
-                for (ccol = 0; ccol < ccols; ++ccol) {
-                    rsum += rcolumnsum[leftcol + ccol] * rweights[0][ccol];
-                    gsum += gcolumnsum[leftcol + ccol] * gweights[0][ccol];
-                    bsum += bcolumnsum[leftcol + ccol] * bweights[0][ccol];
+                unsigned int const subcol  = col - ccolso2 - 1;
+                unsigned int const addcol  = col + ccolso2;
+
+                unsigned int crow;
+                
+                for (crow = 0, matrixSum = 0.0;
+                     crow < convKernelP->rows;
+                     ++crow) {
+                    sumWindow[crow][col] = sumWindow[crow][col-1] +
+                        + window[crow][addcol][plane]
+                        - window[crow][subcol][plane];
+                    matrixSum +=
+                        sumWindow[crow][col] *
+                        convKernelP->weight[plane][crow][0];
                 }
-                temprsum = rsum + 0.5;
-                tempgsum = gsum + 0.5;
-                tempbsum = bsum + 0.5;
-                CHECK_RED;
-                CHECK_GREEN;
-                CHECK_BLUE;
-                PPM_ASSIGN(outputrow[col], r, g, b);
             }
+            outputrow[col][plane] =
+                makeSample(convKernelP->bias + matrixSum, outpamP->maxval);
         }
-        pnm_writepnmrow(stdout, outputrow, cols, maxval, newformat, 0);
     }
+}
+
 
-    /* Now write out the remaining unconvolved rows in xelbuf. */
-    for (irow = crowso2 + 1; irow < crows; ++irow)
-        pnm_writepnmrow(stdout, rowptr[irow], cols, maxval, newformat, 0);
 
+static void
+setupCircMap2(tuple **     const rowbuf,
+              sample **    const convRowSum,
+              tuple **     const circMap,
+              sample **    const sumCircMap,
+              unsigned int const windowTopRow,
+              unsigned int const windowHeight) {
+
+    unsigned int const toprow = windowTopRow % windowHeight;
+    
+    unsigned int crow;
+    unsigned int i;
+
+
+    i = 0;
+
+    for (crow = toprow; crow < windowHeight; ++i, ++crow) {
+        circMap[i] = rowbuf[crow];
+        sumCircMap[i] = convRowSum[crow];
+    }
+    for (crow = 0; crow < toprow; ++crow, ++i) {
+        circMap[i] = rowbuf[crow];
+        sumCircMap[i] = convRowSum[crow];
+    }
 }
 
 
 
 static void
-determineConvolveType(xel * const *         const cxels,
-                      struct convolveType * const typeP) {
+convolveHorizontalRowPlane(struct pam *              const pamP,
+                           tuple **                  const window,
+                           const struct ConvKernel * const convKernelP,
+                           unsigned int              const plane,
+                           tuple *                   const outputrow,
+                           sample **                 const sumWindow) {
 /*----------------------------------------------------------------------------
-   Determine which form of convolution is best.  The general form always
-   works, but with some special case convolution matrices, faster forms
-   of convolution are possible.
-
-   We don't check for the case that one of the PPM colors can have 
-   differing types.  We handle only cases where all PPMs are of the same
-   special case.
+   Convolve the row at the center of the convolution window described
+   by *convKernelP, where window[][] contains the input image tuples
+   for the window.  *pamP describes the rows in it, but its height is
+   one convolution window.
+
+   Convolve only the Plane 'plane' samples.
+
+   sumWindow[][] mirrors window[].  sumWindow[R] applies to window[R].
+   sumWindow[R][C] is the sum of samples in row R of the convolution window
+   centered on Column C.  We assume the convolution weights are the same
+   everywhere within a row of the kernel, so that we can generate these
+   sums incrementally, moving to the right through the image.
 -----------------------------------------------------------------------------*/
-    int horizontal, vertical;
-    int tempcxel, rtempcxel, gtempcxel, btempcxel;
-    int crow, ccol;
-
-    switch (PNM_FORMAT_TYPE(format)) {
-    case PPM_TYPE:
-        horizontal = TRUE;  /* initial assumption */
-        crow = 0;
-        while (horizontal && (crow < crows)) {
-            ccol = 1;
-            rtempcxel = PPM_GETR(cxels[crow][0]);
-            gtempcxel = PPM_GETG(cxels[crow][0]);
-            btempcxel = PPM_GETB(cxels[crow][0]);
-            while (horizontal && (ccol < ccols)) {
-                if ((PPM_GETR(cxels[crow][ccol]) != rtempcxel) ||
-                    (PPM_GETG(cxels[crow][ccol]) != gtempcxel) ||
-                    (PPM_GETB(cxels[crow][ccol]) != btempcxel)) 
-                    horizontal = FALSE;
-                ++ccol;
-            }
-            ++crow;
-        }
+    unsigned int const ccolso2 = convKernelP->cols / 2;
+    unsigned int const crowso2 = convKernelP->rows / 2;
 
-        vertical = TRUE;   /* initial assumption */
-        ccol = 0;
-        while (vertical && (ccol < ccols)) {
-            crow = 1;
-            rtempcxel = PPM_GETR(cxels[0][ccol]);
-            gtempcxel = PPM_GETG(cxels[0][ccol]);
-            btempcxel = PPM_GETB(cxels[0][ccol]);
-            while (vertical && (crow < crows)) {
-                if ((PPM_GETR(cxels[crow][ccol]) != rtempcxel) |
-                    (PPM_GETG(cxels[crow][ccol]) != gtempcxel) |
-                    (PPM_GETB(cxels[crow][ccol]) != btempcxel))
-                    vertical = FALSE;
-                ++crow;
+    unsigned int const newrow  = convKernelP->rows - 1;
+
+    unsigned int col;
+
+    for (col = 0; col < pamP->width; ++col) {
+        float matrixSum;
+
+        if (col < ccolso2 || col >= pamP->width - ccolso2) {
+            outputrow[col][plane] =
+                clipSample(convKernelP->bias + window[crowso2][col][plane],
+                           pamP->maxval);
+        } else if (col == ccolso2) {
+            unsigned int const leftcol = 0;
+                /* Window is up againt left edge of image */
+
+            {
+                unsigned int ccol;
+                sumWindow[newrow][col] = 0;
+                for (ccol = 0; ccol < convKernelP->cols; ++ccol)
+                    sumWindow[newrow][col] +=
+                        window[newrow][leftcol + ccol][plane];
             }
-            ++ccol;
-        }
-        break;
-        
-    default:
-        horizontal = TRUE; /* initial assumption */
-        crow = 0;
-        while (horizontal && (crow < crows)) {
-            ccol = 1;
-            tempcxel = PNM_GET1(cxels[crow][0]);
-            while (horizontal && (ccol < ccols)) {
-                if (PNM_GET1(cxels[crow][ccol]) != tempcxel)
-                    horizontal = FALSE;
-                ++ccol;
+            {
+                unsigned int crow;
+                for (crow = 0, matrixSum = 0.0;
+                     crow < convKernelP->rows;
+                     ++crow) {
+                    matrixSum += sumWindow[crow][col] *
+                        convKernelP->weight[plane][crow][0];
+                }
             }
-            ++crow;
-        }
-        
-        vertical = TRUE;  /* initial assumption */
-        ccol = 0;
-        while (vertical && (ccol < ccols)) {
-            crow = 1;
-            tempcxel = PNM_GET1(cxels[0][ccol]);
-            while (vertical && (crow < crows)) {
-                if (PNM_GET1(cxels[crow][ccol]) != tempcxel)
-                    vertical = FALSE;
-                ++crow;
+        } else {
+            unsigned int const subcol  = col - ccolso2 - 1;
+            unsigned int const addcol  = col + ccolso2;  
+
+            unsigned int crow;
+
+            sumWindow[newrow][col] =
+                sumWindow[newrow][col-1]
+                + window[newrow][addcol][plane]
+                - window[newrow][subcol][plane];
+
+            for (crow = 0, matrixSum = 0.0; crow < convKernelP->rows; ++crow) {
+                matrixSum += sumWindow[crow][col] *
+                    convKernelP->weight[plane][crow][0];
             }
-            ++ccol;
         }
-        break;
+        outputrow[col][plane] =
+            makeSample(convKernelP->bias + matrixSum, pamP->maxval);
     }
-    
-    /* Which type do we have? */
-    if (horizontal && vertical) {
-        typeP->ppmConvolver = ppm_mean_convolve;
-        typeP->pgmConvolver = pgm_mean_convolve;
-    } else if (horizontal) {
-        typeP->ppmConvolver = ppm_horizontal_convolve;
-        typeP->pgmConvolver = pgm_horizontal_convolve;
-    } else if (vertical) {
-        typeP->ppmConvolver = ppm_vertical_convolve;
-        typeP->pgmConvolver = pgm_vertical_convolve;
-    } else {
-        typeP->ppmConvolver = ppm_general_convolve;
-        typeP->pgmConvolver = pgm_general_convolve;
+}
+
+
+
+static convolver convolveHorizontal;
+
+static void
+convolveHorizontal(struct pam *              const inpamP,
+                   struct pam *              const outpamP,
+                   const struct ConvKernel * const convKernelP) {
+/*----------------------------------------------------------------------------
+  Horizontal Convolution
+
+  Similar idea to using columnsums of the Mean and Vertical convolution, but
+  uses temporary sums of row values.  Need to multiply by weights once for
+  each row in the convolution kernel.  Each time we start a new line, we must
+  recalculate the initials rowsums for the newest row only.  Uses queue to
+  still access previous row sums.
+-----------------------------------------------------------------------------*/
+    unsigned int const crowso2 = convKernelP->rows / 2;
+        /* Same as in convolveMean */
+    unsigned int const windowHeight = convKernelP->rows;
+        /* Same as in convolveMean */
+
+    tuple ** rowbuf;
+        /* Same as in convolveGeneral */
+    tuple ** circMap;
+        /* Same as in convolveGeneral */
+    tuple * outputrow;
+        /* Same as in convolveGeneral */
+    unsigned int plane;
+    sample *** convRowSum;  /* Malloc'd */
+    sample ** sumCircMap;  /* Malloc'd */
+
+    rowbuf = allocRowbuf(inpamP, windowHeight);
+    MALLOCARRAY_NOFAIL(circMap, windowHeight);
+    outputrow = pnm_allocpamrow(outpamP);
+
+    convRowSum = allocRowSum(outpamP->depth, windowHeight, outpamP->width);
+    MALLOCARRAY_NOFAIL(sumCircMap, windowHeight);
+
+    pnm_writepaminit(outpamP);
+
+    readAndScaleRows(inpamP, convKernelP->rows, rowbuf,
+                      outpamP->maxval, outpamP->depth);
+
+    writeUnconvolvedTop(outpamP, convKernelP, rowbuf);
+
+    setupCircMap(circMap, rowbuf, windowHeight, 0);
+
+    /* Convolve the first convolvable row and generate convRowSum[][] */
+    for (plane = 0; plane < outpamP->depth; ++plane) {
+        unsigned int crow;
+
+        for (crow = 0; crow < convKernelP->rows; ++crow)
+            sumCircMap[crow] = convRowSum[plane][crow];
+ 
+        convolveHorizontalRowPlane0(outpamP, circMap, convKernelP, plane,
+                                    outputrow, sumCircMap);
     }
+    pnm_writepamrow(outpamP, outputrow);
+
+    /* Convolve the rest of the rows, using convRowSum[] */
+
+    for (plane = 0; plane < outpamP->depth; ++plane) {
+        unsigned int row;
+            /* Same as in convolveMean */
+
+        for (row = convKernelP->rows/2 + 1;
+             row < inpamP->height - convKernelP->rows/2;
+             ++row) {
+
+            unsigned int const windowBotRow = row + crowso2;
+            unsigned int const windowTopRow = row - crowso2;
+                /* Same as in convolveMean */
+
+            readAndScaleRow(inpamP, rowbuf[windowBotRow % windowHeight],
+                            outpamP->maxval, outpamP->depth);
+            
+            setupCircMap2(rowbuf, convRowSum[plane], circMap, sumCircMap,
+                          windowTopRow, windowHeight);
+
+            convolveHorizontalRowPlane(outpamP, circMap, convKernelP, plane,
+                                       outputrow, sumCircMap);
+            
+            pnm_writepamrow(outpamP, outputrow);
+        }
+    }
+
+    writeUnconvolvedBottom(outpamP, convKernelP, windowHeight, circMap);
+
+    freeRowSum(convRowSum, outpamP->depth, windowHeight);
+    freeRowbuf(rowbuf, windowHeight);
 }
 
 
 
 static void
-convolveIt(int                 const format,
-           struct convolveType const convolveType,
-           const float**       const rweights,
-           const float**       const gweights,
-           const float**       const bweights) {
-
-    switch (PNM_FORMAT_TYPE(format)) {
-    case PPM_TYPE:
-        convolveType.ppmConvolver(rweights, gweights, bweights);
-        break;
-
-    default:
-        convolveType.pgmConvolver(gweights);
+convolveVerticalRowPlane(struct pam *              const pamP,
+                         tuple **                  const circMap,
+                         const struct ConvKernel * const convKernelP,
+                         unsigned int              const plane,
+                         tuple *                   const outputrow,
+                         sample *                  const convColumnSum) {
+
+    unsigned int const crowso2 = convKernelP->rows / 2;
+    unsigned int const ccolso2 = convKernelP->cols / 2;
+
+    unsigned int const subrow = 0;
+        /* Row just above convolution window -- what we subtract from
+           running sum
+        */
+    unsigned int const addrow = 1 + (convKernelP->rows - 1);
+        /* Bottom row of convolution window: What we add to running sum */
+    
+    unsigned int col;
+
+    for (col = 0; col < pamP->width; ++col) {
+        if (col < ccolso2 || col >= pamP->width - ccolso2) {
+            /* The unconvolved left or right edge */
+            outputrow[col][plane] =
+                clipSample(convKernelP->bias + circMap[crowso2][col][plane],
+                           pamP->maxval);
+        } else {
+            float matrixSum;
+
+            if (col == ccolso2) {
+                unsigned int const leftcol = 0;
+                    /* Convolution window is againt left edge of image */
+
+                unsigned int ccol;
+
+                /* Slide window down in the first kernel's worth of columns */
+                for (ccol = 0; ccol < convKernelP->cols; ++ccol) {
+                    convColumnSum[leftcol + ccol] +=
+                        circMap[addrow][leftcol + ccol][plane];
+                    convColumnSum[leftcol + ccol] -=
+                        circMap[subrow][leftcol + ccol][plane];
+                }
+                for (ccol = 0, matrixSum = 0.0;
+                     ccol < convKernelP->cols;
+                     ++ccol) {
+                    matrixSum += convColumnSum[leftcol + ccol] *
+                        convKernelP->weight[plane][0][ccol];
+                }
+            } else {
+                unsigned int const leftcol = col - ccolso2;
+                unsigned int const addcol  = col + ccolso2;
+
+                unsigned int ccol;
+
+                /* Slide window down in the column that just entered the
+                   window
+                */
+                convColumnSum[addcol] += circMap[addrow][addcol][plane];
+                convColumnSum[addcol] -= circMap[subrow][addcol][plane];
+
+                for (ccol = 0, matrixSum = 0.0;
+                     ccol < convKernelP->cols;
+                     ++ccol) {
+                    matrixSum += convColumnSum[leftcol + ccol] *
+                        convKernelP->weight[plane][0][ccol];
+                }
+            }
+            outputrow[col][plane] =
+                makeSample(convKernelP->bias + matrixSum, pamP->maxval);
+        }
     }
 }
 
 
 
+static convolver convolveVertical;
+
 static void
-readKernel(const char * const fileName,
-           int *        const colsP,
-           int *        const rowsP,
-           xelval *     const maxvalP,
-           int *        const formatP,
-           xel ***      const xelsP) {
-/*----------------------------------------------------------------------------
-   Read in the pseudo-PNM that is the convolution matrix.
+convolveVertical(struct pam *              const inpamP,
+                 struct pam *              const outpamP,
+                 const struct ConvKernel * const convKernelP) {
+
+    /* Uses column sums as in mean convolution, above */
+
+    unsigned int const windowHeight = convKernelP->rows + 1;
+        /* Same as in convolveMean */
+    unsigned int const crowso2 = convKernelP->rows / 2;
+        /* Same as in convolveMean */
+
+    tuple ** rowbuf;
+        /* Same as in convolveGeneral */
+    tuple ** circMap;
+        /* Same as in convolveGeneral */
+    tuple * outputrow;
+        /* Same as in convolveGeneral */
+    unsigned int row;
+        /* Row number of next row to read in from the file */
+    sample ** convColumnSum;  /* Malloc'd */
+        /* Same as in convolveMean() */
 
-   This is essentially pnm_readpnm(), except that it can take sample values
-   that exceed the maxval, which is not legal in PNM.  That's why it's
-   psuedo-PNM and not true PNM.
------------------------------------------------------------------------------*/
+    rowbuf = allocRowbuf(inpamP, windowHeight);
+    MALLOCARRAY_NOFAIL(circMap, windowHeight);
+    outputrow = pnm_allocpamrow(outpamP);
 
-    /* pm_getuint() is supposed to be internal to libnetpbm, but since we're
-       doing this backward compatibility hack here, we use it anyway.
-    */
+    convColumnSum = allocSum(outpamP->depth, outpamP->width);
+
+    pnm_writepaminit(outpamP);
+
+    readAndScaleRows(inpamP, convKernelP->rows, rowbuf,
+                      outpamP->maxval, outpamP->depth);
+
+    writeUnconvolvedTop(outpamP, convKernelP, rowbuf);
+
+    setupCircMap(circMap, rowbuf, windowHeight, 0);
 
-    unsigned int
-    pm_getuint(FILE * const file);
+    /* Convolve the first window the long way */
+    computeInitialColumnSums(inpamP, circMap, convKernelP, convColumnSum);
+
+    convolveRowWithColumnSumsVertical(convKernelP, outpamP, circMap,
+                                      outputrow, convColumnSum);
+
+    pnm_writepamrow(outpamP, outputrow);
+
+    for (row = crowso2 + 1; row < inpamP->height - crowso2; ++row) {
+        unsigned int const windowBotRow = row + crowso2;
+            /* Same as in convolveMean */
+        unsigned int const windowTopRow = row - crowso2 - 1;
+            /* Same as in convolveMean */
+        unsigned int plane;
+
+        readAndScaleRow(inpamP, rowbuf[windowBotRow % windowHeight],
+                        outpamP->maxval, outpamP->depth);
+
+        /* Remember the window is one row higher than the convolution
+           kernel.  The top row in the window is not part of this convolution.
+        */
+
+        setupCircMap(circMap, rowbuf, windowHeight,
+                     windowTopRow % windowHeight);
+
+        for (plane = 0; plane < outpamP->depth; ++plane)
+            convolveVerticalRowPlane(outpamP, circMap, convKernelP, plane,
+                                     outputrow, convColumnSum[plane]);
+
+        pnm_writepamrow(outpamP, outputrow);
+    }
+    writeUnconvolvedBottom(outpamP, convKernelP, windowHeight, circMap);
+
+    freeSum(convColumnSum, outpamP->depth);
+    freeRowbuf(rowbuf, windowHeight);
+}
 
-    FILE * fileP;
-    xel ** xels;
-    int cols, rows;
-    xelval maxval;
-    int format;
+
+
+struct convolveType {
+    convolver * convolve;
+};
+
+
+
+static bool
+convolutionIncludesHorizontal(const struct ConvKernel * const convKernelP) {
+
+    bool horizontal;
     unsigned int row;
 
-    fileP = pm_openr(fileName);
+    for (row = 0, horizontal = TRUE;
+         row < convKernelP->rows && horizontal;
+        ++row) {
+        unsigned int col;
+        for (col = 1, horizontal = TRUE;
+             col < convKernelP->cols && horizontal;
+             ++col) {
+
+            unsigned int plane;
+
+            for (plane = 0; plane < convKernelP->planes; ++plane) {
+                if (convKernelP->weight[plane][row][col] !=
+                    convKernelP->weight[plane][row][0])
+                    horizontal = FALSE;
+            }
+        }
+    }
+    return horizontal;
+}
+
 
-    pnm_readpnminit(fileP, &cols, &rows, &maxval, &format);
 
-    xels = pnm_allocarray(cols, rows);
+static bool
+convolutionIncludesVertical(const struct ConvKernel * const convKernelP) {
 
-    for (row = 0; row < rows; ++row) {
-        if (format == PGM_FORMAT || format == PPM_FORMAT) {
-            /* Plain format -- can't use pnm_readpnmrow() because it will
-               reject a sample > maxval
-            */
-            unsigned int col;
-            for (col = 0; col < cols; ++col) {
-                switch (format) {
-                case PGM_FORMAT: {
-                    gray const g = pm_getuint(fileP);
-                    PNM_ASSIGN1(xels[row][col], g);
-                    } break;
-                case PPM_FORMAT: {
-                    pixval const r = pm_getuint(fileP);
-                    pixval const g = pm_getuint(fileP);
-                    pixval const b = pm_getuint(fileP);
-
-                    PNM_ASSIGN(xels[row][col], r, g, b);
-                } break;
-                default:
-                    assert(false);
-                }
+    bool vertical;
+    unsigned int col;
+
+    for (col = 0, vertical = TRUE;
+         col < convKernelP->cols && vertical;
+        ++col) {
+        unsigned int row;
+        for (row = 1, vertical = TRUE;
+             row < convKernelP->rows && vertical;
+             ++row) {
+
+            unsigned int plane;
+
+            for (plane = 0; plane < convKernelP->planes; ++plane) {
+                if (convKernelP->weight[plane][row][col] !=
+                    convKernelP->weight[plane][0][col])
+                    vertical = FALSE;
             }
-        } else {
-            /* Raw or PBM format -- pnm_readpnmrow() won't do any maxval
-               checking
-            */
-            pnm_readpnmrow(fileP, xels[row], cols, maxval, format);
         }
     }
-    *colsP   = cols;
-    *rowsP   = rows;
-    *maxvalP = maxval;
-    *formatP = format;
-    *xelsP   = xels;
+    return vertical;
+}
+
+
+
+static void
+determineConvolveType(const struct ConvKernel * const convKernelP,
+                      struct convolveType *     const typeP) {
+/*----------------------------------------------------------------------------
+   Determine which form of convolution is best to convolve the kernel
+   *convKernelP over tuples[][].  The general form always works, but with some
+   special case convolution matrices, faster forms of convolution are
+   possible.
+
+   We don't check for the case that the planes can have differing types.  We
+   handle only cases where all planes are of the same special case.
+-----------------------------------------------------------------------------*/
+    bool const horizontal = convolutionIncludesHorizontal(convKernelP);
+    bool const vertical   = convolutionIncludesVertical(convKernelP);
 
-    pm_close(fileP);
+    if (horizontal && vertical) {
+        pm_message("Convolution is a simple mean horizontally and vertically");
+        typeP->convolve = convolveMean;
+    } else if (horizontal) {
+        pm_message("Convolution is a simple mean horizontally");
+        typeP->convolve = convolveHorizontal;
+    } else if (vertical) {
+        pm_message("Convolution is a simple mean vertically");
+        typeP->convolve = convolveVertical;
+    } else {
+        typeP->convolve = convolveGeneral;
+    }
 }
 
 
@@ -1904,65 +2251,50 @@ int
 main(int argc, char * argv[]) {
 
     struct cmdlineInfo cmdline;
-    xel** cxels;
-    int cformat;
-    xelval cmaxval;
+    FILE * ifP;
     struct convolveType convolveType;
-    float ** rweights;
-    float ** gweights;
-    float ** bweights;
+    struct ConvKernel * convKernelP;
+    struct pam inpam;
+    struct pam outpam;
 
     pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    readKernel(cmdline.kernelFilespec,
-               &ccols, &crows, &cmaxval, &cformat, &cxels);
-
-    if (ccols % 2 != 1 || crows % 2 != 1)
-        pm_error("the convolution matrix must have an odd number of "
-                 "rows and columns" );
-
-    ccolso2 = ccols / 2;
-    crowso2 = crows / 2;
-
-    ifp = pm_openr(cmdline.inputFilespec);
-
-    pnm_readpnminit(ifp, &cols, &rows, &maxval, &format);
-    if (cols < ccols || rows < crows)
-        pm_error("the image is smaller than the convolution matrix" );
-
-    newformat = MAX(PNM_FORMAT_TYPE(cformat), PNM_FORMAT_TYPE(format));
-    if (PNM_FORMAT_TYPE(cformat) != newformat)
-        pnm_promoteformat(cxels, ccols, crows, cmaxval, cformat, 
-                          cmaxval, newformat);
-    if (PNM_FORMAT_TYPE(format) != newformat) {
-        switch (PNM_FORMAT_TYPE(newformat)) {
-        case PPM_TYPE:
-            if (PNM_FORMAT_TYPE(format) != newformat)
-                pm_message("promoting to PPM");
-            break;
-        case PGM_TYPE:
-            if (PNM_FORMAT_TYPE(format) != newformat)
-                pm_message("promoting to PGM");
-            break;
-        }
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
+
+    getKernel(cmdline, inpam.depth, &convKernelP);
+
+    outpam = inpam;  /* initial value */
+
+    outpam.file = stdout;
+
+    if ((PNM_FORMAT_TYPE(inpam.format) == PBM_TYPE ||
+         PNM_FORMAT_TYPE(inpam.format) == PGM_TYPE) &&
+        convKernelP->planes == 3) {
+
+        pm_message("promoting to PPM");
+        outpam.format = PPM_FORMAT;
     }
 
-    computeWeights(cxels, ccols, crows, newformat, cmaxval, !cmdline.nooffset,
-                   &rweights, &gweights, &bweights);
+    outpam.depth = MAX(inpam.depth, convKernelP->planes);
+
+    pnm_setminallocationdepth(&inpam, MAX(inpam.depth, outpam.depth));
+
+    validateEnoughImageToConvolve(&inpam, convKernelP);
 
     /* Handle certain special cases when runtime can be improved. */
 
-    determineConvolveType(cxels, &convolveType);
+    determineConvolveType(convKernelP, &convolveType);
 
-    convolveIt(format, convolveType, 
-               (const float **)rweights, 
-               (const float **)gweights, 
-               (const float **)bweights);
+    convolveType.convolve(&inpam, &outpam, convKernelP);
 
+    convKernelDestroy(convKernelP);
     pm_close(stdout);
-    pm_close(ifp);
+    pm_close(ifP);
+
     return 0;
 }
 
diff --git a/editor/pnmcrop.c b/editor/pnmcrop.c
index 1abfc7d4..c6aabff1 100644
--- a/editor/pnmcrop.c
+++ b/editor/pnmcrop.c
@@ -106,7 +106,7 @@ parseCommandLine(int argc, const char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
         
     free(option_def);
@@ -870,8 +870,8 @@ main(int argc, const char *argv[]) {
         */
     FILE * bdfP;
         /* The border file.  NULL if none. */
-    bool eof;    /* no more images in input stream */
-    bool beof;   /* no more images in borderfile stream */
+    int eof;    /* no more images in input stream */
+    int beof;   /* no more images in borderfile stream */
 
     pm_proginit(&argc, argv);
 
diff --git a/editor/pnmflip b/editor/pnmflip
index 44d95b45..07d4ddb9 100755
--- a/editor/pnmflip
+++ b/editor/pnmflip
@@ -1,5 +1,28 @@
-#!/usr/bin/perl -w
+#!/bin/sh
 
+##############################################################################
+# This is essentially a Perl program.  We exec the Perl interpreter specifying
+# this same file as the Perl program and use the -x option to cause the Perl
+# interpreter to skip down to the Perl code.  The reason we do this instead of
+# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is
+# that the user may have multiple Perl interpreters and the one he wants to
+# use is properly located in the PATH.  The user's choice of Perl interpreter
+# may be crucial, such as when the user also has a PERL5LIB environment
+# variable and it selects modules that work with only a certain main
+# interpreter program.
+#
+# An alternative some people use is to have /usr/bin/env as the script
+# interpreter.  We don't do that because we think the existence and
+# compatibility of /bin/sh is more reliable.
+#
+# Note that we aren't concerned about efficiency because the user who needs
+# high efficiency can use directly the programs that this program invokes.
+#
+##############################################################################
+
+exec perl -w -x -S -- "$0" "$@"
+
+#!/usr/bin/perl
 #============================================================================
 #  This is a compatibility interface to Pamflip.
 #
diff --git a/editor/pnmgamma.c b/editor/pnmgamma.c
index b079adf1..b357b0d8 100644
--- a/editor/pnmgamma.c
+++ b/editor/pnmgamma.c
@@ -10,6 +10,7 @@
 ** implied warranty.
 */
 
+#include <assert.h>
 #include <math.h>
 #include <ctype.h>
 
@@ -145,7 +146,7 @@ parseCommandLine(int argc, char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -190,7 +191,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = TRUE; 
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (bt709tolinear + lineartobt709 + bt709ramp + srgbramp +
@@ -308,6 +309,7 @@ buildPowGamma(xelval       table[],
         double const normalized = ((double) i) / maxval;
             /* Xel sample value normalized to 0..1 */
         double const v = pow(normalized, oneOverGamma);
+
         table[i] = MIN((xelval)(v * newMaxval + 0.5), newMaxval);  
             /* denormalize, round and clip */
     }
@@ -509,11 +511,15 @@ buildBt709ToSrgbGamma(xelval       table[],
         else
             radiance = pow((normalized + 0.099) / 1.099, gamma709);
 
+        assert(radiance <= 1.0);
+
         if (radiance < linearCutoffSrgb * normalizer)
             srgb = radiance * linearExpansionSrgb;
         else
             srgb = 1.055 * pow(normalized, oneOverGammaSrgb) - 0.055;
 
+        assert(srgb <= 1.0);
+
         table[i] = srgb * newMaxval + 0.5;
     }
 }
@@ -563,11 +569,15 @@ buildSrgbToBt709Gamma(xelval       table[],
         else
             radiance = pow((normalized + 0.099) / 1.099, gammaSrgb);
 
+        assert(radiance <= 1.0);
+
         if (radiance < linearCutoff709 * normalizer)
             bt709 = radiance * linearExpansion709;
         else
             bt709 = 1.055 * pow(normalized, oneOverGamma709) - 0.055;
 
+        assert(bt709 <= 1.0);
+
         table[i] = bt709 * newMaxval + 0.5;
     }
 }
diff --git a/editor/pnmhisteq.c b/editor/pnmhisteq.c
index 1987efc3..8af42019 100644
--- a/editor/pnmhisteq.c
+++ b/editor/pnmhisteq.c
@@ -24,6 +24,8 @@ struct cmdlineInfo {
     */
     const char * inputFileName;
     unsigned int gray;
+    unsigned int noblack;
+    unsigned int nowhite;
     const char * wmap;
     const char * rmap;
     unsigned int verbose;
@@ -39,7 +41,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -55,6 +57,10 @@ parseCommandLine(int argc, char ** argv,
             &wmapSpec,          0);
     OPTENT3(0, "gray",     OPT_FLAG,   NULL,
             &cmdlineP->gray,    0);
+    OPTENT3(0, "noblack",     OPT_FLAG,   NULL,
+            &cmdlineP->noblack,    0);
+    OPTENT3(0, "nowhite",     OPT_FLAG,   NULL,
+            &cmdlineP->nowhite,    0);
     OPTENT3(0, "verbose",  OPT_FLAG,   NULL,
             &cmdlineP->verbose, 0);
 
@@ -62,7 +68,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
@@ -91,8 +97,6 @@ computeLuminosityHistogram(xel * const *   const xels,
                            int             const format,
                            bool            const monoOnly,
                            unsigned int ** const lumahistP,
-                           xelval *        const lminP,
-                           xelval *        const lmaxP,
                            unsigned int *  const pixelCountP) {
 /*----------------------------------------------------------------------------
   Scan the image and build the luminosity histogram.  If the input is
@@ -144,7 +148,7 @@ computeLuminosityHistogram(xel * const *   const xels,
             for (col = 0; col < cols; ++col) {
                 xel const thisXel = xels[row][col];
                 if (!monoOnly || PPM_ISGRAY(thisXel)) {
-                    xelval const l = PPM_LUMIN(thisXel);
+                    xelval const l = ppm_luminosity(thisXel);
 
                     lmin = MIN(lmin, l);
                     lmax = MAX(lmax, l);
@@ -162,25 +166,6 @@ computeLuminosityHistogram(xel * const *   const xels,
 
     *lumahistP = lumahist;
     *pixelCountP = pixelCount;
-    *lminP = lmin;
-    *lmaxP = lmax;
-}
-
-
-
-static void
-findMaxLuma(const xelval * const lumahist,
-            xelval         const maxval,
-            xelval *       const maxLumaP) {
-
-    xelval maxluma;
-    unsigned int i;
-
-    for (i = 0, maxluma = 0; i <= maxval; ++i)
-        if (lumahist[i] > 0)
-            maxluma = i;
-
-    *maxLumaP = maxluma;
 }
 
 
@@ -216,53 +201,159 @@ readMapFile(const char * const rmapFileName,
 
 
 
+static xelval
+maxLumaPresent(const xelval * const lumahist,
+               xelval         const darkestCandidate,
+               xelval         const brightestCandidate) {
+/*----------------------------------------------------------------------------
+    The maximum luminosity in the image, in the range ['darkestCandidate',
+   'brightestCandidate'], given that the luminosity histogram for the image is
+   'lumahist' (lumahist[N] is the number of pixels in the image with
+   luminosity N).
+-----------------------------------------------------------------------------*/
+    xelval maxluma;
+    xelval i;
+
+    for (i = darkestCandidate, maxluma = darkestCandidate;
+         i <= brightestCandidate;
+         ++i) {
+
+        if (lumahist[i] > 0)
+            maxluma = i;
+    }
+    return maxluma;
+}
+
+
+
 static void
-computeMap(const unsigned int * const lumahist,
-           xelval               const maxval,
-           unsigned int         const pixelCount,
-           gray *               const lumamap) {
+equalize(const unsigned int * const lumahist,
+         xelval               const darkestRemap,
+         xelval               const brightestRemap,
+         unsigned int         const remapPixelCount,
+         gray *               const lumamap) {
+/*----------------------------------------------------------------------------
+   Fill in the mappings of luminosities from 'darkestRemap' through
+   'brightestRemap' in 'lumamap', to achieve an equalization based on the
+   histogram 'lumahist'.  lumahist[N] is the number of pixels in the original
+   image of luminosity N.
+
+   'remapPixelCount' is the number of pixels in the given luminosity range.
+   It is redundant with 'lumahist'; we get it for computational convenience.
+-----------------------------------------------------------------------------*/
+    xelval const maxluma =
+        maxLumaPresent(lumahist, darkestRemap, brightestRemap);
 
-    /* Calculate initial histogram equalization curve. */
+    unsigned int const range = brightestRemap - darkestRemap;
     
-    unsigned int i;
-    unsigned int pixsum;
-    xelval maxluma;
+    {
+        xelval origLum;
+        unsigned int pixsum;
 
-    for (i = 0, pixsum = 0; i <= maxval; ++i) {
+        for (origLum = darkestRemap, pixsum = 0;
+             origLum <= brightestRemap;
+             ++origLum) {
             
-        /* With 16 bit grays, the following calculation can
-           overflow a 32 bit long.  So, we do it in floating
-           point.
-        */
+            /* With 16 bit grays, the following calculation can overflow a 32
+               bit long.  So, we do it in floating point.
+            */
 
-        lumamap[i] = ROUNDU((((double) pixsum * maxval)) / pixelCount);
+            lumamap[origLum] =
+                darkestRemap +
+                ROUNDU((((double) pixsum * range)) / remapPixelCount);
+            
+            pixsum += lumahist[origLum];
+        }
 
-        pixsum += lumahist[i];
     }
-
-    findMaxLuma(lumahist, maxval, &maxluma);
-
     {
-        double const lscale = (double)maxval /
-            ((lumahist[maxluma] > 0) ?
-             (double) lumamap[maxluma] : (double) maxval);
+        double const lscale = (double)range /
+            ((lumamap[maxluma] > darkestRemap) ?
+             (double) lumamap[maxluma] - darkestRemap : (double) range);
 
-        unsigned int i;
+        xelval origLum;
 
         /* Normalize so that the brightest pixels are set to maxval. */
 
-        for (i = 0; i <= maxval; ++i)
-            lumamap[i] = MIN(maxval, ROUNDU(lumamap[i] * lscale));
+        for (origLum = darkestRemap; origLum <= brightestRemap; ++origLum)
+            lumamap[origLum] =
+                MIN(brightestRemap, 
+                    darkestRemap + ROUNDU(lumamap[origLum] * lscale));
     }
 }
 
 
 
 static void
+computeMap(const unsigned int * const lumahist,
+           xelval               const maxval,
+           unsigned int         const pixelCount,
+           bool                 const noblack,
+           bool                 const nowhite,
+           gray *               const lumamap) {
+/*----------------------------------------------------------------------------
+  Calculate initial histogram equalization curve.
+
+  'lumahist' is the luminosity histogram for the image; lumahist[N] is
+  the number of pixels that have luminosity N.
+
+  'maxval' is the maxval of the image (ergo the maximum luminosity).
+
+  'pixelCount' is the number of pixels in the image, which is redundant
+  with 'lumahist' but provided for computational convenience.
+   
+  'noblack' means don't include the black pixels in the equalization and
+  make the black pixels in the output the same ones as in the input.
+
+  'nowhite' is equivalent for the white pixels.
+
+  We return the map as *lumamap, where lumamap[N] is the luminosity in the
+  output of a pixel with luminosity N in the input.  Its storage size must
+  be at least 'maxval' + 1.
+-----------------------------------------------------------------------------*/
+    xelval darkestRemap, brightestRemap;
+        /* The lowest and highest luminosity values that we will remap
+           according to the equalization strategy.  They're just 0 and maxval
+           unless modified by 'noblack' and 'nowhite'.
+        */
+    unsigned int remapPixelCount;
+        /* The number of pixels we map according to the equalization
+           strategy; it doesn't include black pixels and white pixels that
+           are excluded from the equalization because of 'noblack' and
+           'nowhite'
+        */
+
+    remapPixelCount = pixelCount;  /* Initial assumption */
+
+    if (noblack) {
+        lumamap[0] = 0;
+        darkestRemap = 1;
+        remapPixelCount -= lumahist[0];
+    } else {
+        darkestRemap = 0;
+    }
+
+    if (nowhite) {
+        lumamap[maxval] = maxval;
+        brightestRemap = maxval - 1;
+        remapPixelCount -= lumahist[maxval];
+    } else {
+        brightestRemap = maxval;
+    }
+
+    equalize(lumahist, darkestRemap, brightestRemap, remapPixelCount,
+             lumamap);
+}
+
+
+
+static void
 getMapping(const char *         const rmapFileName,
            const unsigned int * const lumahist,
            xelval               const maxval,
            unsigned int         const pixelCount,
+           bool                 const noblack,
+           bool                 const nowhite,
            gray **              const lumamapP) {
 /*----------------------------------------------------------------------------
   Calculate the luminosity mapping table which gives the
@@ -275,7 +366,7 @@ getMapping(const char *         const rmapFileName,
     if (rmapFileName)
         readMapFile(rmapFileName, maxval, lumamap);
     else
-        computeMap(lumahist, maxval, pixelCount, lumamap);
+        computeMap(lumahist, maxval, pixelCount, noblack, nowhite, lumamap);
 
     *lumamapP = lumamap;
 }
@@ -302,6 +393,48 @@ reportMap(const unsigned int * const lumahist,
 
 
 
+static xel
+scaleXel(xel    const thisXel,
+         double const scaler) {
+/*----------------------------------------------------------------------------
+   Scale the components of 'xel' by multiplying by 'scaler'.
+
+   Assume this doesn't cause it to exceed maxval.
+-----------------------------------------------------------------------------*/
+    xel retval;
+
+    PNM_ASSIGN(retval,
+               ((xelval)(PNM_GETR(thisXel) * scaler + 0.5)),
+               ((xelval)(PNM_GETG(thisXel) * scaler + 0.5)),
+               ((xelval)(PNM_GETB(thisXel) * scaler + 0.5)));
+    
+    return retval;
+}
+
+
+
+static xel
+remapRgbValue(xel          const thisXel,
+              xelval       const maxval,
+              const gray * const lumamap) {
+/*----------------------------------------------------------------------------
+  Return the color 'thisXel' with its HSV value changed per 'lumamap' but
+  the same hue and saturation.
+
+  'maxval' is the maxval for 'xel' and our return value.
+-----------------------------------------------------------------------------*/
+    struct hsv const hsv =
+        ppm_hsv_from_color(thisXel, maxval);
+    xelval const oldValue =
+        MIN(maxval, ROUNDU(hsv.v * maxval));
+    xelval const newValue =
+        lumamap[oldValue];
+    
+    return scaleXel(thisXel, (double)newValue/oldValue);
+}
+
+
+
 static void
 remap(xel **       const xels,
       unsigned int const cols,
@@ -323,16 +456,8 @@ remap(xel **       const xels,
                 if (monoOnly && PPM_ISGRAY(thisXel)) {
                     /* Leave this pixel alone */
                 } else {
-                    struct hsv hsv;
-                    xelval iv;
-
-                    hsv = ppm_hsv_from_color(thisXel, maxval);
-                    iv = MIN(maxval, ROUNDU(hsv.v * maxval));
-                    
-                    hsv.v = MIN(1.0, 
-                                ((double) lumamap[iv]) / ((double) maxval));
-
-                    xels[row][col] = ppm_color_from_hsv(hsv, maxval);
+                    xels[row][col] = remapRgbValue(xels[row][col],
+                                                   maxval, lumamap);
                 }
             }
         }
@@ -376,7 +501,6 @@ main(int argc, char * argv[]) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
-    xelval lmin, lmax;
     gray * lumamap;           /* Luminosity map */
     unsigned int * lumahist;  /* Histogram of luminosity values */
     int rows, cols;           /* Rows, columns of input image */
@@ -395,11 +519,12 @@ main(int argc, char * argv[]) {
 
     pm_close(ifP);
 
-    computeLuminosityHistogram(xels, rows, cols, maxval, format,
-                               cmdline.gray, &lumahist, &lmin, &lmax,
-                               &pixelCount);
+    computeLuminosityHistogram(xels, rows, cols, maxval, format, cmdline.gray,
+                               &lumahist, &pixelCount);
 
-    getMapping(cmdline.rmap, lumahist, maxval, pixelCount, &lumamap);
+    getMapping(cmdline.rmap, lumahist, maxval, pixelCount,
+               cmdline.noblack > 0, cmdline.nowhite > 0,
+               &lumamap);
 
     if (cmdline.verbose)
         reportMap(lumahist, maxval, lumamap);
diff --git a/editor/pnminvert.c b/editor/pnminvert.c
index 40fee9be..4bee8837 100644
--- a/editor/pnminvert.c
+++ b/editor/pnminvert.c
@@ -12,6 +12,17 @@
 
 #include "pnm.h"
 
+/* Implementation note: A suitably advanced compiler, such as Gcc 4,
+   implements the for statements in our algorithm with instructions that do 16
+   bytes at a time on CPUs that have them (movdqa on x86).  This is "tree
+   vectorization."  A more primitive compiler will do one byte at a time; we
+   could change the code to use libnetpbm's wordaccess.h facility and it will
+   do one word at a time.  (But we don't think it's worth complicating the
+   code for that).
+*/
+
+
+
 #define CHARBITS (sizeof(unsigned char)*8)
 
 
@@ -25,9 +36,6 @@ invertPbm(FILE * const ifP,
 /*----------------------------------------------------------------------------
    Invert a PBM image.  Use the "packed" PBM functions for speed.
 -----------------------------------------------------------------------------*/
-    /* We could make this faster by inverting whole words at a time,
-       using libnetpbm's wordaccess.h facility.
-    */
     int const colChars = pbm_packed_bytes(cols);
 
     unsigned char * bitrow; 
@@ -42,11 +50,8 @@ invertPbm(FILE * const ifP,
         for (colChar = 0; colChar < colChars; ++colChar)
             bitrow[colChar] = ~ bitrow[colChar];
         
-        /* Clean off remainder of fractional last character */
-        if (cols % CHARBITS > 0) {
-            bitrow[colChars-1] >>= CHARBITS - cols % CHARBITS;
-            bitrow[colChars-1] <<= CHARBITS - cols % CHARBITS;
-        }
+        /* Clean off remainder of fractional last character and write */
+        pbm_cleanrowend_packed(bitrow, cols);
         pbm_writepbmrow_packed(ofP, bitrow, cols, 0);
     }
     pbm_freerow_packed(bitrow);
diff --git a/editor/pnminvert.test b/editor/pnminvert.test
deleted file mode 100644
index 5534f20d..00000000
--- a/editor/pnminvert.test
+++ /dev/null
@@ -1,15 +0,0 @@
-echo Test 1.  Should print 1240379484 41
-./pnminvert ../testgrid.pbm | cksum
-echo Test 2.  Should print 1416115901 101484
-./pnminvert ../testimg.ppm | cksum
-echo Test 3.  Should print 2961441369 33838
-ppmtopgm ../testimg.ppm | ./pnminvert | cksum
-echo Test 4.  Should print 2595564405 14
-pbmmake -w 7 7 | ./pnminvert | cksum
-echo Test 5.  Should print 2595564405 14
-pbmmake -b 7 7 | cksum
-echo Test 6.  Should print 2595564405 14
-pbmmake -b 7 7 | ./pnminvert | ./pnminvert | cksum
-echo Test 7.  Should print 2896726098 15
-pbmmake -g 8 8 | ./pnminvert | cksum
-
diff --git a/editor/pnmmargin b/editor/pnmmargin
index b31deefd..0f57d1d4 100755
--- a/editor/pnmmargin
+++ b/editor/pnmmargin
@@ -31,30 +31,30 @@ while true ; do
         plainopt="-plain"
         shift
         ;;
-	-w|-wh|-whi|-whit|-white )
-	color="-white"
-	shift
-	;;
-	-b|-bl|-bla|-blac|-black )
-	color="-black"
-	shift
-	;;
-	-c|-co|-col|-colo|-color )
-	shift
-	if [ ! ${1-""} ] ; then
-       	    echo "usage: $0 [-white|-black|-color <colorspec>] <size> [pnmfile]" 1>&2
-	    exit 1
-	fi
-	color="$1"
-	shift
-	;;
-	-* )
-	echo "usage: $0 [-white|-black|-color <colorspec>] <size> [pnmfile]" 1>&2
-	exit 1
-	;;
-	* )
-	break
-	;;
+        -w|-wh|-whi|-whit|-white )
+        color="-white"
+        shift
+        ;;
+        -b|-bl|-bla|-blac|-black )
+        color="-black"
+        shift
+        ;;
+        -c|-co|-col|-colo|-color )
+        shift
+        if [ ! ${1-""} ] ; then
+            echo "usage: $0 [-white|-black|-color <colorspec>] <size> [pnmfile]" 1>&2
+            exit 1
+        fi
+        color="$1"
+        shift
+        ;;
+        -* )
+        echo "usage: $0 [-white|-black|-color <colorspec>] <size> [pnmfile]" 1>&2
+        exit 1
+        ;;
+        * )
+        break
+        ;;
     esac
 done
 
@@ -82,7 +82,7 @@ if [ $size -eq 0 ] ; then
 else
     # If -white or -black, write output with pnmpad and exit.
     # Otherwise construct spacer files.
-    
+
     case "$color" in
         -gofigure )
         pnmcut 0 0 1 1 $tmp1 | pnmtile $size 1 > $tmp2
@@ -97,12 +97,10 @@ else
         ;;
     esac
     pamflip -rotate90 $tmp2 > $tmp3
-    
+
     # Cat things together.
     pnmcat -lr $tmp2 $tmp1 $tmp2 > $tmp4
     pnmcat -tb $plainopt $tmp3 $tmp4 $tmp3
 fi
 
 
-
-
diff --git a/editor/pnmmontage.c b/editor/pnmmontage.c
index 2e30a43b..e54afc45 100644
--- a/editor/pnmmontage.c
+++ b/editor/pnmmontage.c
@@ -10,6 +10,7 @@
  * implied warranty.
  */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <assert.h>
 #include <limits.h>
@@ -23,7 +24,7 @@
 
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     const char * header;
     const char * data;
     const char * prefix;
@@ -37,7 +38,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 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.  
@@ -78,7 +79,9 @@ parseCommandLine(int argc, const char ** argv,
     opt.short_allowed = FALSE;
     opt.allowNegNum = FALSE;
 
-    optParseOptions3(&argc, (char**)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char**)argv, opt, sizeof(opt), 0);
+
+    free(option_def);
 
     if (!dataSpec)
         cmdlineP->data = NULL;
@@ -116,24 +119,27 @@ parseCommandLine(int argc, const char ** argv,
 
 typedef struct {
     int f[sizeof(int) * 8 + 1];
-} factorset;
+} Factorset;
 
 typedef struct {
-    int x; int y;
-} coord;
+    int x;
+    int y;
+} Coord;
 
 typedef struct {
-    coord ul;
-    coord size;
-} rectangle;
+    Coord ul;
+    Coord size;
+} Rectangle;
+
 
-static coord
-lr(rectangle const r) {
+
+static Coord
+lr(Rectangle const r) {
 /*----------------------------------------------------------------------------
-   Return the coordinates of the lower right corner of 'r'
-   (i.e. the pixel just beyond the lowest rightmost one).
+  The coordinates of the lower right corner of 'r' (i.e. the pixel just beyond
+  the lowest rightmost one).
 -----------------------------------------------------------------------------*/
-    coord retval;
+    Coord retval;
 
     retval.x = r.ul.x + r.size.x;
     retval.y = r.ul.y + r.size.y;
@@ -141,50 +147,67 @@ lr(rectangle const r) {
     return retval;
 }
 
-static factorset 
-factor(int n)
-{
-  int i, j;
-  factorset f;
-  for (i = 0; i < sizeof(int) * 8 + 1; ++i)
-    f.f[i] = 0;
-  for (i = 2, j = 0; n > 1; ++i)
-  {
-    if (n % i == 0)
-      f.f[j++] = i, n /= i, --i;
-  }
-  return (f);
+
+
+static Factorset 
+factor(unsigned int const arg) {
+/*----------------------------------------------------------------------------
+   The prime factors of 'arg'.
+-----------------------------------------------------------------------------*/
+    unsigned int n;
+    unsigned int i, j;
+    Factorset retval;
+
+    /* Initialize array element to zero */
+    for (i = 0; i < ARRAY_SIZE(retval.f); ++i)
+        retval.f[i] = 0;
+
+    /* Set array elements starting with the first to the factors */
+
+    for (i = 2, j = 0, n = arg; n > 1; ) {
+        if (n % i == 0) {
+            retval.f[j++] = i;
+            n /= i;
+        } else
+            ++i;
+    }
+    return retval;
 }
 
+
+
 static int 
-gcd(int n, int m)
-{
-  factorset nf, mf;
-  int i, j;
-  int g;
-
-  nf = factor(n);
-  mf = factor(m);
-
-  i = j = 0;
-  g = 1;
-  while (nf.f[i] && mf.f[j])
-  {
-    if (nf.f[i] == mf.f[j])
-      g *= nf.f[i], ++i, ++j;
-    else if (nf.f[i] < mf.f[j])
-      ++i;
-    else
-      ++j;
-  }
-  return (g);
+gcf(unsigned int const n,
+    unsigned int const m) {
+/*----------------------------------------------------------------------------
+   Greatest common factor of 'n' and 'm'
+-----------------------------------------------------------------------------*/
+    Factorset const nFactors = factor(n);
+    Factorset const mFactors = factor(m);
+
+    unsigned int i, j;
+    unsigned int g;
+
+    i = j = 0;
+    g = 1;
+    while (nFactors.f[i] && mFactors.f[j]) {
+        if (nFactors.f[i] == mFactors.f[j]) {
+            g *= nFactors.f[i];
+            ++i;
+            ++j;
+        } else if (nFactors.f[i] < mFactors.f[j])
+            ++i;
+        else
+            ++j;
+    }
+    return g;
 }
 
 
 
 static bool
-overlaps(rectangle const a,
-         rectangle const b) {
+overlaps(Rectangle const a,
+         Rectangle const b) {
 
     return
         (a.ul.x < lr(b).x && a.ul.y < lr(b).y) &&
@@ -194,8 +217,8 @@ overlaps(rectangle const a,
 
 
 static bool
-collides(rectangle         const test,
-         const rectangle * const fieldList,
+collides(Rectangle         const test,
+         const Rectangle * const fieldList,
          unsigned int      const n) {
 /*----------------------------------------------------------------------------
    Return true iff the rectangle 'test' overlaps any of the 'n' rectangles
@@ -203,19 +226,19 @@ collides(rectangle         const test,
 -----------------------------------------------------------------------------*/
     unsigned int i;
 
-    for (i = 0; i < n; ++i)
+    for (i = 0; i < n; ++i) {
         if (overlaps(fieldList[i], test))
             return true;
-
+    }
     return false;
 }
 
 
 
 static void 
-recursefindpack(rectangle *    const current,
-                coord          const currentsz,
-                coord *        const best,
+recursefindpack(Rectangle *    const current,
+                Coord          const currentsz,
+                Coord *        const best,
                 unsigned int   const minarea,
                 unsigned int * const maxareaP, 
                 unsigned int   const depth,
@@ -235,13 +258,13 @@ recursefindpack(rectangle *    const current,
     } else {
         unsigned int i;
 
-        rectangle * const newP = &current[depth];
+        Rectangle * const newP = &current[depth];
 
         for (i = 0; ; ++i) {
             for (newP->ul.x = 0, newP->ul.y = i * yinc;
                  newP->ul.y <= i * yinc;) {
 
-                coord c;
+                Coord c;
 
                 c.x = MAX(lr(*newP).x, currentsz.x);
                 c.y = MAX(lr(*newP).y, currentsz.y);
@@ -273,36 +296,35 @@ recursefindpack(rectangle *    const current,
 
 static void 
 findpack(struct pam * const imgs,
-         unsigned int const n,
-         coord *      const coords,
+         unsigned int const imgCt,
+         Coord **     const coordsP,
          unsigned int const quality,
          unsigned int const qfactor) {
 
-    int minarea;
-    int i;
-    int rdiv;
-    int cdiv;
-    int minx;
-    int miny;
-    rectangle * current;
+    Coord * coords;  /* malloc'ed array */
+    unsigned int minarea;
+    unsigned int i;
+    unsigned int rdiv;
+    unsigned int cdiv;
+    Rectangle * current;  /* malloc'ed array */
     unsigned int z;
-    coord c;
+    Coord c;
+
+    MALLOCARRAY(coords, imgCt);
+  
+    if (!coords)
+        pm_error("Out of memory allocating %u-element coords array", imgCt);
 
-    minx = -1; miny = -1;  /* initial value */
     z = UINT_MAX;  /* initial value */
     c.x = 0; c.y = 0;  /* initial value */
 
     if (quality > 1) {
         unsigned int realMinarea;
-        for (realMinarea = i = 0; i < n; ++i)
-            realMinarea += imgs[i].height * imgs[i].width,
-                minx = MAX(minx, imgs[i].width),
-                miny = MAX(miny, imgs[i].height);
-
+        for (realMinarea = i = 0; i < imgCt; ++i)
+            realMinarea += imgs[i].height * imgs[i].width;
         minarea = realMinarea * qfactor / 100;
-    } else {
-        minarea = INT_MAX - 1;
-    }
+    } else
+        minarea = UINT_MAX - 1;
 
     /* It's relatively easy to show that, if all the images
      * are multiples of a particular size, then a best
@@ -311,20 +333,24 @@ findpack(struct pam * const imgs,
      *
      * This speeds computation immensely.
      */
-    for (rdiv = imgs[0].height, i = 1; i < n; ++i)
-        rdiv = gcd(imgs[i].height, rdiv);
+    for (rdiv = imgs[0].height, i = 1; i < imgCt; ++i)
+        rdiv = gcf(imgs[i].height, rdiv);
 
-    for (cdiv = imgs[0].width, i = 1; i < n; ++i)
-        cdiv = gcd(imgs[i].width, cdiv);
+    for (cdiv = imgs[0].width, i = 1; i < imgCt; ++i)
+        cdiv = gcf(imgs[i].width, cdiv);
 
-    MALLOCARRAY(current, n);
+    MALLOCARRAY(current, imgCt);
 
-    for (i = 0; i < n; ++i) {
+    for (i = 0; i < imgCt; ++i) {
         current[i].size.x = imgs[i].width;
         current[i].size.y = imgs[i].height;
     }
-    recursefindpack(current, c, coords, minarea, &z, 0, n, cdiv, rdiv,
+    recursefindpack(current, c, coords, minarea, &z, 0, imgCt, cdiv, rdiv,
                     quality, qfactor);
+
+    free(current);
+
+    *coordsP = coords;
 }
 
 
@@ -333,8 +359,8 @@ static void
 adjustDepth(tuple *            const tuplerow,
             const struct pam * const inpamP,
             const struct pam * const outpamP,
-            coord              const coord) {
-
+            Coord              const coord) {
+    
     if (inpamP->depth < outpamP->depth) {
         unsigned int i;
         for (i = coord.x; i < coord.x + inpamP->width; ++i) {
@@ -351,12 +377,12 @@ static void
 adjustMaxval(tuple *            const tuplerow,
              const struct pam * const inpamP,
              const struct pam * const outpamP,
-             coord              const coord) {
+             Coord              const coord) {
 
     if (inpamP->maxval < outpamP->maxval) {
-        int i;
+        unsigned int i;
         for (i = coord.x; i < coord.x + inpamP->width; ++i) {
-            int j;
+            unsigned int j;
             for (j = 0; j < outpamP->depth; ++j)
                 tuplerow[i][j] *= outpamP->maxval / inpamP->maxval;
         }
@@ -382,29 +408,37 @@ makeRowBlack(struct pam * const pamP,
 
 static void
 writePam(struct pam *       const outpamP,
-         unsigned int       const nfiles,
-         const coord *      const coords,
+         unsigned int       const imgCt,
+         const Coord *      const coords,
          const struct pam * const imgs) {
-
-    tuple *tuplerow;
-    int i;
+/*----------------------------------------------------------------------------
+   Write the entire composite image.  There are 'imgCt' source images,
+   described by imgs[].  Their placement in the output is coords[].
+   Properties of the output image, including where to write it
+   and its dimensions are *outpamP.
+-----------------------------------------------------------------------------*/
+    tuple * tuplerow;
+    unsigned int row;  /* Row number in the output image */
   
     pnm_writepaminit(outpamP);
 
     tuplerow = pnm_allocpamrow(outpamP);
 
-    for (i = 0; i < outpamP->height; ++i) {
-        int j;
-
+    for (row = 0; row < outpamP->height; ++row) {
+        unsigned int imgIdx;
+        
         makeRowBlack(outpamP, tuplerow);  /* initial value */
 
-        for (j = 0; j < nfiles; ++j) {
-            if (coords[j].y <= i && i < coords[j].y + imgs[j].height) {
-                pnm_readpamrow(&imgs[j], &tuplerow[coords[j].x]);
-                adjustDepth(tuplerow, &imgs[j], outpamP, coords[j]);
+        for (imgIdx = 0; imgIdx < imgCt; ++imgIdx) {
+            const Coord *      const imgCoordP = &coords[imgIdx];
+            const struct pam * const imgPamP   = &imgs[imgIdx];
 
-                adjustMaxval(tuplerow, &imgs[j], outpamP, coords[j]);
+            if (imgCoordP->y <= row && row < imgCoordP->y + imgPamP->height) {
+                pnm_readpamrow(imgPamP, &tuplerow[imgCoordP->x]);
 
+                adjustDepth(tuplerow, imgPamP, outpamP, *imgCoordP);
+
+                adjustMaxval(tuplerow, imgPamP, outpamP, *imgCoordP);
             }
         }
         pnm_writepamrow(outpamP, tuplerow);
@@ -418,18 +452,18 @@ static void
 writeData(FILE *             const dataFileP,
           unsigned int       const width,
           unsigned int       const height,
-          unsigned int       const nfiles,
+          unsigned int       const imgCt,
           const char **      const names,
-          const coord *      const coords,
+          const Coord *      const coords,
           const struct pam * const imgs) {
 
-    unsigned int i;
+    unsigned int imgIdx;
 
     fprintf(dataFileP, ":0:0:%u:%u\n", width, height);
 
-    for (i = 0; i < nfiles; ++i) {
-        fprintf(dataFileP, "%s:%u:%u:%u:%u\n", names[i], coords[i].x,
-                coords[i].y, imgs[i].width, imgs[i].height);
+    for (imgIdx = 0; imgIdx < imgCt; ++imgIdx) {
+        fprintf(dataFileP, "%s:%u:%u:%u:%u\n", names[imgIdx], coords[imgIdx].x,
+                coords[imgIdx].y, imgs[imgIdx].width, imgs[imgIdx].height);
     }
 }
 
@@ -442,7 +476,7 @@ writeHeader(FILE * const headerFileP,
             unsigned int const height,
             unsigned int const nfiles,
             const char ** const names,
-            const coord * const coords,
+            const Coord * const coords,
             const struct pam * imgs) {
 
     unsigned int i;
@@ -455,7 +489,7 @@ writeHeader(FILE * const headerFileP,
 
     for (i = 0; i < nfiles; ++i) {
         char * const buffer = strdup(names[i]);
-        coord const coord = coords[i];
+        Coord const coord = coords[i];
         struct pam const img = imgs[i];
 
         unsigned int j;
@@ -554,11 +588,11 @@ computeOutputType(sample *           const maxvalP,
 
 
 static void
-computeOutputDimensions(int * const widthP,
-                        int * const heightP,
-                        unsigned int const nfiles,
+computeOutputDimensions(int *              const widthP,
+                        int *              const heightP,
+                        unsigned int       const nfiles,
                         const struct pam * const imgs,
-                        const coord * const coords) {
+                        const Coord *      const coords) {
 
     unsigned int widthGuess, heightGuess;
     unsigned int i;
@@ -577,100 +611,162 @@ computeOutputDimensions(int * const widthP,
 
 
 
-int 
-main(int argc, const char **argv) {
+static unsigned int
+qfactorFromQuality(unsigned int const quality,
+                   unsigned int const quality2) {
 
-    struct cmdlineInfo cmdline;
-    struct pam * imgs;
-    struct pam outimg;
-    unsigned int nfiles;
-    coord * coords;
-    FILE * header;
-    FILE * data;
-    const char ** names;
-    unsigned int i;
-    unsigned int qfactor;  /* In per cent */
+    unsigned int qfactor;
 
-    pm_proginit(&argc, argv);
-
-    parseCommandLine(argc, argv, &cmdline);
-
-    header = cmdline.header ? pm_openw(cmdline.header) : NULL;
-    data = cmdline.data ? pm_openw(cmdline.data) : NULL;
-
-    switch (cmdline.quality2) {
+    switch (quality2) {
     case 0: case 1:
-        qfactor = cmdline.quality;
+        qfactor = quality;
         break;
     case 2: case 3: case 4: case 5: case 6: 
-        qfactor = 100 * (8 - cmdline.quality2); 
+        qfactor = 100 * (8 - quality2); 
         break;
     case 7: qfactor = 150; break;
     case 8: qfactor = 125; break;
     case 9: qfactor = 100; break;
     default: pm_error("Internal error - impossible value of 'quality2': %u",
-                      cmdline.quality2);
+                      quality2);
     }
 
-    nfiles = cmdline.nFiles > 0 ? cmdline.nFiles : 1;
+    return qfactor;
+}
 
-    MALLOCARRAY(imgs, nfiles);
-    MALLOCARRAY(coords, nfiles);
-    MALLOCARRAY(names, nfiles);
-  
-    if (!imgs || !coords || !names)
+
+
+static void
+openFiles(struct CmdlineInfo const cmdline,
+          unsigned int *     const fileCtP,
+          struct pam **      const imgPamP,
+          const char ***     const namesP) {
+
+    unsigned int fileCt;
+    struct pam * imgPam;
+    const char ** names;
+
+    fileCt = cmdline.nFiles > 0 ? cmdline.nFiles : 1;
+
+    MALLOCARRAY(imgPam, fileCt);
+    MALLOCARRAY(names, fileCt);
+
+    if (!imgPam || !names)
         pm_error("out of memory");
 
     if (cmdline.nFiles > 0) {
         unsigned int i;
 
         for (i = 0; i < cmdline.nFiles; ++i) {
-            imgs[i].file = pm_openr(cmdline.inFileName[i]);
+            imgPam[i].file = pm_openr(cmdline.inFileName[i]);
             names[i] = strdup(cmdline.inFileName[i]);
         }
     } else {
-        imgs[0].file = stdin;
+        imgPam[0].file = stdin;
         names[0] = strdup("stdin");
     }
 
-    for (i = 0; i < nfiles; ++i)
-        pnm_readpaminit(imgs[i].file, &imgs[i], PAM_STRUCT_SIZE(tuple_type));
+    *fileCtP = fileCt;
+    *imgPamP = imgPam;
+    *namesP  = names;
+}
 
-    sortImagesByArea(nfiles, imgs, names);
 
-    findpack(imgs, nfiles, coords, cmdline.quality2, qfactor);
 
-    computeOutputType(&outimg.maxval, &outimg.format, outimg.tuple_type,
-                      &outimg.depth, nfiles, imgs);
+static void
+readFileHeaders(struct pam * const imgPam,
+                unsigned int const fileCt) {
 
-    computeOutputDimensions(&outimg.width, &outimg.height, nfiles,
-                            imgs, coords);
+    unsigned int i;
 
-    pnm_setminallocationdepth(&outimg, outimg.depth);
+    for (i = 0; i < fileCt; ++i)
+        pnm_readpaminit(imgPam[i].file, &imgPam[i],
+                        PAM_STRUCT_SIZE(tuple_type));
+}
+
+
+
+static void
+closeFiles(const struct pam * const imgPam,
+           unsigned int       const fileCt,
+           FILE *             const headerFileP,
+           FILE *             const dataFileP) {
+
+    unsigned int i;
+
+    for (i = 0; i < fileCt; ++i)
+        pm_close(imgPam[i].file);
+
+    pm_close(stdout);
+
+    if (headerFileP)
+        pm_close(headerFileP);
+
+    if (dataFileP)
+        pm_close(dataFileP);
+}
 
+
+
+int 
+main(int argc, const char **argv) {
+
+    struct CmdlineInfo cmdline;
+    struct pam * imgPam;  /* malloced */
+    struct pam outimg;
+    unsigned int fileCt;
+    Coord * coords;  /* malloced */
+    FILE * headerFileP;
+    FILE * dataFileP;
+    const char ** names; /* malloced */
+    unsigned int qfactor;  /* In per cent */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    headerFileP = cmdline.header ? pm_openw(cmdline.header) : NULL;
+    dataFileP = cmdline.data ? pm_openw(cmdline.data) : NULL;
+
+    qfactor = qfactorFromQuality(cmdline.quality, cmdline.quality2);
+
+    openFiles(cmdline, &fileCt, &imgPam, &names);
+
+    readFileHeaders(imgPam, fileCt);
+
+    sortImagesByArea(fileCt, imgPam, names);
+
+    findpack(imgPam, fileCt, &coords, cmdline.quality2, qfactor);
+
+    computeOutputType(&outimg.maxval, &outimg.format, outimg.tuple_type,
+                      &outimg.depth, fileCt, imgPam);
+
+    computeOutputDimensions(&outimg.width, &outimg.height, fileCt,
+                            imgPam, coords);
     outimg.size = sizeof(outimg);
     outimg.len = PAM_STRUCT_SIZE(allocation_depth);
     pnm_setminallocationdepth(&outimg, outimg.depth);
     outimg.plainformat = false;
     outimg.file = stdout;
  
-    writePam(&outimg, nfiles, coords, imgs);
+    writePam(&outimg, fileCt, coords, imgPam);
 
-    if (data)
-        writeData(data, outimg.width, outimg.height,
-                  nfiles, names, coords, imgs);
+    if (dataFileP)
+        writeData(dataFileP, outimg.width, outimg.height,
+                  fileCt, names, coords, imgPam);
 
-    if (header)
-        writeHeader(header, cmdline.prefix, outimg.width, outimg.height,
-                    nfiles, names, coords, imgs);
+    if (headerFileP)
+        writeHeader(headerFileP, cmdline.prefix, outimg.width, outimg.height,
+                    fileCt, names, coords, imgPam);
 
-    for (i = 0; i < nfiles; ++i)
-        pm_close(imgs[i].file);
-    pm_close(stdout);
-    if (header)
-        pm_close(header);
-    if (data)
-        pm_close(data);
+    closeFiles(imgPam, fileCt, headerFileP, dataFileP);
+
+    free(coords);
+    free(imgPam);
+    free(names);
 
     return 0;
 }
+
+
+
diff --git a/editor/pnmnlfilt.c b/editor/pnmnlfilt.c
index bde0cd82..f55a67bd 100644
--- a/editor/pnmnlfilt.c
+++ b/editor/pnmnlfilt.c
@@ -990,7 +990,7 @@ main(int argc, char *argv[]) {
 
     FILE * ifP;
     struct cmdlineInfo cmdline;
-	bool eof;  /* We've hit the end of the input stream */
+	int eof;  /* We've hit the end of the input stream */
     unsigned int imageSeq;  /* Sequence number of image, starting from 0 */
 
     pnm_init(&argc, argv);
diff --git a/editor/pnmnorm.c b/editor/pnmnorm.c
index 27d51115..131b39d0 100644
--- a/editor/pnmnorm.c
+++ b/editor/pnmnorm.c
@@ -31,7 +31,9 @@
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
+#include "nstring.h"
 #include "shhopt.h"
+#include "matrix.h"
 #include "pnm.h"
 
 enum brightMethod {BRIGHT_LUMINOSITY, BRIGHT_COLORVALUE, BRIGHT_SATURATION};
@@ -40,7 +42,7 @@ struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* Filespec of input file */
+    const char * inputFileName;  /* Name of input file */
     unsigned int bvalueSpec;
     xelval bvalue;
     unsigned int bpercentSpec;
@@ -49,6 +51,11 @@ struct cmdlineInfo {
     xelval wvalue;
     unsigned int wpercentSpec;
     float wpercent;
+    unsigned int bsingle;
+    unsigned int wsingle;
+    float middle;
+    unsigned int midvalueSpec;
+    xelval midvalue;
     enum brightMethod brightMethod;
     unsigned int keephues;
     float maxExpansion;
@@ -56,6 +63,7 @@ struct cmdlineInfo {
            by per centile.  This is a factor, not a per cent increase.
            E.g. 50% increase means a factor of 1.50.
         */
+    unsigned int verbose;
 };
 
 
@@ -74,12 +82,12 @@ parseCommandLine(int argc, const char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
     unsigned int luminosity, colorvalue, saturation;
-    unsigned int maxexpandSpec;
+    unsigned int middleSpec, maxexpandSpec;
     float maxexpand;
     
     unsigned int option_def_index;
@@ -95,6 +103,14 @@ parseCommandLine(int argc, const char ** argv,
             &cmdlineP->bvalue,     &cmdlineP->bvalueSpec, 0);
     OPTENT3(0,   "wvalue",        OPT_UINT,   
             &cmdlineP->wvalue,     &cmdlineP->wvalueSpec, 0);
+    OPTENT3(0,   "bsingle",       OPT_FLAG,   
+            NULL,                 &cmdlineP->bsingle, 0);
+    OPTENT3(0,   "wsingle",       OPT_FLAG,   
+            NULL,                 &cmdlineP->wsingle, 0);
+    OPTENT3(0,   "middle",        OPT_FLOAT,   
+            &cmdlineP->middle,     &middleSpec, 0);
+    OPTENT3(0,   "midvalue",      OPT_UINT,   
+            &cmdlineP->midvalue,   &cmdlineP->midvalueSpec, 0);
     OPTENT3(0,   "maxexpand",     OPT_FLOAT,   
             &maxexpand,            &maxexpandSpec, 0);
     OPTENT3(0,   "keephues",      OPT_FLAG,   
@@ -107,6 +123,8 @@ parseCommandLine(int argc, const char ** argv,
             NULL,                  &saturation, 0);
     OPTENT3(0,   "brightmax",     OPT_FLAG,   
             NULL,                  &colorvalue, 0);
+    OPTENT3(0,   "verbose",       OPT_FLAG,   
+            NULL,                  &cmdlineP->verbose, 0);
 
     /* Note: -brightmax was documented and accepted long before it was
        actually implemented.  By the time we implemented it, we
@@ -117,7 +135,7 @@ parseCommandLine(int argc, const char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (!cmdlineP->wpercentSpec)
@@ -138,6 +156,20 @@ parseCommandLine(int argc, const char ** argv,
         pm_error("You specified a per centage > 100 for bpercent: %f",
                  cmdlineP->bpercent);
 
+    if (cmdlineP->bsingle && (cmdlineP->bpercentSpec || cmdlineP->bvalueSpec))
+        pm_error("You cannot specify both -bsingle and -bpercent or -bvalue");
+
+    if (cmdlineP->wsingle && (cmdlineP->wpercentSpec || cmdlineP->wvalueSpec))
+        pm_error("You cannot specify both -wsingle and -wpercent or -wvalue");
+
+    if (middleSpec) {
+        if (cmdlineP->middle < 0.0 || cmdlineP->middle > 1.0)
+            pm_error("-middle is a normalized brightness value; it "
+                     "must be in the range 0..1.  You specified %f",
+                     cmdlineP->middle);
+    } else
+        cmdlineP->middle = 0.5;
+
     if (luminosity + colorvalue + saturation > 1)
         pm_error("You can specify only one of "
                  "-luminosity, -colorvalue, and -saturation");
@@ -163,9 +195,9 @@ parseCommandLine(int argc, const char ** argv,
                  "specification.  "
                  "You specified %d arguments.", argc-1);
     if (argc-1 < 1)
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
     else
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
 }
 
 
@@ -211,7 +243,7 @@ buildHistogram(FILE *   const ifp,
             if (PNM_FORMAT_TYPE(format) == PPM_TYPE) {
                 switch(brightMethod) {
                 case BRIGHT_LUMINOSITY:
-                    brightness = PPM_LUMIN(p);
+                    brightness = ppm_luminosity(p);
                     break;
                 case BRIGHT_COLORVALUE:
                     brightness = ppm_colorvalue(p);
@@ -230,6 +262,50 @@ buildHistogram(FILE *   const ifp,
 
 
 
+static xelval
+minimumValue(const unsigned int * const hist,
+             unsigned int         const highest) {
+
+    xelval i;
+    bool foundOne;
+
+    for (i = 0, foundOne = false; !foundOne; ) {
+        if (hist[i] > 0)
+            foundOne = true;
+        else {
+            if (i == highest)
+                pm_error("INTERNAL ERROR in '%s'.  No pixels", __FUNCTION__);
+            else
+                ++i;
+        }
+    }
+    return i;
+}
+
+
+
+static xelval
+maximumValue(const unsigned int * const hist,
+             unsigned int         const highest) {
+
+    xelval i;
+    bool foundOne;
+
+    for (i = highest, foundOne = false; !foundOne; ) {
+        if (hist[i] > 0)
+            foundOne = true;
+        else {
+            if (i == 0)
+                pm_error("INTERNAL ERROR in '%s'.  No pixels", __FUNCTION__);
+            else
+                --i;
+        }
+    }
+    return i;
+}
+
+
+
 static void
 computeBottomPercentile(unsigned int         hist[], 
                         unsigned int   const highest,
@@ -239,7 +315,7 @@ computeBottomPercentile(unsigned int         hist[],
 /*----------------------------------------------------------------------------
    Compute the lowest index of hist[] such that the sum of the hist[]
    values with that index and lower represent at least 'percent' per cent of
-   'n' (which is assumed to be the sum of all the values in hist[],
+   'total' (which is assumed to be the sum of all the values in hist[],
    given to us to save us the time of computing it).
 -----------------------------------------------------------------------------*/
     unsigned int cutoff = total * percent / 100.0;
@@ -271,7 +347,7 @@ computeTopPercentile(unsigned int         hist[],
 /*----------------------------------------------------------------------------
    Compute the highest index of hist[] such that the sum of the hist[]
    values with that index and higher represent 'percent' per cent of
-   'n' (which is assumed to be the sum of all the values in hist[],
+   'total' (which is assumed to be the sum of all the values in hist[],
    given to us to save us the time of computing it).
 -----------------------------------------------------------------------------*/
     unsigned int cutoff = total * percent / 100.0;
@@ -430,9 +506,9 @@ resolvePercentParams(FILE *             const ifP,
 /*----------------------------------------------------------------------------
    Figure out the endpoint of the stretch (the value that is to be stretched
    to black and the one that is to be stretched to white) as requested
-   by the -bvalue, -bpercent, -wvalue, and -wpercent options.
+   by the -{b,w}{value,percent,single} options.
 
-   These values may be invalid due to overlapping, and they may exceed
+   These values may be invalid because of overlapping, and they may exceed
    the maximum allowed stretch; Caller must deal with that.
 -----------------------------------------------------------------------------*/
     unsigned int * hist;  /* malloc'ed */
@@ -445,7 +521,9 @@ resolvePercentParams(FILE *             const ifP,
         buildHistogram(ifP, cols, rows, maxval, format, hist,
                        cmdline.brightMethod);
 
-        if (cmdline.bvalueSpec && !cmdline.bpercentSpec) {
+        if (cmdline.bsingle)
+            *bvalueP = minimumValue(hist, maxval);
+        else if (cmdline.bvalueSpec && !cmdline.bpercentSpec) {
             *bvalueP = cmdline.bvalue;
         } else {
             xelval percentBvalue;
@@ -457,7 +535,9 @@ resolvePercentParams(FILE *             const ifP,
                 *bvalueP = percentBvalue;
         }
 
-        if (cmdline.wvalueSpec && !cmdline.wpercentSpec) {
+        if (cmdline.wsingle)
+            *wvalueP = maximumValue(hist, maxval);
+        else if (cmdline.wvalueSpec && !cmdline.wpercentSpec) {
             *wvalueP = cmdline.wvalue;
         } else {
             xelval percentWvalue;
@@ -482,14 +562,25 @@ computeEndValues(FILE *             const ifP,
                  int                const format,
                  struct cmdlineInfo const cmdline,
                  xelval *           const bvalueP,
-                 xelval *           const wvalueP) {
+                 xelval *           const wvalueP,
+                 bool *             const quadraticP,
+                 xelval *           const midvalueP) {
 /*----------------------------------------------------------------------------
-   Figure out what original values will be translated to full white and
-   full black -- thus defining to what all the other values get translated.
-
-   This may involve looking at the image.  The image is in the file
-   'ifp', which is positioned just past the header (at the raster).
-   Leave it positioned arbitrarily.
+   Figure out what original values will be translated to full bright and full
+   dark and, if user requested, a middle brightness -- thus defining to what
+   all the other values get translated.
+
+   This may involve looking at the image.  The image is in the file 'ifP',
+   which is positioned just past the header (at the raster).  Leave it
+   positioned arbitrarily.
+
+   We return *quadraticP == true iff the normalization is to be via a
+   quadratic transfer function fixed at 3 points - full bright, full dark, and
+   something in between.  In that case, the original brightnesses of those
+   three points are *bvalueP, *midvalueP, and *wvalueP.  We return *quadraticP
+   == false iff the normalization is to be via a linear function fixed at 2
+   points - full bright and full dark.  In that case, *midvalueP is
+   meaningless.
 -----------------------------------------------------------------------------*/
     xelval reqBvalue, reqWvalue, nonOlapBvalue, nonOlapWvalue;
     unsigned int bLower, wRaise;
@@ -507,15 +598,187 @@ computeEndValues(FILE *             const ifP,
 
     *bvalueP = nonOlapBvalue - bLower;
     *wvalueP = nonOlapWvalue + wRaise;
+
+    if (cmdline.midvalueSpec) {
+        if (cmdline.midvalue > *bvalueP && cmdline.midvalue < *wvalueP) {
+            *quadraticP = true;
+            *midvalueP = cmdline.midvalue;
+        } else
+            *quadraticP = false;
+    } else
+        *quadraticP = false;
+}
+
+
+
+static void
+computeLinearTransfer(xelval   const bvalue,
+                      xelval   const wvalue,
+                      xelval   const maxval,
+                      xelval * const newBrightness) {
+/*----------------------------------------------------------------------------
+   Map the middle brightnesses (the ones that don't get clipped to full dark
+   or full bright, i.e. from 'bvalue' to 'wvalue') linearly onto 0..maxval.
+   Set this mapping in newBrightness[].
+-----------------------------------------------------------------------------*/
+    unsigned int const range = wvalue - bvalue;
+
+    xelval i;
+    unsigned int val;
+    /* The following for structure is a hand optimization of this one:
+       for (i = bvalue; i <= wvalue; ++i)
+       newBrightness[i] = (i-bvalue)*maxval/range);
+       (with proper rounding)
+    */
+    for (i = bvalue, val = range/2; 
+         i <= wvalue; 
+         ++i, val += maxval)
+        newBrightness[i] = MIN(val / range, maxval);
+
+    assert(newBrightness[bvalue] == 0);
+    assert(newBrightness[wvalue] == maxval);
+}
+
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   A quadratic polynomial function
+-----------------------------------------------------------------------------*/
+    double a;  /* x^2 coefficient */
+    double b;  /* x^1 coefficient */
+    double c;  /* x^0 coefficient */
+} Quadfn;
+
+
+
+static void
+computeQuadraticFunction(xelval   const bvalue,
+                         xelval   const midvalue,
+                         xelval   const wvalue,
+                         xelval   const middle,
+                         xelval   const maxval,
+                         Quadfn * const functionP) {
+
+    /* The matrix equation we solve (for varMatrix) is
+
+       a * x = c
+    */
+    double ** a;
+    double c[3];
+    double x[3];
+    const char * error;
+
+    MALLOCARRAY2_NOFAIL(a, 3, 3);
+
+    a[0][0] = SQR(bvalue);   a[0][1] = bvalue;   a[0][2] = 1.0;
+    a[1][0] = SQR(midvalue); a[1][1] = midvalue; a[1][2] = 1.0;
+    a[2][0] = SQR(wvalue);   a[2][1] = wvalue;   a[2][2] = 1.0;
+        
+    c[0] = 0.0;
+    c[1] = middle;
+    c[2] = maxval;
+    
+    pm_solvelineareq(a, x, c, 3, &error);
+
+    if (error) {
+        pm_error("Cannot fit a quadratic function to the points "
+                 "(%u, %u), (%u, %u), and (%u, %u).  %s",
+                 bvalue, 0, midvalue, middle, wvalue, maxval, error);
+        pm_strfree(error);
+    } else {
+        functionP->a = x[0];
+        functionP->b = x[1];
+        functionP->c = x[2];
+    }
+
+    pm_freearray2((void **)a);
 }
 
 
 
 static void
-computeTransferFunction(xelval            const bvalue, 
-                        xelval            const wvalue,
-                        xelval            const maxval,
-                        xelval **         const newBrightnessP) {
+computeQuadraticTransfer(xelval   const bvalue,
+                         xelval   const midvalue,
+                         xelval   const wvalue,
+                         float    const middleNorm,
+                         xelval   const maxval,
+                         bool     const verbose,
+                         xelval * const newBrightness) {
+/*----------------------------------------------------------------------------
+   Map the middle brightnesses (the ones that don't get clipped to full dark
+   or full bright, i.e. from 'bvalue' to 'wvalue') quadratically onto
+   0..maxval, such that 'bvalue' maps to 0, 'wvalue' maps to 'maxval, and
+   'midvalue' maps to the normalized value 'middleNorm' (i.e. the actual
+   xelval middleNorm * maxval).
+
+   Set this mapping in newBrightness[].
+-----------------------------------------------------------------------------*/
+    xelval const middle = ROUNDU(middleNorm * maxval);
+
+    /* Computing this function is just the task of finding a parabola that
+       passes through 3 given points:
+        
+           (bvalue, 0)
+           (midvalue, middle)
+           (wvalue, maxval)
+
+       We do that by solving the system of three linear equations in
+       in 3 variables.  The 3 variables are the coefficients of the
+       quadratic function we're looking for -- A, B, and C in this:
+
+           NEWVAL = A * OLDVAL^2 + B * OLDVAL + C
+
+       The three equations of the system are:
+
+           0      = A * bvalue^2   + B * bvalue   + C
+           middle = A * midvalue^2 + B * midvalue + C
+           maxval = A * wvalue^2   + B * wvalue   + C
+
+       Expressed in matrix form:
+
+          [ bvalue^2   bvalue   1 ]   [ A ]   [ 0      ]
+          [ midvalue^2 midvalue 1 ] * [ B ] = [ middle ]
+          [ wvalue^2   wvalue   1 ]   [ C ]   [ maxval ]
+
+       So we solve that for A, B, and C.
+
+       With those coefficients, we have the quadratic function, and we
+       simple apply it to every old sample value I in the range to get the
+       new:
+
+           newBrightness[I] = A * I^2 + B * I + C
+    */
+
+    Quadfn xfer;
+
+    computeQuadraticFunction(bvalue, midvalue, wvalue, middle, maxval,
+                             &xfer);
+
+
+    if (verbose)
+        pm_message("Transfer function is %f * s^2 + %f * s + %f",
+                   xfer.a, xfer.b, xfer.c);
+
+    {
+        xelval i;
+        for (i = bvalue; i <= wvalue; ++i)
+            newBrightness[i] =
+                MIN(ROUNDU(xfer.a * SQR(i) + xfer.b * i + xfer.c), maxval);
+    }
+}
+
+
+
+static void
+computeTransferFunction(bool      const quadratic,
+                        xelval    const bvalue, 
+                        xelval    const midvalue, 
+                        xelval    const wvalue,
+                        float     const middle,
+                        xelval    const maxval,
+                        bool      const verbose,
+                        xelval ** const newBrightnessP) {
 /*----------------------------------------------------------------------------
    Compute the transfer function, i.e. the array *newBrightnessP such that
    (*newBrightnessP)[x] is the brightness of the xel that should replace a
@@ -524,9 +787,16 @@ computeTransferFunction(xelval            const bvalue,
 
    'bvalue' is the highest brightness that should map to zero brightness;
    'wvalue' is the lowest brightness that should map to full brightness.
-   brightnesses in between should be stretched linearly.  (That stretching
-   could conceivably result in more brightnesses mapping to zero and full
-   brightness, due to rounding).
+
+   If 'quadratic' is false, brightnesses in between should be stretched
+   linearly.  Otherwise, brightness 'midvalue' should map to brightness
+   'middle' (which is expressed on a 0..1 normalized scale) and brightnesses
+   should be stretched according to a quadratic polynomial that includes those
+   3 points.
+
+   This stretching could conceivably result in more brightnesses mapping to
+   zero and full brightness that 'bvalue' and 'wvalue' demand, because of
+   rounding.
 
    Define function only for values 0..maxval.
 -----------------------------------------------------------------------------*/
@@ -543,23 +813,13 @@ computeTransferFunction(xelval            const bvalue,
         for (i = 0; i < bvalue; ++i)
             newBrightness[i] = 0;
 
-    /* Map the middle brightnesses linearly onto 0..maxval */
-    {
-        unsigned int const range = wvalue - bvalue;
-        unsigned int val;
-        /* The following for loop is a hand optimization of this one:
-           for (i = bvalue; i <= wvalue; ++i)
-             newBrightness[i] = (i-bvalue)*maxval/range);
-           (with proper rounding)
-        */
-        for (i = bvalue, val = range/2; 
-             i <= wvalue; 
-             ++i, val += maxval)
-            newBrightness[i] = MIN(val / range, maxval);
-
-        assert(newBrightness[bvalue] == 0);
-        assert(newBrightness[wvalue] == maxval);
-    }
+    /* Map the middle brightnesses onto 0..maxval */
+    
+    if (quadratic)
+        computeQuadraticTransfer(bvalue, midvalue, wvalue, middle, maxval,
+                                 verbose, newBrightness);
+    else
+        computeLinearTransfer(bvalue, wvalue, maxval, newBrightness);
 
     /* Clip the highest brightnesses to maxval */
     for (i = wvalue+1; i <= maxval; ++i)
@@ -589,7 +849,7 @@ brightScaler(xel               const p,
              
     switch (brightMethod) {
     case BRIGHT_LUMINOSITY:
-        oldBrightness = PPM_LUMIN(p);
+        oldBrightness = ppm_luminosity(p);
         break;
     case BRIGHT_COLORVALUE:
         oldBrightness = ppm_colorvalue(p);
@@ -653,6 +913,25 @@ writeRowNormalized(xel *             const xelrow,
 
 
 
+static void
+reportTransferParm(bool   const quadratic,
+                   xelval const bvalue,
+                   xelval const midvalue,
+                   xelval const wvalue,
+                   xelval const maxval,
+                   float  const middle) {
+
+    if (quadratic)
+        pm_message("remapping %u..%u..%u to %u..%u..%u",
+                   bvalue, midvalue, wvalue,
+                   0, ROUNDU(maxval*middle), maxval);
+    else
+        pm_message("remapping %u..%u to %u..%u",
+                   bvalue, wvalue, 0, maxval);
+}
+
+
+
 int
 main(int argc, const char *argv[]) {
 
@@ -661,20 +940,21 @@ main(int argc, const char *argv[]) {
     pm_filepos imagePos;
     xelval maxval;
     int rows, cols, format;
-    xelval bvalue, wvalue;
+    bool quadratic;
+    xelval bvalue, midvalue, wvalue;
     
     pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr_seekable(cmdline.inputFilespec);
+    ifP = pm_openr_seekable(cmdline.inputFileName);
 
     /* Rescale so that bvalue maps to 0, wvalue maps to maxval. */
     pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
     pm_tell2(ifP, &imagePos, sizeof(imagePos));
 
     computeEndValues(ifP, cols, rows, maxval, format, cmdline, 
-                     &bvalue, &wvalue);
+                     &bvalue, &wvalue, &quadratic, &midvalue);
     {
         xelval * newBrightness;
         int row;
@@ -685,9 +965,13 @@ main(int argc, const char *argv[]) {
 
         xelrow = pnm_allocrow(cols);
 
-        pm_message("remapping %u..%u to %u..%u", bvalue, wvalue, 0, maxval);
+        reportTransferParm(quadratic, bvalue, midvalue, wvalue, maxval,
+                           cmdline.middle);
 
-        computeTransferFunction(bvalue, wvalue, maxval, &newBrightness);
+        
+        computeTransferFunction(quadratic, bvalue, midvalue, wvalue,
+                                cmdline.middle, maxval, cmdline.verbose,
+                                &newBrightness);
 
         pm_seek2(ifP, &imagePos, sizeof(imagePos));
         pnm_writepnminit(stdout, cols, rows, maxval, format, 0);
@@ -704,6 +988,9 @@ main(int argc, const char *argv[]) {
         pnm_freerow(rowbuf);
         pnm_freerow(xelrow);
     } 
-   pm_close(ifP);
+    pm_close(ifP);
     return 0;
 }
+
+
+
diff --git a/editor/pnmpad.c b/editor/pnmpad.c
index 34672dc5..051f3895 100644
--- a/editor/pnmpad.c
+++ b/editor/pnmpad.c
@@ -11,6 +11,9 @@
 #include "pnm.h"
 
 #define MAX_WIDTHHEIGHT INT_MAX-10
+    /* The maximum width or height value we can handle without risking
+       arithmetic overflow
+    */
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -31,6 +34,8 @@ struct cmdlineInfo {
     unsigned int bottomSpec;
     float xalign;
     float yalign;
+    unsigned int mwidth;
+    unsigned int mheight;
     unsigned int white;     /* >0: pad white; 0: pad black */
     unsigned int verbose;
 };
@@ -51,7 +56,7 @@ parseCommandLine(int argc, const char ** argv,
 
     unsigned int option_def_index;
     unsigned int blackOpt;
-    unsigned int xalignSpec, yalignSpec;
+    unsigned int xalignSpec, yalignSpec, mwidthSpec, mheightSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
@@ -82,6 +87,10 @@ parseCommandLine(int argc, const char ** argv,
             &yalignSpec,           0);
     OPTENT3(0,   "black",     OPT_FLAG,    NULL,
             &blackOpt,           0);
+    OPTENT3(0,   "mwidth",    OPT_UINT,    &cmdlineP->mwidth,
+            &mwidthSpec,         0);
+    OPTENT3(0,   "mheight",   OPT_UINT,    &cmdlineP->mheight,
+            &mheightSpec,        0);
     OPTENT3(0,   "white",     OPT_FLAG,    NULL,
             &cmdlineP->white,    0);
     OPTENT3(0,   "verbose",   OPT_FLAG,    NULL,
@@ -91,7 +100,7 @@ parseCommandLine(int argc, const char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof opt, 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof opt, 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (blackOpt && cmdlineP->white)
@@ -147,6 +156,12 @@ parseCommandLine(int argc, const char ** argv,
     } else
         cmdlineP->yalign = 0.5;
 
+    if (!mwidthSpec)
+        cmdlineP->mwidth = 1;
+
+    if (!mheightSpec)
+        cmdlineP->mheight = 1;
+
     /* get optional input filename */
     if (argc-1 > 1)
         pm_error("This program takes at most 1 parameter.  You specified %d",
@@ -163,7 +178,7 @@ static void
 parseCommandLineOld(int argc, const char ** argv,
                     struct cmdlineInfo * const cmdlineP) {
 
-    /* This syntax was abandonned in February 2002. */
+    /* This syntax was abandoned in February 2002. */
     pm_message("Warning: old style options are deprecated!");
 
     cmdlineP->xsize = cmdlineP->ysize = 0;
@@ -229,13 +244,17 @@ parseCommandLineOld(int argc, const char ** argv,
 
 static void
 validateHorizontalSize(struct cmdlineInfo const cmdline,
-                       unsigned int const cols) {
-
-    unsigned int const xsize = cmdline.xsizeSpec ? cmdline.xsize : 0;
-    unsigned int const lpad  = cmdline.leftSpec  ? cmdline.left  : 0;
-    unsigned int const rpad  = cmdline.rightSpec ? cmdline.right : 0;
+                       unsigned int       const cols) {
+/*----------------------------------------------------------------------------
+   Abort the program if the padding parameters in 'cmdline', applied to
+   an image width 'cols', would result in numbers too large for us to
+   compute with easily.
+-----------------------------------------------------------------------------*/
+    unsigned int const lpad         = cmdline.leftSpec   ? cmdline.left   : 0;
+    unsigned int const rpad         = cmdline.rightSpec  ? cmdline.right  : 0;
+    unsigned int const mwidthMaxPad = cmdline.mwidth - 1;
 
-    if (xsize > MAX_WIDTHHEIGHT)
+    if (cmdline.xsizeSpec && cmdline.xsize > MAX_WIDTHHEIGHT)
         pm_error("The width value you specified is too large.");
 
     if (lpad > MAX_WIDTHHEIGHT)
@@ -244,66 +263,167 @@ validateHorizontalSize(struct cmdlineInfo const cmdline,
     if (rpad > MAX_WIDTHHEIGHT)
         pm_error("The right padding value you specified is too large.");
 
-    if ((double) cols + (double) lpad + (double) rpad > MAX_WIDTHHEIGHT)
-        pm_error("Given padding value(s) makes output width too large.");
+    if ((double) cols +
+        (double) lpad + 
+        (double) rpad +
+        (double) mwidthMaxPad > MAX_WIDTHHEIGHT)
+        pm_error("Given padding parameters make output width too large "
+                 "for this program to compute");
+
+    if (cmdline.xsizeSpec &&
+        (double) cmdline.xsize + (double) mwidthMaxPad> MAX_WIDTHHEIGHT)
+        pm_error("Given padding parameters make output width too large "
+                 "for this program to compute");
 }
 
 
 
 static void
-computeHorizontalPadSizes(struct cmdlineInfo const cmdline,
-                          unsigned int       const cols,
-                          unsigned int *     const lpadP,
-                          unsigned int *     const rpadP) {
-
-    validateHorizontalSize(cmdline, cols);
-
-    if (cmdline.xsizeSpec) {
-        if (cmdline.leftSpec && cmdline.rightSpec) {
-            if (cmdline.left + cols + cmdline.right < cmdline.xsize) {
-                pm_error("Left padding (%u), and right "
+computePadSizeBeforeMult(unsigned int   const unpaddedSize,
+                         bool           const sizeSpec,
+                         unsigned int   const sizeReq,
+                         bool           const begPadSpec,
+                         unsigned int   const begPadReq,
+                         bool           const endPadSpec,
+                         unsigned int   const endPadReq,
+                         double         const align,
+                         unsigned int * const begPadP,
+                         unsigned int * const endPadP) {
+/*----------------------------------------------------------------------------
+   Compute the padding on each end that would be required if user did not
+   request any "multiple" padding; i.e. he didn't say request e.g. that the
+   output width be a multiple of 10 pixels.
+-----------------------------------------------------------------------------*/
+    if (sizeSpec) {
+        if (begPadSpec && endPadSpec) {
+            if (begPadReq + unpaddedSize + endPadReq < unpaddedSize) {
+                pm_error("Beginning adding (%u), and end "
                          "padding (%u) are insufficient to bring the "
-                         "image width of %d up to %u.",
-                         cmdline.left, cmdline.right, cols, cmdline.xsize);
+                         "image size of %u up to %u.",
+                         begPadReq, endPadReq, unpaddedSize, sizeReq);
             } else {
-                *lpadP = cmdline.left;
-                *rpadP = cmdline.right;
+                *begPadP = begPadReq;
+                *endPadP = endPadReq;
             }
-        } else if (cmdline.leftSpec) {
-            *lpadP = cmdline.left;
-            *rpadP = MAX(cmdline.xsize, cmdline.left + cols) -
-                (cmdline.left + cols);
-        } else if (cmdline.rightSpec) {
-            *rpadP = cmdline.right;
-            *lpadP = MAX(cmdline.xsize, cols + cmdline.right) -
-                (cols + cmdline.right);
+        } else if (begPadSpec) {
+            *begPadP = begPadReq;
+            *endPadP = MAX(sizeReq, unpaddedSize + begPadReq) -
+                (begPadReq + unpaddedSize);
+        } else if (endPadSpec) {
+            *endPadP = endPadReq;
+            *begPadP = MAX(sizeReq, unpaddedSize + endPadReq) -
+                (unpaddedSize + endPadReq);
         } else {
-            if (cmdline.xsize > cols) {
-                *lpadP = ROUNDU((cmdline.xsize - cols) * cmdline.xalign);
-                *rpadP = cmdline.xsize - cols - *lpadP;
+            if (sizeReq > unpaddedSize) {
+                *begPadP = ROUNDU((sizeReq - unpaddedSize) * align);
+                *endPadP = sizeReq - unpaddedSize - *begPadP;
             } else {
-                *lpadP = 0;
-                *rpadP = 0;
+                *begPadP = 0;
+                *endPadP = 0;
             }
         }
     } else {
-        *lpadP = cmdline.leftSpec  ? cmdline.left  : 0;
-        *rpadP = cmdline.rightSpec ? cmdline.right : 0;
+        *begPadP = begPadSpec ? begPadReq : 0;
+        *endPadP = endPadSpec ? endPadReq : 0;
+    }
+}
+
+
+
+static void
+computePadSizesOneDim(unsigned int   const unpaddedSize,
+                      bool           const sizeSpec,
+                      unsigned int   const sizeReq,
+                      bool           const begPadSpec,
+                      unsigned int   const begPadReq,
+                      bool           const endPadSpec,
+                      unsigned int   const endPadReq,
+                      double         const align,
+                      unsigned int   const multiple,
+                      unsigned int * const begPadP,
+                      unsigned int * const endPadP) {
+/*----------------------------------------------------------------------------
+   Compute the number of pixels of padding needed before and after a row or
+   column ("before" means on the left side of a row or the top side of a
+   column).  Return them as *padBegP and *padEndP, respectively.
+
+   'unpaddedSize' is the size (width/height) of the row or column before
+   any padding.
+
+   The rest of the inputs are the padding parameters, equivalent to the
+   program's corresponding command line options.
+-----------------------------------------------------------------------------*/
+    unsigned int begPadBeforeMult, endPadBeforeMult;
+        /* The padding we would apply if user did not request multiple
+           padding (such as "make the output a multiple of 10 pixels")
+        */
+
+    computePadSizeBeforeMult(unpaddedSize, sizeSpec, sizeReq,
+                             begPadSpec, begPadReq, endPadSpec, endPadReq,
+                             align,
+                             &begPadBeforeMult, &endPadBeforeMult);
+
+    {
+        unsigned int const sizeBeforeMpad =
+            unpaddedSize + begPadBeforeMult + endPadBeforeMult;
+        unsigned int const paddedSize =
+            ROUNDUP(sizeBeforeMpad, multiple);
+        unsigned int const morePadNeeded = paddedSize - sizeBeforeMpad;
+        unsigned int const totalPadBeforeMult =
+            begPadBeforeMult + endPadBeforeMult;
+        double const begFrac =
+            totalPadBeforeMult > 0 ? 
+            (double)begPadBeforeMult / totalPadBeforeMult :
+            0.0;
+        unsigned int const addlMsizeBegPad = ROUNDU(morePadNeeded * begFrac);
+            /* # of pixels we have to add to the beginning to satisfy
+               user's desire for the final size to be a multiple of something
+            */
+        unsigned int const addlMsizeEndPad = morePadNeeded - addlMsizeBegPad;
+            /* Analogous to 'addlMsizeBegPad' */
+
+        *begPadP = begPadBeforeMult + addlMsizeBegPad;
+        *endPadP = endPadBeforeMult + addlMsizeEndPad;
     }
 }
 
 
 
 static void
+computeHorizontalPadSizes(struct cmdlineInfo const cmdline,
+                          unsigned int       const cols,
+                          unsigned int *     const lpadP,
+                          unsigned int *     const rpadP) {
+
+    validateHorizontalSize(cmdline, cols);
+
+    computePadSizesOneDim(cols,
+                          cmdline.xsizeSpec > 0, cmdline.xsize,
+                          cmdline.leftSpec > 0, cmdline.left,
+                          cmdline.rightSpec > 0, cmdline.right,
+                          cmdline.xalign,
+                          cmdline.mwidth,
+                          lpadP, rpadP);
+}
+
+
+
+static void
 validateVerticalSize(struct cmdlineInfo const cmdline,
                      unsigned int       const rows) {
+/*----------------------------------------------------------------------------
+   Abort the program if the padding parameters in 'cmdline', applied to
+   an image width 'cols', would result in numbers too large for us to
+   compute with easily.
+-----------------------------------------------------------------------------*/
+    unsigned int const tpad          =
+        cmdline.topSpec    ?  cmdline.top     : 0;
+    unsigned int const bpad          =
+        cmdline.bottomSpec ?  cmdline.bottom  : 0;
+    unsigned int const mheightMaxPad = cmdline.mheight - 1;
 
-    unsigned int const ysize = cmdline.ysizeSpec  ? cmdline.ysize  : 0;
-    unsigned int const tpad  = cmdline.topSpec    ? cmdline.top    : 0;
-    unsigned int const bpad  = cmdline.bottomSpec ? cmdline.bottom : 0;
-
-    if (ysize > MAX_WIDTHHEIGHT)
-        pm_error("The height value you specified is too large.");
+    if (cmdline.ysizeSpec && cmdline.ysize > MAX_WIDTHHEIGHT)
+        pm_error("The width value you specified is too large.");
 
     if (tpad > MAX_WIDTHHEIGHT)
         pm_error("The top padding value you specified is too large.");
@@ -311,8 +431,17 @@ validateVerticalSize(struct cmdlineInfo const cmdline,
     if (bpad > MAX_WIDTHHEIGHT)
         pm_error("The bottom padding value you specified is too large.");
 
-    if ((double) rows + (double) tpad + (double) bpad > MAX_WIDTHHEIGHT)
-        pm_error("Given padding value(s) makes output height too large.");
+    if ((double) rows +
+        (double) tpad +
+        (double) bpad +
+        (double) mheightMaxPad > MAX_WIDTHHEIGHT)
+        pm_error("Given padding parameters make output height too large "
+            "for this program to compute");
+
+    if (cmdline.ysizeSpec &&
+        (double) cmdline.ysize && (double) mheightMaxPad > MAX_WIDTHHEIGHT)
+        pm_error("Given padding parameters make output height too large "
+            "for this program to compute");
 }
 
 
@@ -325,38 +454,13 @@ computeVerticalPadSizes(struct cmdlineInfo const cmdline,
 
     validateVerticalSize(cmdline, rows);
 
-    if (cmdline.ysizeSpec) {
-        if (cmdline.topSpec && cmdline.bottomSpec) {
-            if (cmdline.bottom + rows + cmdline.top < cmdline.ysize) {
-                pm_error("Top padding (%u), and bottom "
-                         "padding (%u) are insufficient to bring the "
-                         "image height of %d up to %u.",
-                         cmdline.top, cmdline.bottom, rows, cmdline.ysize);
-            } else {
-                *tpadP = cmdline.top;
-                *bpadP = cmdline.bottom;
-            }
-        } else if (cmdline.topSpec) {
-            *tpadP = cmdline.top;
-            *bpadP = MAX(cmdline.ysize, cmdline.top + rows) -
-                (cmdline.top + rows);
-        } else if (cmdline.bottomSpec) {
-            *bpadP = cmdline.bottom;
-            *tpadP = MAX(cmdline.ysize, rows + cmdline.bottom) -
-                (rows + cmdline.bottom);
-        } else {
-            if (cmdline.ysize > rows) {
-                *bpadP = ROUNDU((cmdline.ysize - rows) * cmdline.yalign);
-                *tpadP = cmdline.ysize - rows - *bpadP;
-            } else {
-                *bpadP = 0;
-                *tpadP = 0;
-            }
-        }
-    } else {
-        *bpadP = cmdline.bottomSpec ? cmdline.bottom : 0;
-        *tpadP = cmdline.topSpec    ? cmdline.top    : 0;
-    }
+    computePadSizesOneDim(rows,
+                          cmdline.ysizeSpec > 0, cmdline.ysize,
+                          cmdline.topSpec > 0, cmdline.top,
+                          cmdline.bottomSpec > 0, cmdline.bottom,
+                          cmdline.yalign,
+                          cmdline.mheight,
+                          tpadP, bpadP);
 }
 
 
diff --git a/editor/pnmpaste.c b/editor/pnmpaste.c
index 123b0feb..c27e288c 100644
--- a/editor/pnmpaste.c
+++ b/editor/pnmpaste.c
@@ -1,4 +1,4 @@
-/* pnmpaste.c - paste a rectangle into a portable anymap
+/* pnmpaste.c - paste a rectangle into a PNM image
 **
 ** Copyright (C) 1989 by Jef Poskanzer.
 **
@@ -21,7 +21,7 @@
 
 enum boolOp {REPLACE, AND, OR, XOR /*, NAND, NOR, NXOR */ };
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -36,7 +36,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
@@ -65,7 +65,7 @@ parseCommandLine(int argc, const char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = TRUE;  /* We have parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof opt, 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof opt, 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (replaceOpt + andOpt + orOpt + xorOpt > 1)
@@ -108,7 +108,7 @@ static unsigned char
 leftBits(unsigned char const x,
          unsigned int  const n){
 /*----------------------------------------------------------------------------
-  Clear rightmost (8-n) bits, retain leftmost (=high) n bits.
+  'x' with the leftmost (high) n bits retained and the rest cleared to zero.
 -----------------------------------------------------------------------------*/
     assert(n <= 8);
 
@@ -121,7 +121,7 @@ static unsigned char
 rightBits(unsigned char const x,
           unsigned int  const n){
 /*----------------------------------------------------------------------------
-  Return rightmost (=low) n bits of x. (Clear the rest).
+  The rightmost 'n' bits of 'x'.
 -----------------------------------------------------------------------------*/
     assert(n <= 8);
 
@@ -146,6 +146,12 @@ insertDirect(FILE *          const ifP,
    'buffer' is a scratch buffer for our use, at least wide enough to hold
    a packed PBM row of 'ifP'.
 -----------------------------------------------------------------------------*/
+    /* We use pbm_readpbmrow_packed() to read whole bytes rounded up and merge
+       those into 'destrow', which means we update more than we're supposed to
+       if the image is not a multiple of 8 columns.  In that case, we then fix
+       up the last byte by replacing the bits from the original image that we
+       messed up.
+    */
     unsigned int  const colBytes  = pbm_packed_bytes(cols);
     unsigned int  const last      = colBytes - 1;
     unsigned char const origRight = destrow[last];
@@ -172,6 +178,10 @@ insertDirect(FILE *          const ifP,
         }
     }
 
+    /* destrow[] now contains garbage in all but the cols % 8 leftmost bits of
+       the last byte we touched.  Those are supposed to be unchanged from the
+       input, so we restore them now.
+    */
     if (cols % 8 > 0)
         destrow[last] = leftBits(destrow[last], cols % 8)
             | rightBits(origRight, 8 - cols % 8);
@@ -194,10 +204,10 @@ insertShift(FILE *          const ifP,
    buffer[] is wide enough to hold a packed PBM row of *ifP plus two
    bytes of margin.
 -----------------------------------------------------------------------------*/
-    unsigned int const shiftBytes = pbm_packed_bytes(cols + offset);
-    unsigned int const last = shiftBytes - 1;
-    unsigned char const origLeft  = destrow[0];
-    unsigned char const origRight = destrow[last];
+    unsigned int  const shiftByteCt = pbm_packed_bytes(cols + offset);
+    unsigned int  const last        = shiftByteCt - 1;
+    unsigned char const origLeft    = destrow[0];
+    unsigned char const origRight   = destrow[last];
 
     unsigned int const padOffset = (cols + offset) % 8;
 
@@ -209,7 +219,7 @@ insertShift(FILE *          const ifP,
 
     /* Note that buffer[0] is undefined. */
 
-    for (i = 0; i < shiftBytes; ++i) {
+    for (i = 0; i < shiftByteCt; ++i) {
         unsigned int  const rsh = offset;
         unsigned int  const lsh = 8-rsh;
         unsigned char const t = buffer[i] << lsh | buffer[i+1] >> rsh;
@@ -227,9 +237,9 @@ insertShift(FILE *          const ifP,
         }
     }
 
-    /* destrow[] now contains garbage in the 'offset' leftmost bits
-       and 8-offset rightmost bits.  Those are supposed to be unchanged
-       from the input, so we restore them now.
+    /* destrow[] now contains garbage in the 'offset' leftmost bits and
+       8-offset rightmost bits of the last byte we touched.  Those are
+       supposed to be unchanged from the input, so we restore them now.
     */
 
     destrow[0] = leftBits(origLeft, offset) |
@@ -257,11 +267,11 @@ pastePbm(FILE *       const fpInset,
 /*----------------------------------------------------------------------------
   Fast paste for PBM
 -----------------------------------------------------------------------------*/
-    unsigned char * const baserow = pbm_allocrow_packed(baseCols);
-    unsigned char * const buffer = pbm_allocrow_packed(insetCols+16);
-    int const shiftBytes = insertCol / 8;
-    unsigned int const shiftOffset = insertCol % 8;
-    int const baseColBytes = pbm_packed_bytes(baseCols);
+    unsigned char * const baserow       = pbm_allocrow_packed(baseCols);
+    unsigned char * const buffer        = pbm_allocrow_packed(insetCols+16);
+    unsigned int    const shiftByteCt   = insertCol / 8;
+    unsigned int    const shiftOffset   = insertCol % 8;
+    unsigned int    const baseColByteCt = pbm_packed_bytes(baseCols);
 
     unsigned int row;
 
@@ -272,16 +282,16 @@ pastePbm(FILE *       const fpInset,
         
         if (row >= insertRow && row < insertRow + insetRows) {
             if (shiftOffset == 0)
-                insertDirect(fpInset, &baserow[shiftBytes], insetCols,
+                insertDirect(fpInset, &baserow[shiftByteCt], insetCols,
                              insetFormat, operation, buffer);
             else
-                insertShift(fpInset, &baserow[shiftBytes], insetCols,
+                insertShift(fpInset, &baserow[shiftByteCt], insetCols,
                             insetFormat, shiftOffset, operation, buffer);
         }
 
         if (baseCols % 8 > 0)
-            baserow[baseColBytes-1]
-                = leftBits(baserow[baseColBytes-1] , baseCols % 8);
+            baserow[baseColByteCt-1]
+                = leftBits(baserow[baseColByteCt-1] , baseCols % 8);
 
         pbm_writepbmrow_packed(stdout, baserow, baseCols, 0);
     }
@@ -344,7 +354,7 @@ pasteNonPbm(FILE *       const fpInset,
 int
 main(int argc, const char ** argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * fpInset;
     FILE * fpBase;
     xelval maxvalInset, maxvalBase;
diff --git a/editor/pnmquant b/editor/pnmquant
index 5edbe85e..93d452cd 100755
--- a/editor/pnmquant
+++ b/editor/pnmquant
@@ -1,6 +1,29 @@
-#!/usr/bin/perl -w
+#!/bin/sh
 
 ##############################################################################
+# This is essentially a Perl program.  We exec the Perl interpreter specifying
+# this same file as the Perl program and use the -x option to cause the Perl
+# interpreter to skip down to the Perl code.  The reason we do this instead of
+# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is
+# that the user may have multiple Perl interpreters and the one he wants to
+# use is properly located in the PATH.  The user's choice of Perl interpreter
+# may be crucial, such as when the user also has a PERL5LIB environment
+# variable and it selects modules that work with only a certain main
+# interpreter program.
+#
+# An alternative some people use is to have /usr/bin/env as the script
+# interpreter.  We don't do that because we think the existence and
+# compatibility of /bin/sh is more reliable.
+#
+# Note that we aren't concerned about efficiency because the user who needs
+# high efficiency can use directly the programs that this program invokes.
+#
+##############################################################################
+
+exec perl -w -x -S -- "$0" "$@"
+
+#!/usr/bin/perl
+##############################################################################
 #                         pnmquant 
 ##############################################################################
 #  By Bryan Henderson, San Jose CA; December 2001.
@@ -11,7 +34,6 @@
 use strict;
 use English;
 use Getopt::Long;
-#use File::Temp "tempfile";  # not available before Perl 5.6.1
 use File::Spec;
 #use Fcntl ":seek";  # not available in Perl 5.00503
 use Fcntl;  # gets open flags
@@ -22,22 +44,26 @@ my ($SEEK_SET, $SEEK_CUR, $SEEK_END) = (0, 1, 2);
 
 sub tempFile($) {
 
-# Here's what we'd do if we could expect Perl 5.6.1 or later, instead
-# of calling this subroutine:
-#    my ($file, $filename) = tempfile("pnmquant_XXXX", 
-#                                     SUFFIX=>".pnm", 
-#                                     DIR=>File::Spec->tmpdir())
-#                                     UNLINK=>$TRUE);
-    my ($suffix) = @_;
-    my $fileName;
-    local *file;  # For some inexplicable reason, must be local, not my
-    my $i;
-    $i = 0;
-    do {
-        $fileName = File::Spec->tmpdir() . "/pnmquant_" . $i++ . $suffix;
-    } until sysopen(*file, $fileName, O_RDWR|O_CREAT|O_EXCL);
-
-    return(*file, $fileName);
+    # We trust Perl's File::Temp to do a better job of creating the temp
+    # file, but it doesn't exist before Perl 5.6.1.
+
+    if (eval { require File::Temp; 1 }) {
+        return File::Temp::tempfile("pnmquant_XXXX", 
+                                    SUFFIX=>".pnm", 
+                                    DIR=>File::Spec->tmpdir(),
+                                    UNLINK=>$TRUE);
+    } else {
+        my ($suffix) = @_;
+        my $fileName;
+        local *file;  # For some inexplicable reason, must be local, not my
+        my $i;
+        $i = 0;
+        do {
+            $fileName = File::Spec->tmpdir() . "/pnmquant_" . $i++ . $suffix;
+        } until sysopen(*file, $fileName, O_RDWR|O_CREAT|O_EXCL);
+
+        return(*file, $fileName);
+    }
 }
 
 
@@ -247,7 +273,7 @@ openSeekableAsStdin($cmdlineR->{infile});
 
 # Save Standard Output for our eventual output
 open(OLDOUT, ">&STDOUT");
-select(OLDOUT);  # avoids Perl bug where it says we never use STDOUT 
+select(OLDOUT);  # avoids Perl bug where it says we never use OLDOUT 
 
 
 my $mapfileSpec = makeColormap($cmdlineR->{ncolors}, 
diff --git a/editor/pnmquantall b/editor/pnmquantall
new file mode 100755
index 00000000..2f1a3adf
--- /dev/null
+++ b/editor/pnmquantall
@@ -0,0 +1,209 @@
+#!/bin/sh
+
+##############################################################################
+# This is essentially a Perl program.  We exec the Perl interpreter specifying
+# this same file as the Perl program and use the -x option to cause the Perl
+# interpreter to skip down to the Perl code.  The reason we do this instead of
+# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is
+# that the user may have multiple Perl interpreters and the one he wants to
+# use is properly located in the PATH.  The user's choice of Perl interpreter
+# may be crucial, such as when the user also has a PERL5LIB environment
+# variable and it selects modules that work with only a certain main
+# interpreter program.
+#
+# An alternative some people use is to have /usr/bin/env as the script
+# interpreter.  We don't do that because we think the existence and
+# compatibility of /bin/sh is more reliable.
+#
+# Note that we aren't concerned about efficiency because the user who needs
+# high efficiency can use directly the programs that this program invokes.
+#
+##############################################################################
+
+exec perl -w -x -S -- "$0" "$@"
+
+#!/usr/bin/perl
+##############################################################################
+#                                  pnmquantall  
+##############################################################################
+#
+# HISTORY:
+#
+# This was in the original 1989 Pbmplus as a C shell program.  In Netpbm 9.13
+# (April 2001), it was converted to Bash.  (Actually, it was thought to be
+# Bourne shell, but it used arrays).  In Netpbm 10.58 (March 2012), it was
+# converted to Perl for better portability.
+#
+# The 2012 Perl conversion also changed the name from Ppmquantall to
+# Pnmquantall.  It had already handled non-PPM input files for many years.
+#
+# The original program was more complex:  Because in those days Pnmcolormap
+# and Pnmremap did not exist, Ppmquantall concatenated all the input images
+# together and ran Ppmquant (later Pnmquant) on the combination.  It then
+# split the combination image apart to make one output image per input image.
+# Today, Pnmquant is just a combination of Pnmcolormap and Pnmremap, and
+# we are able to use them better separately in Ppmquantall: We still make
+# the combination image, but use it only to compute the colormap with
+# Pnmcolormap.  We then apply that colormap separately to each input image
+# to produce an output image.
+#
+# Bryan Henderson wrote the current version from scratch in March 2012
+# and contributed it to the public domain.
+#
+##############################################################################
+
+use strict;
+use warnings;
+use English;
+use Fcntl;  # gets open flags
+use File::Copy;
+
+my $TRUE=1; my $FALSE = 0;
+
+
+
+sub parseArgs($$$$) {
+    my ($argvR, $extR, $newColorCtR, $fileNamesR) = @_;
+
+    my @argv = @{$argvR};
+
+    my $firstArgPos;
+
+    if (@argv > 0 && $argv[0] eq "-ext") {
+        if (@argv < 2) {
+            print STDERR ("-ext requires a value\n");
+        exit(100);
+        } else {
+            $$extR = $argv[1];
+            $firstArgPos = 2;
+        }
+    } else {
+        $$extR = "";
+        $firstArgPos = 0;
+    }
+
+    if (@argv < $firstArgPos + 2) {
+        print STDERR ("Not enough arguments.  You need at least the number " .
+                      "of colors and one file name\n");
+        exit(100);
+    }
+    
+    $$newColorCtR = $argv[$firstArgPos];
+
+    @{$fileNamesR} = @argv[$firstArgPos + 1 .. @argv-1];
+}
+
+
+
+sub tempFile($) {
+
+    # We trust Perl's File::Temp to do a better job of creating the temp
+    # file, but it doesn't exist before Perl 5.6.1.
+
+    if (eval { require File::Temp; 1 }) {
+        return File::Temp::tempfile("pnmquant_XXXX", 
+                                    SUFFIX=>".pnm", 
+                                    DIR=>File::Spec->tmpdir(),
+                                    UNLINK=>$TRUE);
+    } else {
+        my ($suffix) = @_;
+        my $fileName;
+        local *file;  # For some inexplicable reason, must be local, not my
+        my $i;
+        $i = 0;
+        do {
+            $fileName = File::Spec->tmpdir() . "/pnmquant_" . $i++ . $suffix;
+        } until sysopen(*file, $fileName, O_RDWR|O_CREAT|O_EXCL);
+
+        return(*file, $fileName);
+    }
+}
+
+
+
+sub makeColorMap($$$$) {
+    my ($fileNamesR, $newColorCt, $colorMapFileName, $errorR) = @_;
+
+    my $pnmcatCmd = "pnmcat -topbottom -white -jleft @{$fileNamesR}";
+
+    my $pnmcolormapCmd = "pnmcolormap $newColorCt";
+
+    my $makeMapCmd = "$pnmcatCmd | $pnmcolormapCmd >$colorMapFileName";
+
+    my $termStatus = system($makeMapCmd);
+
+    if ($termStatus != 0) {
+        $$errorR =
+            "Shell command to create the color map failed:  '$makeMapCmd'.";
+    }
+}
+
+
+ 
+sub remapFiles($$$$) {
+    my ($fileNamesR, $colorMapFileName, $ext, $errorR) = @_;
+
+    my ($outputFh, $outputFileName) = tempFile("pnm");
+    if (!defined($outputFh)) {
+        $$errorR = "Unable to create temporary file.  Errno=$ERRNO";
+    } else {
+        for (my $i = 0; $i < @{$fileNamesR} && !$$errorR; ++$i) {
+            my $inFileName = $fileNamesR->[$i];
+
+            my $pnmremapCmd =
+                "pnmremap '$inFileName' -mapfile=$colorMapFileName " .
+                ">$outputFileName";
+
+            my $pnmremapTermStatus = system($pnmremapCmd);
+
+            if ($pnmremapTermStatus != 0) {
+                $errorR =
+                    "Shell command to quantize '$inFileName'  failed:  " .
+                    "'$pnmremapCmd'";
+            } else {
+                my $newFileName = $inFileName . $ext;
+
+                unlink($newFileName);
+                File::Copy::move($outputFileName, $newFileName)
+                    or $errorR = "Rename to '$newFileName' failed.";
+            }
+        }
+        unlink($outputFileName);  # In case something failed
+    }
+}
+
+
+
+###############################################################################
+#                             MAINLINE
+###############################################################################
+
+my $progError;
+
+parseArgs(\@ARGV, \my $ext, \my $newColorCt, \my @fileNames);
+
+my ($colorMapFh, $colorMapFileName) = tempFile("pnm");
+if (!defined($colorMapFh)) {
+    $progError = "Unable to create temporary file.  Errno=$ERRNO";
+}
+
+if (!$progError) {
+    makeColorMap(\@fileNames, $newColorCt, $colorMapFileName, \$progError);
+}
+print ("got color map\n");
+if (!$progError) {
+    remapFiles(\@fileNames, $colorMapFileName, $ext, \$progError);
+}
+
+my $exitStatus;
+
+if ($progError) {
+    print STDERR ("Failed.  $progError\n");
+    $exitStatus = 1;
+} else {
+    $exitStatus = 0;
+}
+
+unlink($colorMapFileName);
+
+exit($exitStatus);
diff --git a/editor/pnmremap.c b/editor/pnmremap.c
index db35e2c0..ed758aa3 100644
--- a/editor/pnmremap.c
+++ b/editor/pnmremap.c
@@ -30,17 +30,18 @@
 #include "nstring.h"
 #include "shhopt.h"
 #include "pam.h"
+#include "ppm.h"
 #include "pammap.h"
 
 #define MAXCOLORS 32767u
 
-enum missingMethod {
+enum MissingMethod {
     MISSING_FIRST,
     MISSING_SPECIFIED,
     MISSING_CLOSE
 };
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -48,7 +49,7 @@ struct cmdlineInfo {
     const char * mapFilespec;    /* Filespec of colormap file */
     unsigned int floyd;   /* Boolean: -floyd/-fs option */
     unsigned int norandom;
-    enum missingMethod missingMethod;
+    enum MissingMethod missingMethod;
     char * missingcolor;      
         /* -missingcolor value.  Null if not specified */
     unsigned int verbose;
@@ -57,8 +58,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine (int argc, char ** argv,
-                  struct cmdlineInfo *cmdlineP) {
+parseCommandLine (int argc, const char ** argv,
+                  struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -70,7 +71,7 @@ parseCommandLine (int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -82,24 +83,24 @@ parseCommandLine (int argc, char ** argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
     
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "floyd",        OPT_FLAG,   
-            NULL,                       &cmdlineP->floyd, 0);
-    OPTENT3(0,   "fs",           OPT_FLAG,   
-            NULL,                       &cmdlineP->floyd, 0);
-    OPTENT3(0,   "nofloyd",      OPT_FLAG,   
-            NULL,                       &nofloyd, 0);
-    OPTENT3(0,   "nofs",         OPT_FLAG,   
-            NULL,                       &nofloyd, 0);
-    OPTENT3(0,   "norandom",     OPT_FLAG,   
+    OPTENT3(0,   "floyd",          OPT_FLAG,   
+            NULL,                       &cmdlineP->floyd,    0);
+    OPTENT3(0,   "fs",             OPT_FLAG,   
+            NULL,                       &cmdlineP->floyd,    0);
+    OPTENT3(0,   "nofloyd",        OPT_FLAG,   
+            NULL,                       &nofloyd,            0);
+    OPTENT3(0,   "nofs",           OPT_FLAG,   
+            NULL,                       &nofloyd,            0);
+    OPTENT3(0,   "norandom",       OPT_FLAG,   
             NULL,                       &cmdlineP->norandom, 0);
     OPTENT3(0,   "firstisdefault", OPT_FLAG,   
-            NULL,                       &firstisdefault, 0);
-    OPTENT3(0,   "mapfile",      OPT_STRING, 
-            &cmdlineP->mapFilespec,    &mapfileSpec, 0);
-    OPTENT3(0,   "missingcolor", OPT_STRING, 
-            &cmdlineP->missingcolor,   &missingSpec, 0);
-    OPTENT3(0, "verbose",        OPT_FLAG,   NULL,                  
-            &cmdlineP->verbose,        0 );
+            NULL,                       &firstisdefault,     0);
+    OPTENT3(0,   "mapfile",        OPT_STRING, 
+            &cmdlineP->mapFilespec,    &mapfileSpec,         0);
+    OPTENT3(0,   "missingcolor",   OPT_STRING, 
+            &cmdlineP->missingcolor,   &missingSpec,         0);
+    OPTENT3(0, "verbose",          OPT_FLAG,   NULL,                  
+            &cmdlineP->verbose,                              0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -107,7 +108,7 @@ parseCommandLine (int argc, char ** argv,
 
     cmdlineP->missingcolor = NULL;  /* default value */
     
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (cmdlineP->floyd && nofloyd)
@@ -134,6 +135,8 @@ parseCommandLine (int argc, char ** argv,
         cmdlineP->inputFilespec = "-";
     else
         cmdlineP->inputFilespec = argv[1];
+
+    free(option_def);
 }
 
 
@@ -267,7 +270,7 @@ selectDepthAdjustment(const struct pam * const pamP,
     if (newDepth == pamP->depth)
         *adjustmentP = ADJUST_NONE;
     else {
-        if (stripeq(pamP->tuple_type, "RGB")) {
+        if (pm_stripeq(pamP->tuple_type, "RGB")) {
             if (newDepth != 1) {
                 pm_error("Map image depth of %u differs from input image "
                          "depth of %u, and the tuple type is RGB.  "
@@ -276,8 +279,8 @@ selectDepthAdjustment(const struct pam * const pamP,
                          newDepth, pamP->depth);
             } else
                 *adjustmentP = ADJUST_RGBTO1;
-        } else if (stripeq(pamP->tuple_type, "GRAYSCALE") ||
-                   stripeq(pamP->tuple_type, "BLACKANDWHITE")) {
+        } else if (pm_stripeq(pamP->tuple_type, "GRAYSCALE") ||
+                   pm_stripeq(pamP->tuple_type, "BLACKANDWHITE")) {
             if (newDepth != 3) {
                 pm_error("Map image depth of %u differs from input image "
                          "depth of %u, and the tuple type is GRAYSCALE "
@@ -748,7 +751,7 @@ lookupThroughHash(struct pam *            const pamP,
         } else 
             searchColormapClose(pamP, tuple, colorFinderP, colormapIndexP);
         if (*usehashP) {
-            bool fits;
+            int fits;
             pnm_addtotuplehash(pamP, colorhash, tuple, *colormapIndexP, 
                                &fits);
             if (!fits) {
@@ -1062,7 +1065,7 @@ remap(FILE *             const ifP,
    same as that of the input even though the individual pixels have different
    colors.
 -----------------------------------------------------------------------------*/
-    bool eof;
+    int eof;
     eof = FALSE;
     while (!eof) {
         struct pam inpam, outpam;
@@ -1102,7 +1105,14 @@ processMapFile(const char *   const mapFileName,
                tupletable *   const colormapP,
                unsigned int * const colormapSizeP,
                tuple *        const firstColorP) {
+/*----------------------------------------------------------------------------
+   Read a color map from the file named 'mapFileName'.  It's a map that
+   associates each color in that file with a unique whole number.  Return the
+   map as *colormapP, with the number of entries in it as *colormapSizeP.
 
+   Also determine the first color (top left) in the map file and return that
+   as *firstColorP.
+-----------------------------------------------------------------------------*/
     FILE * mapfile;
     struct pam mappam;
     tuple ** maptuples;
@@ -1142,7 +1152,7 @@ getSpecifiedMissingColor(struct pam * const pamP,
             specColor[PAM_GRN_PLANE] = PPM_GETG(color);
             specColor[PAM_BLU_PLANE] = PPM_GETB(color);
         } else if (pamP->depth == 1) {
-            specColor[0] = PPM_LUMIN(color);
+            specColor[0] = ppm_luminosity(color);
         } else {
             pm_error("You may not use -missing with a colormap that is not "
                      "of depth 1 or 3.  Yours has depth %u",
@@ -1155,9 +1165,9 @@ getSpecifiedMissingColor(struct pam * const pamP,
 
 
 int
-main(int argc, char * argv[] ) {
+main(int argc, const char * argv[] ) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     struct pam outpamCommon;
         /* Describes the output images.  Width and height fields are
@@ -1180,7 +1190,7 @@ main(int argc, char * argv[] ) {
            color (i.e. we'll choose an approximate match from the map).
         */
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -1216,3 +1226,6 @@ main(int argc, char * argv[] ) {
 
     return 0;
 }
+
+
+
diff --git a/editor/pnmrotate.c b/editor/pnmrotate.c
index ba37f4e0..da5de3a8 100644
--- a/editor/pnmrotate.c
+++ b/editor/pnmrotate.c
@@ -80,7 +80,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = TRUE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!backgroundSpec)
diff --git a/editor/pnmscalefixed.c b/editor/pnmscalefixed.c
index 8deb65d7..884ca315 100644
--- a/editor/pnmscalefixed.c
+++ b/editor/pnmscalefixed.c
@@ -22,8 +22,9 @@
 #include <math.h>
 
 #include "pm_c_util.h"
-#include "pnm.h"
+#include "mallocvar.h"
 #include "shhopt.h"
+#include "pnm.h"
 
 /* The pnm library allows us to code this program without branching cases
    for PGM and PPM, but we do the branch anyway to speed up processing of 
@@ -61,27 +62,29 @@ parse_command_line(int argc, char ** argv,
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optStruct *option_def = malloc(100*sizeof(optStruct));
-        /* Instructions to OptParseOptions2 on how to parse our options.
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
          */
-    optStruct2 opt;
+    optStruct3 opt;
 
     unsigned int option_def_index;
     int xysize, xsize, ysize, pixels;
     int reduce;
     float xscale, yscale, scale_parm;
 
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENTRY(0,   "xsize",     OPT_UINT,    &xsize,         0);
-    OPTENTRY(0,   "width",     OPT_UINT,    &xsize,         0);
-    OPTENTRY(0,   "ysize",     OPT_UINT,    &ysize,         0);
-    OPTENTRY(0,   "height",    OPT_UINT,    &ysize,         0);
-    OPTENTRY(0,   "xscale",    OPT_FLOAT,   &xscale,        0);
-    OPTENTRY(0,   "yscale",    OPT_FLOAT,   &yscale,        0);
-    OPTENTRY(0,   "pixels",    OPT_UINT,    &pixels,        0);
-    OPTENTRY(0,   "xysize",    OPT_FLAG,    &xysize,        0);
-    OPTENTRY(0,   "verbose",   OPT_FLAG,    &cmdline_p->verbose,        0);
-    OPTENTRY(0,   "reduce",    OPT_UINT,    &reduce,        0);
+    OPTENT3(0,   "xsize",     OPT_UINT,    &xsize,              NULL, 0);
+    OPTENT3(0,   "width",     OPT_UINT,    &xsize,              NULL, 0);
+    OPTENT3(0,   "ysize",     OPT_UINT,    &ysize,              NULL, 0);
+    OPTENT3(0,   "height",    OPT_UINT,    &ysize,              NULL, 0);
+    OPTENT3(0,   "xscale",    OPT_FLOAT,   &xscale,             NULL, 0);
+    OPTENT3(0,   "yscale",    OPT_FLOAT,   &yscale,             NULL, 0);
+    OPTENT3(0,   "pixels",    OPT_UINT,    &pixels,             NULL, 0);
+    OPTENT3(0,   "xysize",    OPT_FLAG,    &xysize,             NULL, 0);
+    OPTENT3(0,   "verbose",   OPT_FLAG,    &cmdline_p->verbose, NULL, 0);
+    OPTENT3(0,   "reduce",    OPT_UINT,    &reduce,             NULL, 0);
 
     /* Set the defaults. -1 = unspecified */
     xsize = -1;
@@ -97,7 +100,7 @@ parse_command_line(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 */
 
-    optParseOptions2(&argc, argv, opt, 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (xsize == 0)
@@ -253,7 +256,7 @@ compute_output_dimensions(const struct cmdline_info cmdline,
             *newrowsP = rows;
     }    
 
-    /* If the calculations above yielded (due to rounding) a zero 
+    /* If the calculations above yielded (because of rounding) a zero 
        dimension, we fudge it up to 1.  We do this rather than considering
        it a specification error (and dying) because it's friendlier to 
        automated processes that work on arbitrary input.  It saves them
@@ -280,7 +283,7 @@ horizontal_scale(const xel inputxelrow[], xel newxelrow[],
    output rows.
 
    *stretchP is the number of columns (could be fractional) on the right 
-   that we had to fill by stretching due to rounding problems.
+   that we had to fill by stretching because of rounding problems.
 -----------------------------------------------------------------------------*/
     long r, g, b;
     long fraccoltofill, fraccolleft;
@@ -571,7 +574,7 @@ main(int argc, char **argv ) {
             
             if (cmdline.verbose && row == 0 && stretch != 0)
                 pm_message("%d/%d = %f right columns filled by stretching "
-                           "due to arithmetic imprecision", 
+                           "because of arithmetic imprecision", 
                            stretch, SCALE, (float) stretch/SCALE);
             
             pnm_writepnmrow(stdout, newxelrow, newcols, 
diff --git a/editor/pnmshear.c b/editor/pnmshear.c
index 359df299..99fa3026 100644
--- a/editor/pnmshear.c
+++ b/editor/pnmshear.c
@@ -1,4 +1,4 @@
-/* pnmshear.c - read a portable anymap and shear it by some angle
+ /* pnmshear.c - read a portable anymap and shear it by some angle
 **
 ** Copyright (C) 1989, 1991 by Jef Poskanzer.
 **
@@ -17,6 +17,7 @@
 #include <string.h>
 
 #include "pm_c_util.h"
+#include "mallocvar.h"
 #include "ppm.h"
 #include "pnm.h"
 #include "shhopt.h"
@@ -24,11 +25,13 @@
 #define SCALE 4096
 #define HALFSCALE 2048
 
-struct cmdline_info {
+
+
+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 * inputFileName;   /* Name of input file */
     double       angle;           /* requested shear angle, in radians */
     unsigned int noantialias;     /* -noantialias option */
     const char * background;      /* NULL if none */
@@ -37,15 +40,17 @@ struct cmdline_info {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdline_info *cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo *cmdlineP) {
 
     optStruct3 opt;
     unsigned int option_def_index = 0;
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
 
     unsigned int backgroundSpec;
 
+    MALLOCARRAY(option_def, 100);
+
     OPTENT3(0, "noantialias",      OPT_FLAG,  NULL, &cmdlineP->noantialias, 0);
     OPTENT3(0, "background",       OPT_STRING, &cmdlineP->background,
             &backgroundSpec, 0);
@@ -54,7 +59,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;
     opt.allowNegNum = TRUE;
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
 
     if (!backgroundSpec)
         cmdlineP->background = NULL;
@@ -68,15 +73,16 @@ parseCommandLine(int argc, char ** argv,
             pm_error("Angle argument is not a valid floating point number: "
                      "'%s'", argv[1]);
         if (argc-1 < 2)
-            cmdlineP->input_filespec = "-";
+            cmdlineP->inputFileName = "-";
         else {
-            cmdlineP->input_filespec = argv[2];
+            cmdlineP->inputFileName = argv[2];
             if (argc-1 > 2)
                 pm_error("too many arguments (%d).  "
                          "The only arguments are shear angle and filespec.",
                          argc-1);
         }
     }
+    free(option_def);
 }
 
 
@@ -200,7 +206,7 @@ backgroundColor(const char * const backgroundColorName,
 
 
 int
-main(int argc, char * argv[]) {
+main(int argc, const char * argv[]) {
 
     FILE * ifP;
     xel * xelrow;
@@ -212,13 +218,13 @@ main(int argc, char * argv[]) {
     xelval maxval, newmaxval;
     double shearfac;
 
-    struct cmdline_info cmdline;
+    struct CmdlineInfo cmdline;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.input_filespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
     pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
     xelrow = pnm_allocrow(cols);
@@ -256,7 +262,7 @@ main(int argc, char * argv[]) {
             shearCols = (rows - row) * shearfac;
 
         shearRow(xelrow, cols, newxelrow, newcols, 
-                  shearCols, format, bgxel, !cmdline.noantialias);
+                 shearCols, format, bgxel, !cmdline.noantialias);
 
         pnm_writepnmrow(stdout, newxelrow, newcols, newmaxval, newformat, 0);
     }
@@ -266,3 +272,6 @@ main(int argc, char * argv[]) {
 
     return 0;
 }
+
+
+
diff --git a/editor/pnmsmooth.c b/editor/pnmsmooth.c
index 16d8ec33..a76bd42b 100644
--- a/editor/pnmsmooth.c
+++ b/editor/pnmsmooth.c
@@ -21,6 +21,7 @@
 
 
 #include <unistd.h>
+#include <assert.h>
 #include <string.h>
 #include <errno.h>
 
@@ -28,6 +29,7 @@
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "nstring.h"
+#include "pm.h"   /* For pm_plain_output */
 #include "pm_system.h"
 #include "pnm.h"
 
@@ -39,13 +41,13 @@ struct cmdlineInfo {
     const char * inputFilespec;  /* Filespec of input file */
     unsigned int width;
     unsigned int height;
-    const char * dump;
+    unsigned int dump;
 };
 
 
 
 static void
-parseCommandLine (int argc, char ** argv,
+parseCommandLine (int argc, const char ** argv,
                   struct cmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
@@ -58,19 +60,19 @@ parseCommandLine (int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
     unsigned int option_def_index;
 
-    unsigned int widthSpec, heightSpec, dumpSpec, sizeSpec;
+    unsigned int widthSpec, heightSpec, sizeSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
     
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "dump",          OPT_STRING,   
-            &cmdlineP->dump,            &dumpSpec, 0);
+    OPTENT3(0,   "dump",          OPT_FLAG,   
+            NULL,                       &cmdlineP->dump, 0);
     OPTENT3(0,   "width",         OPT_UINT,
             &cmdlineP->width,           &widthSpec, 0);
     OPTENT3(0,   "height",        OPT_UINT,
@@ -82,18 +84,17 @@ parseCommandLine (int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
+    free(option_def);
+
     if (!widthSpec)
         cmdlineP->width = 3;
 
     if (!heightSpec)
         cmdlineP->height = 3;
 
-    if (!dumpSpec)
-        cmdlineP->dump = NULL;
-
     if (sizeSpec) {
         /* -size is strictly for backward compatibility.  This program
            used to use a different command line processor and had
@@ -142,69 +143,122 @@ parseCommandLine (int argc, char ** argv,
 }
 
 
-
 static void
-writeConvolutionImage(FILE *       const cofp,
-                      unsigned int const cols,
-                      unsigned int const rows,
-                      int          const format) {
-
-    xelval const convmaxval = rows * cols * 2;
-        /* normalizing factor for our convolution matrix */
-    xelval const g = rows * cols + 1;
-        /* weight of all pixels in our convolution matrix */
-    int row;
-    xel *outputrow;
-
-    if (convmaxval > PNM_OVERALLMAXVAL)
-        pm_error("The convolution matrix is too large.  "
-                 "Width x Height x 2\n"
-                 "must not exceed %d and it is %d.",
-                 PNM_OVERALLMAXVAL, convmaxval);
-
-    pnm_writepnminit(cofp, cols, rows, convmaxval, format, 0);
-    outputrow = pnm_allocrow(cols);
-
-    for (row = 0; row < rows; ++row) {
-        unsigned int col;
-        for (col = 0; col < cols; ++col)
-            PNM_ASSIGN1(outputrow[col], g);
-        pnm_writepnmrow(cofp, outputrow, cols, convmaxval, format, 0);
+validateComputableDimensions(unsigned int const cols,
+                             unsigned int const rows){
+/*----------------------------------------------------------------------------
+   Make sure that convolution matrix dimensions are small enough to
+   represent in a string.
+-----------------------------------------------------------------------------*/
+    unsigned int const maxStringLength = INT_MAX - 2 -6;
+
+    if (cols >  maxStringLength / rows / 2 )
+       pm_error("The convolution matrix size %u x %u is too large.",
+                cols, rows);
+}
+
+
+
+static const char *
+makeConvolutionKernel(unsigned int const cols,
+                      unsigned int const rows) {
+/*----------------------------------------------------------------------------
+  Return a value for a Pnmconvol '-matrix' option that specifies a
+  convolution kernel with with dimensions 'cols' by 'rows' with 1
+  for every weight.  Caller can use this with Pnmconvol -normalize.
+-----------------------------------------------------------------------------*/
+    unsigned int const maxOptSize = cols * rows * 2;
+
+    char * matrix;
+
+    MALLOCARRAY(matrix, maxOptSize);
+
+    if (matrix == NULL)
+        pm_error("Could not get memory for a %u x %u convolution matrix",
+                 rows, cols);
+    else {
+        unsigned int row;
+        unsigned int cursor;
+     
+        for (row = 0, cursor = 0; row < rows; ++row) {
+            unsigned int col;
+
+            if (row > 0)
+                matrix[cursor++] = ';';
+
+            for (col = 0; col < cols; ++col) {
+                if (col > 0)
+                    matrix[cursor++] = ',';
+
+                matrix[cursor++] = '1';
+            }
+        }
+        assert(cursor < maxOptSize);
+        matrix[cursor] = '\0';
     }
-    pnm_freerow(outputrow);
+
+    return matrix;
+}
+
+
+
+static void
+validateMatrixOptSize(unsigned int const rows,
+                      unsigned int const cols) {
+
+    /* If the user accidentally specifies absurdly large values for the
+       convolution matrix size, the failure mode can be a confusing message
+       resulting from the 'pnmconvol' arguments being too large.  To try
+       to be more polite in that case, we apply an arbitrary limit on the
+       size of the option here.
+    */
+
+    if (rows * cols > 5000)
+        pm_error("Convolution matrix dimensions %u x %u are too large "
+                 "to be useful, so we assume you made a mistake.  "
+                 "We refuse to use numbers this large because they might "
+                 "cause excessive resource use that would cause failures "
+                 "whose cause would not be obvious to you", cols, rows);
 }
 
 
 
 int
-main(int argc, char ** argv) {
+main(int argc, const char ** argv) {
 
     struct cmdlineInfo cmdline;
-    FILE * convFileP;
-    const char * tempfileName;
+    const char * matrixOptValue;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    if (cmdline.dump)
-        convFileP = pm_openw(cmdline.dump);
-    else
-        pm_make_tmpfile(&convFileP, &tempfileName);
-        
-    writeConvolutionImage(convFileP, cmdline.width, cmdline.height,
-                          PGM_FORMAT);
+    validateComputableDimensions(cmdline.width, cmdline.height);
+    validateMatrixOptSize(cmdline.width, cmdline.height);
 
-    pm_close(convFileP);
+    matrixOptValue = makeConvolutionKernel(cmdline.width, cmdline.height);
 
     if (cmdline.dump) {
-        /* We're done.  Convolution image is in user's file */
+        pm_error("-dump option no longer exists.  "
+                 "You don't need it because you can now do uniform "
+                 "convolution easily with the -matrix and -normalize "
+                 "options of 'pnmconvol'.");
     } else {
+        const char * const plainOpt = pm_plain_output ? "-plain" : NULL;
+
+        const char * matrixOpt;
+
+        pm_asprintf(&matrixOpt, "-matrix=%s", matrixOptValue);
+
+        pm_message("Running Pnmconvol -normalize %s", matrixOpt);
+
         pm_system_lp("pnmconvol", NULL, NULL, NULL, NULL,
-                     "pnmconvol", tempfileName, cmdline.inputFilespec, NULL);
+                     "pnmconvol", matrixOpt, cmdline.inputFilespec,
+                     "-normalize", "-quiet", plainOpt, NULL);
 
-        unlink(tempfileName);
-        strfree(tempfileName);
+        pm_strfree(matrixOpt);
     }
+    pm_strfree(matrixOptValue);
+
     return 0;
 }
diff --git a/editor/pnmstitch.c b/editor/pnmstitch.c
index 45aee2f4..849445fb 100644
--- a/editor/pnmstitch.c
+++ b/editor/pnmstitch.c
@@ -77,7 +77,7 @@
  *      - Add RotateCrop filter algorithm (in-memory copy of image,
  *        detect least loss horizontal crop on a rotated image).
  *      - pnmstitch should be generalized to handle transformation
- *        occuring on the left image, currently it blends assuming
+ *        occurring on the left image, currently it blends assuming
  *        that there is no transformation effects on the left image.
  *      - user selectable blending algorithms?
  */
@@ -233,7 +233,7 @@ parseCommandLine ( int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -265,7 +265,7 @@ parseCommandLine ( int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!widthSpec) {
@@ -370,7 +370,7 @@ static void
 free_image(Image * image)
 {
     if (image->name) {
-        strfree(image->name);
+        pm_strfree(image->name);
         image->name = NULL;     
     }
     if (image->tuple) {
@@ -405,14 +405,14 @@ openWithPossibleExtension(const char *  const baseName,
         
         const char * trialName;
         
-        asprintfN(&trialName, "%s%s", baseName, extlist[extIndex]);
+        pm_asprintf(&trialName, "%s%s", baseName, extlist[extIndex]);
         
         ifP = fopen(trialName, "rb");
         
         if (ifP)
             *filenameP = trialName;
         else
-            strfree(trialName);
+            pm_strfree(trialName);
     }
     if (!ifP) 
         pm_error ("Failed to open input file named '%s' "
@@ -470,7 +470,7 @@ writeinit(Image * image)
 {
     if (streq(image->name, "-")) {
         image->pam.file = stdout;
-        strfree(image->name);
+        pm_strfree(image->name);
         image->name = strdup("<stdout>");
     } else {
         image->pam.file = pm_openw(image->name);
@@ -893,7 +893,7 @@ stitchOneRow(Image *    const Left,
      *  We scale the overlap of the left and right images, we need to
      * discover and hold on to the left edge of the right image to
      * determine the rate at which we blend. Most (7/8) of the blending
-     * occurs in the first half of the overlap to reduce the occurences
+     * occurs in the first half of the overlap to reduce the occurrences
      * of blending artifacts. If there is no overlap, the image present
      * has no blending activity, this is determined by the black
      * background and is not through an alpha layer to help reduce
@@ -1393,7 +1393,7 @@ LinearConstrain(Stitcher * me, int x, int y, int width, int height)
  *  width sliver of the left hand side of the right image and compare
  *  the sample to the left hand image. Accuracy is honored over speed.
  *  The image overlap is expected between 7/16 to 1/16 in the horizontal
- *  position, and a minumum of 5/8 in the vertical dimension.
+ *  position, and a minimum of 5/8 in the vertical dimension.
  *
  *  Blind alleys:
  *      - reduced resolution can match in totally wrong regions,
@@ -2071,7 +2071,7 @@ BiLinearConstrain(Stitcher * me, int x, int y, int width, int height)
  *  width sliver of the left hand side of the right image and compare
  *  the sample to the left hand image. Accuracy is honored over speed.
  *  The image overlap is expected between 7/16 to 1/16 in the horizontal
- *  position, and a minumum of 5/8 in the vertical dimension.
+ *  position, and a minimum of 5/8 in the vertical dimension.
  *
  *  Blind alleys:
  *      - Tried a simpler constraint for right side to be `back'
diff --git a/editor/pnmtile.c b/editor/pnmtile.c
index 21512b36..5ec53415 100644
--- a/editor/pnmtile.c
+++ b/editor/pnmtile.c
@@ -52,7 +52,7 @@ parseCommandLine(int argc, const char ** argv,
 
     OPTENTINIT;
 
-    optParseOptions3(&argc, (char**)argv, opt, sizeof opt, 0);
+    pm_optParseOptions3(&argc, (char**)argv, opt, sizeof opt, 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 < 2)
diff --git a/editor/ppmbrighten.c b/editor/ppmbrighten.c
index 0a4b1140..a3e9a270 100644
--- a/editor/ppmbrighten.c
+++ b/editor/ppmbrighten.c
@@ -44,7 +44,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -68,7 +68,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
     
     if (saturationSpec) {
diff --git a/editor/ppmchange.c b/editor/ppmchange.c
index f0d2cc38..dea85a77 100644
--- a/editor/ppmchange.c
+++ b/editor/ppmchange.c
@@ -70,7 +70,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!remainderSpec)
diff --git a/editor/ppmcolormask.c b/editor/ppmcolormask.c
index 0d7a214c..5ef8d1c1 100644
--- a/editor/ppmcolormask.c
+++ b/editor/ppmcolormask.c
@@ -9,6 +9,7 @@
   Contributed to the public domain by its author.
 =========================================================================*/
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <assert.h>
 #include <string.h>
@@ -59,7 +60,7 @@ parseColorOpt(const char *         const colorOpt,
     colorCount = 0; /* initial value */
     while (!eol && colorCount < ARRAY_SIZE(cmdlineP->maskColor)) {
         const char * token;
-        token = strsepN(&cursor, ",");
+        token = pm_strsep(&cursor, ",");
         if (token) {
             if (strneq(token, "bk:", 3)) {
                 cmdlineP->maskColor[colorCount].matchType = MATCH_BK;
@@ -108,7 +109,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and all of *cmdlineP. */
 
     if (colorSpec)
@@ -221,7 +222,12 @@ main(int argc, char *argv[]) {
             int col;
             ppm_readppmrow(ifP, inputRow, cols, maxval, format);
             for (col = 0; col < cols; ++col) {
-                if (colorIsInSet(inputRow[col], maxval, cmdline)) {
+                pixel thisColor;
+                    /* Color of this pixel with same maxval as used in
+                       'cmdline'
+                    */
+                PPM_DEPTH(thisColor, inputRow[col], maxval, PPM_MAXMAXVAL);
+                if (colorIsInSet(thisColor, PPM_MAXMAXVAL, cmdline)) {
                     maskRow[col] = PBM_BLACK;
                     ++numPixelsMasked;
                 } else 
diff --git a/editor/ppmdist.c b/editor/ppmdist.c
index 90c2e3d3..e8f17bff 100644
--- a/editor/ppmdist.c
+++ b/editor/ppmdist.c
@@ -22,6 +22,7 @@ struct colorToGrayEntry {
 #define MAXCOLORS 255
 
 
+
 static gray
 newGrayValue(pixel *pix, struct colorToGrayEntry *colorToGrayMap, int colors) {
 
@@ -40,20 +41,34 @@ newGrayValue(pixel *pix, struct colorToGrayEntry *colorToGrayMap, int colors) {
 
 
 
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn cmpColorToGrayEntryByIntensity;
+#endif
+
 static int
-cmpColorToGrayEntryByIntensity(const void *entry1, const void *entry2) {
+cmpColorToGrayEntryByIntensity(const void * const a,
+                               const void * const b) {
+
+    const struct colorToGrayEntry * const entry1P = a;
+    const struct colorToGrayEntry * const entry2P = b;
 
-    return ((struct colorToGrayEntry *) entry1)->gray -
-        ((struct colorToGrayEntry *) entry2)->gray;
+    return entry1P->gray - entry2P->gray;
 }
 
 
 
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn cmpColorToGrayEntryByFrequency;
+#endif
+
 static int
-cmpColorToGrayEntryByFrequency(const void * entry1, const void * entry2) {
+cmpColorToGrayEntryByFrequency(const void * const a,
+                               const void * const b) {
+
+    const struct colorToGrayEntry * const entry1P = a;
+    const struct colorToGrayEntry * const entry2P = b;
 
-    return ((struct colorToGrayEntry *) entry1)->frequency -
-        ((struct colorToGrayEntry *) entry2)->frequency;
+    return entry1P->frequency - entry2P->frequency;
 }
 
 
@@ -125,7 +140,7 @@ main(int argc, char *argv[]) {
          * by frequency - but again, for a small number of colors
          * it's a small matter.
          */
-        colorToGrayMap[color].gray = PPM_LUMIN(hist[color].color);
+        colorToGrayMap[color].gray = ppm_luminosity(hist[color].color);
     }
 
     /*
diff --git a/editor/ppmdither.c b/editor/ppmdither.c
index beb45e2f..ec1b9771 100644
--- a/editor/ppmdither.c
+++ b/editor/ppmdither.c
@@ -1,60 +1,294 @@
-/* ppmdither.c - Ordered dithering of a color ppm file to a specified number
-**               of primary shades.
-**
-** Copyright (C) 1991 by Christos Zoulas.
-**
-** 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.
-*/
+/*=============================================================================
+                                 pamdither
+===============================================================================
+  By Bryan Henderson, July 2006.
+
+  Contributed to the public domain.
 
-#include "ppm.h"
+  This is meant to replace Ppmdither by Christos Zoulas, 1991.
+=============================================================================*/
+#include <assert.h>
+
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
 
 /* Besides having to have enough memory available, the limiting factor
    in the dithering matrix power is the size of the dithering value.
    We need 2*dith_power bits in an unsigned int.  We also reserve
    one bit to give headroom to do calculations with these numbers.
 */
-#define MAX_DITH_POWER ((sizeof(unsigned int)*8 - 1) / 2)
+#define MAX_DITH_POWER (((unsigned)sizeof(unsigned int)*8 - 1) / 2)
+
+
+struct colorResolution {
+    unsigned int c[3];
+        /* comp[PAM_RED_PLANE] is number of distinct red levels, etc. */
+};
+
+#define RED PAM_RED_PLANE
+#define GRN PAM_GRN_PLANE
+#define BLU PAM_BLU_PLANE
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;  /* File name of input file */
+    unsigned int dim;
+    struct colorResolution colorRes;
+    unsigned int verbose;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int dimSpec, redSpec, greenSpec, blueSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+    
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "dim",          OPT_UINT, 
+            &cmdlineP->dim,            &dimSpec,                  0);
+    OPTENT3(0, "red",          OPT_UINT, 
+            &cmdlineP->colorRes.c[RED],   &redSpec,       0);
+    OPTENT3(0, "green",        OPT_UINT, 
+            &cmdlineP->colorRes.c[GRN],   &greenSpec,     0);
+    OPTENT3(0, "blue",         OPT_UINT,
+            &cmdlineP->colorRes.c[BLU],   &blueSpec,      0);
+    OPTENT3(0, "verbose",      OPT_FLAG,
+            NULL,                      &cmdlineP->verbose,        0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
+
+    if (!dimSpec)
+        cmdlineP->dim = 4;
+
+    if (cmdlineP->dim > MAX_DITH_POWER)
+        pm_error("Dithering matrix power %u (-dim) is too large.  "
+                 "Must be <= %u",
+                 cmdlineP->dim, MAX_DITH_POWER);
+        
+    if (redSpec) {
+        if (cmdlineP->colorRes.c[RED] < 2)
+            pm_error("-red must be at least 2.  You specified %u",
+                     cmdlineP->colorRes.c[RED]);
+    } else
+        cmdlineP->colorRes.c[RED] = 5;
+
+    if (greenSpec) {
+        if (cmdlineP->colorRes.c[GRN] < 2)
+            pm_error("-green must be at least 2.  You specified %u",
+                     cmdlineP->colorRes.c[GRN]);
+    } else
+        cmdlineP->colorRes.c[GRN] = 9;
+
+    if (blueSpec) {
+        if (cmdlineP->colorRes.c[BLU] < 2)
+            pm_error("-blue must be at least 2.  You specified %u",
+                     cmdlineP->colorRes.c[BLU]);
+    } else
+        cmdlineP->colorRes.c[BLU] = 5;
+
+    if (argc-1 > 1)
+        pm_error("Program takes at most one argument: the input file "
+                 "specification.  "
+                 "You specified %d arguments.", argc-1);
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else
+        cmdlineP->inputFileName = argv[1];
+}
+
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   A scaler object scales a red/green/blue triple, each component having its
+   own maxval, to a tuple having another maxval.  That maxval is the same for
+   all three components.  The input and output maxvals are characteristic of
+   the scaler.
+
+   Example: The scaler scales from a red value of 0-3, green value of
+   0-3, and blue value of 0-1 to a tuple with maxval 255.  So you can
+   ask it to scale (1,1,1) and it responds with (85, 85, 255).
+-----------------------------------------------------------------------------*/
+    struct colorResolution colorRes;
+        /* Number of values of each color component possible, i.e. maxval
+           plus 1
+        */
+    tuple * out;
+        /* Malloced array that provides the scaled output when indexed by a
+           certain function (see scaler_scale()) of the input red, green, and
+           blue values.
+        */
+} scaler;    
+
+
+
+static tuple *
+allocScalerMap(unsigned int const size) {
+    /* The tuple row data structure starts with 'size' pointers to
+       the tuples, immediately followed by the 'size' tuples
+       themselves.  Each tuple consists of 3 samples.  
+    */
+
+    unsigned int const depth = 3;
+    unsigned int const bytesPerTuple = depth * sizeof(sample);
+
+    tuple * map;
+
+    map = malloc(size * (sizeof(tuple *) + bytesPerTuple));
+                      
+    if (map != NULL) {
+        /* Now we initialize the pointers to the individual tuples
+           to make this a regulation C two dimensional array.  
+        */
+        char * p;
+        unsigned int i;
+        
+        p = (char*) (map + size);  /* location of Tuple 0 */
+        for (i = 0; i < size; ++i) {
+            map[i] = (tuple) p;
+            p += bytesPerTuple;
+        }
+    }
+    return map;
+}
+
+
+
+static void
+scaler_create(sample                 const outputMaxval,
+              struct colorResolution const colorRes,
+              scaler **              const scalerPP) {
+
+    scaler * scalerP;
+    unsigned int mapSize;
+    
+    if (UINT_MAX / colorRes.c[RED] / colorRes.c[GRN] / colorRes.c[BLU] < 1)
+        pm_error("red/green/blue dimensions %u/%u/%u is uncomputably large",
+                 colorRes.c[RED], colorRes.c[GRN], colorRes.c[BLU]);
+
+    {
+        unsigned int plane;
+        for (plane = 0, mapSize = 1; plane < 3; ++plane)
+            mapSize *= colorRes.c[plane];
+    }
+    MALLOCVAR_NOFAIL(scalerP);
+
+    scalerP->colorRes = colorRes;
+
+    scalerP->out = allocScalerMap(mapSize);
+
+    if (scalerP->out == NULL)
+        pm_error("Unable to allocate memory for %u colors "
+                 "(%u red x %u green x %u blue)",
+                 mapSize, colorRes.c[RED], colorRes.c[GRN], colorRes.c[BLU]);
+
+    {
+        unsigned int r;
+        for (r = 0; r < colorRes.c[RED]; ++r) {
+            unsigned int g;
+            for (g = 0; g < colorRes.c[GRN]; ++g) {
+                unsigned int b;
+                for (b = 0; b < colorRes.c[BLU]; ++b) {
+                    unsigned int const index =
+                        (r * colorRes.c[GRN] + g)
+                        * colorRes.c[BLU] + b;
+                    tuple const t = scalerP->out[index];
+                         
+                    t[PAM_RED_PLANE] =
+                        r * outputMaxval / (colorRes.c[RED] - 1);
+                    t[PAM_GRN_PLANE] = 
+                        g * outputMaxval / (colorRes.c[GRN] - 1);
+                    t[PAM_BLU_PLANE] =
+                        b * outputMaxval / (colorRes.c[BLU] - 1);
+                }
+            }
+        }
+    }
+    *scalerPP = scalerP;
+}
+
+
+
+static void
+scaler_destroy(scaler * const scalerP) {
+
+    free(scalerP->out);
+
+    free(scalerP);
+}
 
-typedef unsigned char ubyte;
 
-static unsigned int dith_power;     /* base 2 log of dither matrix dimension */
-static unsigned int dith_dim;      	/* dimension of the dither matrix	*/
-static unsigned int dith_dm2;      	/* dith_dim squared				*/
-static unsigned int **dith_mat; 	/* the dithering matrix			*/
-static int debug;
 
-/* COLOR():
- *	returns the index in the colormap for the
- *      r, g, b values specified.
- */
-#define COLOR(r,g,b) (((r) * dith_ng + (g)) * dith_nb + (b))
+static tuple
+scaler_scale(const scaler * const scalerP,
+             unsigned int   const red,
+             unsigned int   const grn,
+             unsigned int   const blu) {
+
+    unsigned int const index =
+        ((red * scalerP->colorRes.c[GRN]) + grn)
+        * scalerP->colorRes.c[BLU] + blu;
+
+    assert(red < scalerP->colorRes.c[RED]);
+    assert(grn < scalerP->colorRes.c[GRN]);
+    assert(blu < scalerP->colorRes.c[BLU]);
+
+    return scalerP->out[index];
+}
 
 
 
 static unsigned int
-dither(pixval const p,
-       pixval const maxval,
+dither(sample       const p,
+       sample       const maxval,
        unsigned int const d,
-       unsigned int const ditheredMaxval) {
+       unsigned int const ditheredMaxval,
+       unsigned int const ditherMatrixArea) {
 /*----------------------------------------------------------------------------
-  Return the dithered intensity for a component of a pixel whose real 
-  intensity for that component is 'p' based on a maxval of 'maxval'.
-  The returned intensity is based on a maxval of ditheredMaxval.
+  Return the dithered brightness for a component of a pixel whose real 
+  brightness for that component is 'p' based on a maxval of 'maxval'.
+  The returned brightness is based on a maxval of ditheredMaxval.
 
   'd' is the entry in the dithering matrix for the position of this pixel
   within the dithered square.
+
+  'ditherMatrixArea' is the area (number of pixels in) the dithered square.
 -----------------------------------------------------------------------------*/
-    unsigned int const ditherSquareMaxval = ditheredMaxval * dith_dm2;
+    unsigned int const ditherSquareMaxval = ditheredMaxval * ditherMatrixArea;
         /* This is the maxval for an intensity that an entire dithered
            square can represent.
         */
-    pixval const pScaled = ditherSquareMaxval * p / maxval;
+    unsigned int const pScaled = ditherSquareMaxval * p / maxval;
         /* This is the input intensity P expressed with a maxval of
            'ditherSquareMaxval'
         */
@@ -64,246 +298,254 @@ dither(pixval const p,
        in the dithered square, as determined by 'd'
     */
 
-    return (pScaled + d) / dith_dm2;
+    return (pScaled + d) / ditherMatrixArea;
 }
 
 
-/* 
- *	Return the value of a dither matrix which is 2**dith_power elements
- *  square at Row x, Column y.
- *	[graphics gems, p. 714]
- */
-static unsigned int
-dith_value(unsigned int y, unsigned int x, const unsigned int dith_power) { 
 
+static unsigned int
+dithValue(unsigned int const yArg,
+          unsigned int const xArg,
+          unsigned int const dithPower) { 
+/*----------------------------------------------------------------------------
+  Return the value of a dither matrix which is 2 ** dithPower elements
+  square at Row x, Column y.
+  [graphics gems, p. 714]
+-----------------------------------------------------------------------------*/
     unsigned int d;
+        /*
+          Think of d as the density. At every iteration, d is shifted
+          left one and a new bit is put in the low bit based on x and y.
+          If x is odd and y is even, or visa versa, then a bit is shifted in.
+          This generates the checkerboard pattern seen in dithering.
+          This quantity is shifted again and the low bit of y is added in.
+          This whole thing interleaves a checkerboard pattern and y's bits
+          which is what you want.
+        */
+    unsigned int x, y;
+    unsigned int i;
 
-    /*
-     * Think of d as the density. At every iteration, d is shifted
-     * left one and a new bit is put in the low bit based on x and y.
-     * If x is odd and y is even, or visa versa, then a bit is shifted in.
-     * This generates the checkerboard pattern seen in dithering.
-     * This quantity is shifted again and the low bit of y is added in.
-     * This whole thing interleaves a checkerboard pattern and y's bits
-     * which is what you want.
-     */
-    int i;
-    for (i = 0, d = 0; i < dith_power; i++, x >>= 1, y >>= 1)
+    for (i = 0, d = 0, x = xArg, y = yArg;
+         i < dithPower;
+         ++i, x >>= 1, y >>= 1)
         d = (d << 2) | (((x & 1) ^ (y & 1)) << 1) | (y & 1);
-    return(d);
-} /* end dith_value */
+
+    return d;
+}
 
 
 
 static unsigned int **
-dith_matrix(unsigned int const dith_dim) {
+dithMatrix(unsigned int const dithPower) {
 /*----------------------------------------------------------------------------
-   Create the dithering matrix for dimension 'dith_dim'.
+   Create the dithering matrix for dimension 'dithDim'.
 
    Return it in newly malloc'ed storage.
 
-   Note that we assume 'dith_dim' is small enough that the dith_mat_sz
+   Note that we assume 'dithPower' is small enough that the 'dithMatSize'
    computed within fits in an int.  Otherwise, results are undefined.
 -----------------------------------------------------------------------------*/
-    unsigned int ** dith_mat;
-    {
-        unsigned int const dith_mat_sz = 
-            (dith_dim * sizeof(int *)) + /* pointers */
-            (dith_dim * dith_dim * sizeof(int)); /* data */
+    unsigned int const dithDim = 1 << dithPower;
+
+    unsigned int ** dithMat;
 
-        dith_mat = (unsigned int **) malloc(dith_mat_sz);
+    assert(dithPower < sizeof(unsigned int) * 8);
 
-        if (dith_mat == NULL) 
+    {
+        unsigned int const dithMatSize = 
+            (dithDim * sizeof(*dithMat)) + /* pointers */
+            (dithDim * dithDim * sizeof(**dithMat)); /* data */
+        
+        dithMat = malloc(dithMatSize);
+        
+        if (dithMat == NULL) 
             pm_error("Out of memory.  "
-                     "Cannot allocate %d bytes for dithering matrix.",
-                     dith_mat_sz);
+                     "Cannot allocate %u bytes for dithering matrix.",
+                     dithMatSize);
     }
     {
-        unsigned int * const dat = (unsigned int *) &dith_mat[dith_dim];
+        unsigned int * const rowStorage = (unsigned int *)&dithMat[dithDim];
         unsigned int y;
-        for (y = 0; y < dith_dim; y++)
-            dith_mat[y] = &dat[y * dith_dim];
+        for (y = 0; y < dithDim; ++y)
+            dithMat[y] = &rowStorage[y * dithDim];
     }
     {
         unsigned int y;
-        for (y = 0; y < dith_dim; y++) {
+        for (y = 0; y < dithDim; ++y) {
             unsigned int x;
-            for (x = 0; x < dith_dim; x++) {
-                dith_mat[y][x] = dith_value(y, x, dith_power);
-                if (debug)
-                    (void) fprintf(stderr, "%4d ", dith_mat[y][x]);
-            }
-            if (debug)
-                (void) fprintf(stderr, "\n");
+            for (x = 0; x < dithDim; ++x)
+                dithMat[y][x] = dithValue(y, x, dithPower);
         }
     }
-    return dith_mat;
+    return dithMat;
 }
 
-    
+
 
 static void
-dith_setup(const unsigned int dith_power, 
-           const unsigned int dith_nr, 
-           const unsigned int dith_ng, 
-           const unsigned int dith_nb, 
-           const pixval output_maxval,
-           pixel ** const colormapP) {
+validateNoDitherOverflow(unsigned int           const ditherMatrixArea,
+                         struct pam *           const inpamP,
+                         struct colorResolution const colorRes) {
 /*----------------------------------------------------------------------------
-   Set up the dithering parameters, color map (lookup table) and
-   dithering matrix.
-
-   Return the colormap in newly malloc'ed storage and return its address
-   as *colormapP.
+   Validate that we'll be able to do the dithering calculations based on
+   the parameters above without busting out of an integer.
 -----------------------------------------------------------------------------*/
-    unsigned int r, g, b;
-
-    if (dith_nr < 2) 
-        pm_error("too few shades for red, minimum of 2");
-    if (dith_ng < 2) 
-        pm_error("too few shades for green, minimum of 2");
-    if (dith_nb < 2) 
-        pm_error("too few shades for blue, minimum of 2");
-
-    MALLOCARRAY(*colormapP, dith_nr * dith_ng * dith_nb);
-    if (*colormapP == NULL) 
-        pm_error("Unable to allocate space for the color lookup table "
-                 "(%d by %d by %d pixels).", dith_nr, dith_ng, dith_nb);
-    
-    for (r = 0; r < dith_nr; r++) 
-        for (g = 0; g < dith_ng; g++) 
-            for (b = 0; b < dith_nb; b++) {
-                PPM_ASSIGN((*colormapP)[COLOR(r,g,b)], 
-                           (r * output_maxval / (dith_nr - 1)),
-                           (g * output_maxval / (dith_ng - 1)),
-                           (b * output_maxval / (dith_nb - 1)));
-            }
-    
-    if (dith_power > MAX_DITH_POWER) {
-        pm_error("Dithering matrix power %d is too large.  Must be <= %d",
-                 dith_power, MAX_DITH_POWER);
-    } else {
-        dith_dim = (1 << dith_power);
-        dith_dm2 = dith_dim * dith_dim;
+    unsigned int maxDitherMaxval;
+    unsigned int plane;
+
+    for (plane = 0, maxDitherMaxval = 1; plane < 0; ++plane) {
+        assert(colorRes.c[plane] >= 2);
+        maxDitherMaxval = MAX(maxDitherMaxval, colorRes.c[plane]-1);
     }
 
-    dith_mat = dith_matrix(dith_dim);
-} /* end dith_setup */
+    if (UINT_MAX / ditherMatrixArea / inpamP->maxval / maxDitherMaxval < 1)
+        pm_error("Numbers are too large to compute.  You must reduce "
+                 "the dither power, the input maxval, or the number of "
+                 "component levels in the output");
+}
+
 
 
-/* 
- *  Dither whole image
- */
 static void
-dith_dither(const unsigned int width, const unsigned int height, 
-            const pixval maxval,
-            const pixel * const colormap, 
-            pixel ** const input, pixel ** const output,
-            const unsigned int dith_nr,
-            const unsigned int dith_ng,
-            const unsigned int dith_nb, 
-            const pixval output_maxval
-            ) {
-
-    const unsigned int dm = (dith_dim - 1);  /* A mask */
-    unsigned int row, col; 
-
-    for (row = 0; row < height; row++)
-        for (col = 0; col < width; col++) {
-            unsigned int const d = dith_mat[row & dm][(width-col-1) & dm];
-            pixel const input_pixel = input[row][col];
-            unsigned int const dithered_r = 
-                dither(PPM_GETR(input_pixel), maxval, d, dith_nr-1);
-            unsigned int const dithered_g = 
-                dither(PPM_GETG(input_pixel), maxval, d, dith_ng-1);
-            unsigned int const dithered_b = 
-                dither(PPM_GETB(input_pixel), maxval, d, dith_nb-1);
-            output[row][col] = 
-                colormap[COLOR(dithered_r, dithered_g, dithered_b)];
-        }
+ditherRow(struct pam *           const inpamP,
+          const tuple *          const inrow,
+          const scaler *         const scalerP,
+          unsigned int **        const ditherMatrix,
+          unsigned int           const ditherMatrixArea,
+          struct colorResolution const colorRes,
+          unsigned int           const row,
+          unsigned int           const modMask,
+          struct pam *           const outpamP,
+          tuple *                const outrow) {
+
+    unsigned int col;
+
+    for (col = 0; col < inpamP->width; ++col) {
+        unsigned int const d =
+            ditherMatrix[row & modMask][(inpamP->width-col-1) & modMask];
+
+        unsigned int dithered[3];
+        unsigned int plane;
+
+        assert(inpamP->depth >= 3);
+
+        for (plane = 0; plane < 3; ++plane)
+            dithered[plane] =
+                dither(inrow[col][plane], inpamP->maxval, d,
+                       colorRes.c[plane]-1, ditherMatrixArea);
+
+        pnm_assigntuple(outpamP,
+                        outrow[col],
+                        scaler_scale(scalerP,
+                                     dithered[PAM_RED_PLANE],
+                                     dithered[PAM_GRN_PLANE],
+                                     dithered[PAM_BLU_PLANE]));
+    }
 }
 
 
+
+static void
+ditherImage(struct pam *           const inpamP,
+            const scaler *         const scalerP,
+            unsigned int           const dithPower,
+            struct colorResolution const colorRes,
+            struct pam *           const outpamP,
+            tuple ***              const outTuplesP) {
+
+    unsigned int const dithDim = 1 << dithPower;
+    unsigned int const ditherMatrixArea = SQR(dithDim);
+
+    unsigned int const modMask = (dithDim - 1);
+       /* And this into N to compute N % dithDim cheaply, since we
+          know (though the compiler doesn't) that dithDim is a power of 2
+       */
+    unsigned int ** const ditherMatrix = dithMatrix(dithPower);
+
+    tuple * inrow;
+    tuple ** outTuples;
+    unsigned int row; 
+    struct pam ditherPam;
+        /* Describes the tuples that ditherRow() sees */
+
+    assert(dithPower < sizeof(unsigned int) * 8);
+    assert(UINT_MAX / dithDim >= dithDim);
+    
+    validateNoDitherOverflow(ditherMatrixArea, inpamP, colorRes);
+
+    inrow = pnm_allocpamrow(inpamP);
+
+    outTuples = pnm_allocpamarray(outpamP);
+
+    /* We will modify the input to promote it to depth 3 */
+    ditherPam = *inpamP;
+    ditherPam.depth = 3;
+
+    for (row = 0; row < inpamP->height; ++row) {
+        pnm_readpamrow(inpamP, inrow);
+
+        pnm_makerowrgb(inpamP, inrow);
+
+        ditherRow(&ditherPam, inrow, scalerP, ditherMatrix, ditherMatrixArea,
+                  colorRes, row, modMask,
+                  outpamP, outTuples[row]);
+    }
+    free(ditherMatrix);
+    pnm_freepamrow(inrow);
+    *outTuplesP = outTuples;
+}
+
+
+
 int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    pixel *colormap;    /* malloc'd */
-    pixel **ipixels;        /* Input image */
-    pixel **opixels;        /* Output image */
-    int cols, rows;
-    pixval maxval;  /* Maxval of the input image */
-    pixval output_maxval;  /* Maxval in the dithered output image */
-    unsigned int argn;
-    const char* const usage = 
-	"[-dim <num>] [-red <num>] [-green <num>] [-blue <num>] [ppmfile]";
-    unsigned int dith_nr; /* number of red shades in output */
-    unsigned int dith_ng; /* number of green shades	in output */
-    unsigned int dith_nb; /* number of blue shades in output */
-
-
-    ppm_init( &argc, argv );
-
-    dith_nr = 5;  /* default */
-    dith_ng = 9;  /* default */
-    dith_nb = 5;  /* default */
-
-    dith_power = 4;  /* default */
-    debug = 0; /* default */
-    argn = 1;
-
-    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-	{
-	if ( pm_keymatch( argv[argn], "-dim", 1) &&  argn + 1 < argc ) {
-	    argn++;
-	    if (sscanf(argv[argn], "%u", &dith_power) != 1)
-		pm_usage( usage );
-	}
-	else if ( pm_keymatch( argv[argn], "-red", 1 ) && argn + 1 < argc ) {
-	    argn++;
-	    if (sscanf(argv[argn], "%u", &dith_nr) != 1)
-		pm_usage( usage );
-	}
-	else if ( pm_keymatch( argv[argn], "-green", 1 ) && argn + 1 < argc ) {
-	    argn++;
-	    if (sscanf(argv[argn], "%u", &dith_ng) != 1)
-		pm_usage( usage );
-	}
-	else if ( pm_keymatch( argv[argn], "-blue", 1 ) && argn + 1 < argc ) {
-	    argn++;
-	    if (sscanf(argv[argn], "%u", &dith_nb) != 1)
-		pm_usage( usage );
-	}
-	else if ( pm_keymatch( argv[argn], "-debug", 6 )) {
-        debug = 1;
-	}
-	else
-	    pm_usage( usage );
-	++argn;
-	}
-
-    if ( argn != argc )
-	{
-	ifp = pm_openr( argv[argn] );
-	++argn;
-	}
-    else
-	ifp = stdin;
-
-    if ( argn != argc )
-	pm_usage( usage );
-
-    ipixels = ppm_readppm( ifp, &cols, &rows, &maxval );
-    pm_close( ifp );
-    opixels = ppm_allocarray(cols, rows);
-    output_maxval = pm_lcm(dith_nr-1, dith_ng-1, dith_nb-1, PPM_MAXMAXVAL);
-    dith_setup(dith_power, dith_nr, dith_ng, dith_nb, output_maxval, 
-               &colormap);
-    dith_dither(cols, rows, maxval, colormap, ipixels, opixels,
-                dith_nr, dith_ng, dith_nb, output_maxval);
-    ppm_writeppm(stdout, opixels, cols, rows, output_maxval, 0);
-    pm_close(stdout);
-    exit(0);
+main(int           argc,
+     const char ** argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    tuple ** outTuples;        /* Output image */
+    scaler * scalerP;
+    struct pam inpam;
+    struct pam outpam;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
+
+    pnm_setminallocationdepth(&inpam, 3);
+    
+    outpam.size               = sizeof(outpam);
+    outpam.len                = PAM_STRUCT_SIZE(tuple_type);
+    outpam.file               = stdout;
+    outpam.width              = inpam.width;
+    outpam.height             = inpam.height;
+    outpam.depth              = 3;
+    outpam.maxval             =
+        pm_lcm(cmdline.colorRes.c[RED]-1,
+               cmdline.colorRes.c[GRN]-1,
+               cmdline.colorRes.c[BLU]-1,
+               PPM_MAXMAXVAL);
+    outpam.bytes_per_sample   = inpam.bytes_per_sample;
+    STRSCPY(outpam.tuple_type, "RGB");
+    outpam.format             = RPPM_FORMAT;
+    outpam.plainformat        = false;
+
+    scaler_create(outpam.maxval, cmdline.colorRes, &scalerP);
+
+    ditherImage(&inpam, scalerP, cmdline.dim, cmdline.colorRes,
+                &outpam, &outTuples);
+
+    pnm_writepam(&outpam, outTuples);
+
+    scaler_destroy(scalerP);
+
+    pnm_freepamarray(outTuples, &outpam);
+
+    pm_close(ifP);
+
+    return 0;
 }
diff --git a/editor/ppmdraw.c b/editor/ppmdraw.c
index 70ae842f..bd569e03 100644
--- a/editor/ppmdraw.c
+++ b/editor/ppmdraw.c
@@ -1,5 +1,6 @@
-#define _XOPEN_SOURCE    /* Make sure M_PI is in math.h */
-#define _BSD_SOURCE      /* Make sure strdup is in string.h */
+#define _XOPEN_SOURCE 500 
+   /* Make sure M_PI is in math.h, strdup is in string.h */
+#define _BSD_SOURCE      /* Make sure strdup is in string.h (alternate) */
 
 #include <string.h>
 #include <ctype.h>
@@ -44,7 +45,7 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine (int argc, char ** argv,
+parseCommandLine (int argc, const char ** argv,
                   struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
@@ -57,7 +58,7 @@ parseCommandLine (int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -80,7 +81,7 @@ parseCommandLine (int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
     
     if (!scriptSpec && !scriptfileSpec)
@@ -189,6 +190,7 @@ enum drawVerb {
     VERB_LINE_HERE,
     VERB_SPLINE3,
     VERB_CIRCLE,
+    VERB_FILLEDCIRCLE,
     VERB_FILLEDRECTANGLE,
     VERB_TEXT,
     VERB_TEXT_HERE
@@ -288,10 +290,10 @@ freeDrawCommand(const struct drawCommand * const commandP) {
     case VERB_SETLINECLIP:
         break;
     case VERB_SETCOLOR:
-        strfree(commandP->u.setcolorArg.colorName);
+        pm_strfree(commandP->u.setcolorArg.colorName);
         break;
     case VERB_SETFONT:
-        strfree(commandP->u.setfontArg.fontFileName);
+        pm_strfree(commandP->u.setfontArg.fontFileName);
         break;
     case VERB_LINE:
         break;
@@ -301,11 +303,13 @@ freeDrawCommand(const struct drawCommand * const commandP) {
         break;
     case VERB_CIRCLE:
         break;
+    case VERB_FILLEDCIRCLE:
+        break;
     case VERB_FILLEDRECTANGLE:
         break;
     case VERB_TEXT:
     case VERB_TEXT_HERE:
-        strfree(commandP->u.textArg.text);
+        pm_strfree(commandP->u.textArg.text);
         break;
     }
     
@@ -346,6 +350,35 @@ freeScript(struct script * const scriptP) {
 
 
 static void
+doFilledCircle(pixel **                   const pixels,
+               unsigned int               const cols,
+               unsigned int               const rows,
+               pixval                     const maxval,
+               const struct drawCommand * const commandP,
+               const struct drawState *   const drawStateP) {
+
+    struct fillobj * fhP;
+
+    fhP = ppmd_fill_create();
+            
+    ppmd_circle(pixels, cols, rows, maxval,
+                commandP->u.circleArg.cx,
+                commandP->u.circleArg.cy,
+                commandP->u.circleArg.radius,
+                ppmd_fill_drawproc,
+                fhP);
+            
+    ppmd_fill(pixels, cols, rows, maxval,
+              fhP,
+              PPMD_NULLDRAWPROC,
+              &drawStateP->color);
+
+    ppmd_fill_destroy(fhP);
+} 
+
+
+
+static void
 doTextHere(pixel **                   const pixels,
            unsigned int               const cols,
            unsigned int               const rows,
@@ -463,6 +496,9 @@ executeScript(struct script * const scriptP,
                         PPMD_NULLDRAWPROC,
                         &drawState.color);
             break;
+        case VERB_FILLEDCIRCLE:
+            doFilledCircle(pixels, cols, rows, maxval, commandP, &drawState);
+            break;
         case VERB_FILLEDRECTANGLE:
             ppmd_filledrectangle(pixels, cols, rows, maxval,
                                  commandP->u.filledrectangleArg.x,
@@ -611,6 +647,17 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 argP->cy     = atoi(commandTokens.token[2]);
                 argP->radius = atoi(commandTokens.token[3]);
             } 
+        } else if (streq(verb, "filledcircle")) {
+            drawCommandP->verb = VERB_FILLEDCIRCLE;
+            if (commandTokens.count < 4)
+                pm_error("Not enough tokens for a 'filledcircle' command.  "
+                         "Need %u.  Got %u", 4, commandTokens.count);
+            else {
+                struct circleArg * const argP = &drawCommandP->u.circleArg;
+                argP->cx     = atoi(commandTokens.token[1]);
+                argP->cy     = atoi(commandTokens.token[2]);
+                argP->radius = atoi(commandTokens.token[3]);
+            } 
         } else if (streq(verb, "filledrectangle")) {
             drawCommandP->verb = VERB_FILLEDRECTANGLE;
             if (commandTokens.count < 5)
@@ -677,7 +724,7 @@ disposeOfCommandTokens(struct tokenSet * const tokenSetP,
     {
         unsigned int i;
         for (i = 0; i < tokenSetP->count; ++i)
-            strfree(tokenSetP->token[i]);
+            pm_strfree(tokenSetP->token[i]);
         tokenSetP->count = 0;
     }
     /* Put the list element for this command at the tail of the list */
@@ -828,7 +875,7 @@ getScript(struct cmdlineInfo const cmdline,
 
     parseScript(scriptText, scriptPP);
 
-    strfree(scriptText);
+    pm_strfree(scriptText);
 }
 
           
@@ -853,14 +900,14 @@ doOneImage(FILE *          const ifP,
 
 
 int
-main(int argc, char * argv[]) {
+main(int argc, const char * argv[]) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
     struct script * scriptP;
-    bool eof;
+    int eof;
 
-    ppm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
diff --git a/editor/ppmfade b/editor/ppmfade
index fbc62968..027fc793 100755
--- a/editor/ppmfade
+++ b/editor/ppmfade
@@ -1,5 +1,31 @@
-#!/usr/bin/perl -w
-#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+#!/bin/sh
+
+##############################################################################
+# This is essentially a Perl program.  We exec the Perl interpreter specifying
+# this same file as the Perl program and use the -x option to cause the Perl
+# interpreter to skip down to the Perl code.  The reason we do this instead of
+# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is
+# that the user may have multiple Perl interpreters and the one he wants to
+# use is properly located in the PATH.  The user's choice of Perl interpreter
+# may be crucial, such as when the user also has a PERL5LIB environment
+# variable and it selects modules that work with only a certain main
+# interpreter program.
+#
+# An alternative some people use is to have /usr/bin/env as the script
+# interpreter.  We don't do that because we think the existence and
+# compatibility of /bin/sh is more reliable.
+#
+# Note that we aren't concerned about efficiency because the user who needs
+# high efficiency can use directly the programs that this program invokes.
+#
+##############################################################################
+
+exec perl -w -x -S -- "$0" "$@"
+
+#!/usr/bin/perl
+##############################################################################
+#                                  ppmfade
+##############################################################################
 #
 #  This program creates a fade (a sequence of frames) between two images.
 #
@@ -12,7 +38,7 @@
 #  much the same thing, but handles non-Netpbm formats too, and is 
 #  implemented in a more primitive language.
 #
-#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+##############################################################################
 use strict;
 
 my $SPREAD =  1;
diff --git a/editor/ppmmix.c b/editor/ppmmix.c
index 5306d1cf..f4678a4b 100644
--- a/editor/ppmmix.c
+++ b/editor/ppmmix.c
@@ -1,131 +1,96 @@
-
 /*********************************************************************/
 /* ppmmix -  mix together two pictures like with a fader             */
 /* Frank Neumann, October 1993                                       */
 /* V1.2 16.11.1993                                                   */
 /*                                                                   */
-/* version history:                                                  */
-/* V1.0 Aug   1993    first version                                  */
-/* V1.1 12.10.1993    uses ppm libs&headers, integer math, cleanups  */
-/* V1.2 16.11.1993    Rewritten to be NetPBM.programming conforming  */
 /*********************************************************************/
 
 #include "ppm.h"
 
-/* global variables */
-#ifdef AMIGA
-static char *version = "$VER: ppmmix 1.2 (16.11.93)"; /* Amiga version identification */
-#endif
-
-/**************************/
-/* start of main function */
-/**************************/
-int main(argc, argv)
-int argc;
-char *argv[];
-{
-	FILE *ifp1, *ifp2;
-	int argn, rows, cols, format, i = 0, j = 0;
-	int rows2, cols2, format2;
-	pixel *srcrow1, *srcrow2, *destrow;
-	pixel *pP1, *pP2, *pP3;
-	pixval maxval, maxval2;
-	pixval r1, r2, r3, g1, g2, g3, b1, b2, b3;
-	double fadefactor;
-	long longfactor;
-	const char * const usage = "fadefactor ppmfile1 ppmfile2\n        fadefactor: 0.0 = only ppmfile1, 1.0 = only ppmfile2\n";
-
-	/* parse in 'default' parameters */
-	ppm_init(&argc, argv);
-
-	argn = 1;
-
-	/* parse in dim factor */
-	if (argn == argc)
-		pm_usage(usage);
-	if (sscanf(argv[argn], "%lf", &fadefactor) != 1)
-		pm_usage(usage);
-	if (fadefactor < 0.0 || fadefactor > 1.0)
-		pm_error("fade factor must be in the range from 0.0 to 1.0 ");
-	++argn;
-
-	/* parse in filenames and open files (cannot be stdin-filters, sorry..) */
-	if (argn == argc-2)
-	{
-		ifp1 = pm_openr(argv[argn]);
-		++argn;
-		ifp2 = pm_openr(argv[argn]);
-	}
-	else
-		pm_usage(usage);
-
-	/* read first data from both files and compare sizes etc. */
-	ppm_readppminit(ifp1, &cols, &rows, &maxval, &format);
-	ppm_readppminit(ifp2, &cols2, &rows2, &maxval2, &format2);
-
-    if ( (cols != cols2) || (rows != rows2) )
+int main(int argc, const char ** argv) {
+
+    FILE * if1P;
+    FILE * if2P;
+    int argn;
+    int rows1, cols1, format1;
+    unsigned int row;
+    int rows2, cols2, format2;
+    pixel *srcrow1, *srcrow2, *destrow;
+    pixval maxval1, maxval2;
+    double fadefactor;
+    long longfactor;
+    const char * const usage = "fadefactor ppmfile1 ppmfile2\n        fadefactor: 0.0 = only ppmfile1, 1.0 = only ppmfile2\n";
+
+    /* parse in 'default' parameters */
+    pm_proginit(&argc, argv);
+
+    argn = 1;
+
+    /* parse in dim factor */
+    if (argn == argc)
+        pm_usage(usage);
+    if (sscanf(argv[argn], "%lf", &fadefactor) != 1)
+        pm_usage(usage);
+    if (fadefactor < 0.0 || fadefactor > 1.0)
+        pm_error("fade factor must be in the range from 0.0 to 1.0 ");
+    ++argn;
+
+    if (argn == argc-2) {
+        if1P = pm_openr(argv[argn]);
+        ++argn;
+        if2P = pm_openr(argv[argn]);
+    } else
+        pm_usage(usage);
+
+    ppm_readppminit(if1P, &cols1, &rows1, &maxval1, &format1);
+    ppm_readppminit(if2P, &cols2, &rows2, &maxval2, &format2);
+
+    if ((cols1 != cols2) || (rows1 != rows2))
         pm_error("image sizes are different!");
 
-    if ( maxval != maxval2)
-		pm_error("images have different maxvalues");
-
-	if (format != format2)
-	{
-		pm_error("images have different PxM types");
-	}
-
-	/* no error checking required here, ppmlib does it all for us */
-	srcrow1 = ppm_allocrow(cols);
-	srcrow2 = ppm_allocrow(cols);
+    if (maxval1 != maxval2)
+        pm_error("images have different maxvalues");
 
-	longfactor = (long)(fadefactor * 65536);
+    srcrow1 = ppm_allocrow(cols1);
+    srcrow2 = ppm_allocrow(cols2);
 
-	/* allocate a row of pixel data for the new pixels */
-	destrow = ppm_allocrow(cols);
+    longfactor = (long)(fadefactor * 65536);
 
-	ppm_writeppminit(stdout, cols, rows, maxval, 0);
+    destrow = ppm_allocrow(cols1);
 
-	for (i = 0; i < rows; i++)
-	{
-		ppm_readppmrow(ifp1, srcrow1, cols, maxval, format);
-		ppm_readppmrow(ifp2, srcrow2, cols, maxval, format);
+    ppm_writeppminit(stdout, cols1, rows1, maxval1, 0);
 
-		pP1 = srcrow1;
-		pP2 = srcrow2;
-        pP3 = destrow;
+    for (row = 0; row < rows1; ++row) {
+        unsigned int col;
+        ppm_readppmrow(if1P, srcrow1, cols1, maxval1, format1);
+        ppm_readppmrow(if2P, srcrow2, cols2, maxval2, format2);
 
-		for (j = 0; j < cols; j++)
-		{
-			r1 = PPM_GETR(*pP1);
-			g1 = PPM_GETG(*pP1);
-			b1 = PPM_GETB(*pP1);
+        for (col = 0; col < cols1; ++col) {
+            pixel const p1 = srcrow1[col];
+            pixval const r1 = PPM_GETR(p1);
+            pixval const g1 = PPM_GETG(p1);
+            pixval const b1 = PPM_GETB(p1);
 
-			r2 = PPM_GETR(*pP2);
-			g2 = PPM_GETG(*pP2);
-			b2 = PPM_GETB(*pP2);
+            pixel const p2 = srcrow2[col];
+            pixval const r2 = PPM_GETR(p2);
+            pixval const g2 = PPM_GETG(p2);
+            pixval const b2 = PPM_GETB(p2);
 
-			r3 = r1 + (((r2 - r1) * longfactor) >> 16);
-			g3 = g1 + (((g2 - g1) * longfactor) >> 16);
-			b3 = b1 + (((b2 - b1) * longfactor) >> 16);
+            pixval const r = r1 + (((r2 - r1) * longfactor) >> 16);
+            pixval const g = g1 + (((g2 - g1) * longfactor) >> 16);
+            pixval const b = b1 + (((b2 - b1) * longfactor) >> 16);
 
+            PPM_ASSIGN(destrow[col], r, g, b);
+        }
 
-			PPM_ASSIGN(*pP3, r3, g3, b3);
+        ppm_writeppmrow(stdout, destrow, cols1, maxval1, 0);
+    }
 
-			pP1++;
-			pP2++;
-			pP3++;
-		}
+    pm_close(if1P);
+    pm_close(if2P);
+    ppm_freerow(srcrow1);
+    ppm_freerow(srcrow2);
+    ppm_freerow(destrow);
 
-		/* write out one line of graphic data */
-		ppm_writeppmrow(stdout, destrow, cols, maxval, 0);
-	}
-
-	pm_close(ifp1);
-	pm_close(ifp2);
-	ppm_freerow(srcrow1);
-	ppm_freerow(srcrow2);
-	ppm_freerow(destrow);
-
-	exit(0);
+    return 0;
 }
-
diff --git a/editor/ppmquant b/editor/ppmquant
index 11bce6d2..57963982 100755
--- a/editor/ppmquant
+++ b/editor/ppmquant
@@ -1,4 +1,28 @@
-#!/usr/bin/perl -w
+#!/bin/sh
+
+##############################################################################
+# This is essentially a Perl program.  We exec the Perl interpreter specifying
+# this same file as the Perl program and use the -x option to cause the Perl
+# interpreter to skip down to the Perl code.  The reason we do this instead of
+# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is
+# that the user may have multiple Perl interpreters and the one he wants to
+# use is properly located in the PATH.  The user's choice of Perl interpreter
+# may be crucial, such as when the user also has a PERL5LIB environment
+# variable and it selects modules that work with only a certain main
+# interpreter program.
+#
+# An alternative some people use is to have /usr/bin/env as the script
+# interpreter.  We don't do that because we think the existence and
+# compatibility of /bin/sh is more reliable.
+#
+# Note that we aren't concerned about efficiency because the user who needs
+# high efficiency can use directly the programs that this program invokes.
+#
+##############################################################################
+
+exec perl -w -x -S -- "$0" "$@"
+
+#!/usr/bin/perl
 ##############################################################################
 #  This is nothing but a compatibility interface for Pnmquant.
 #  An old program coded to call Ppmquant will continue working because
@@ -13,8 +37,6 @@ use strict;
 
 use Getopt::Long;
 
-my $TRUE=1; my $FALSE = 0;
-
 my @ppmquantArgv = @ARGV;
 
 Getopt::Long::Configure('pass_through');
diff --git a/editor/ppmquantall b/editor/ppmquantall
deleted file mode 100755
index 4807de8c..00000000
--- a/editor/ppmquantall
+++ /dev/null
@@ -1,96 +0,0 @@
-#!/bin/sh
-#
-# ppmquantall - run ppmquant on a bunch of files all at once, so they share
-#               a common colormap
-#
-# WARNING: overwrites the source files with the results!!!
-#
-# Verbose explanation: Let's say you've got a dozen pixmaps that you want
-# to display on the screen all at the same time.  Your screen can only
-# display 256 different colors, but the pixmaps have a total of a thousand
-# or so different colors.  For a single pixmap you solve this problem with
-# pnmquant; this script solves it for multiple pixmaps.  All it does is
-# concatenate them together into one big pixmap, run pnmquant on that, and
-# then split it up into little pixmaps again.
-#
-# IMPLEMENTATION NOTE:  Now that Pnmcolormap can compute a single colormap
-# for a whole stream of images, this program could be implemented more
-# simply.  Today, it concatenates a bunch of images into one image, uses
-# Pnmquant to quantize that, then splits the result back into multiple
-# images.  It could instead just run Pnmcolormap over all the images,
-# then run Pnmremap on each input image using the one colormap for all.
-
-usage()
-{
-    echo "usage: $0 [-ext extension] <newcolors> <ppmfile> ..."
-    exit 1
-}
-
-ext=
-
-while :; do
-
-    case "$1" in
-    -ext*)
-        if [ $# -lt 2 ]; then
-            usage
-        fi
-        ext=".$2"
-        shift
-        shift
-    ;;
-
-    *)  
-        break
-    ;;
-
-    esac
-done
-
-if [ $# -lt 2 ]; then
-    usage
-fi
-
-newcolors=$1
-shift
-nfiles=$#
-files=($@)
-
-# Extract the width and height of each of the images.
-# Here, we make the assumption that the width and height are on the
-# second line, even though the PPM format doesn't require that.
-# To be robust, we need to use Pnmfile to get that information, or 
-# Put this program in C and use ppm_readppminit().
-
-widths=()
-heights=()
-
-for i in ${files[@]}; do
-    widths=(${widths[*]} `grep -v '^#' $i | sed '1d; s/ .*//; 2q'`)
-    heights=(${heights[*]} `grep -v '^#' $i | sed '1d; s/.* //; 2q'`)
-done
-
-tempdir="${TMPDIR-/tmp}/ppmquantall.$$"
-mkdir -m 0700 $tempdir || \
-  { echo "Could not create temporary file. Exiting."; exit 1;}
-trap 'rm -rf $tempdir' 0 1 3 15
-
-all=$tempdir/pqa.all.$$
-
-pnmcat -topbottom -jleft -white ${files[@]} | pnmquant $newcolors > $all
-if [ $? != 0 ]; then
-    exit $?
-fi
-
-y=0
-i=0
-
-while [ $i -lt $nfiles ]; do
-    pamcut -left 0 -top $y -width ${widths[$i]} -height ${heights[$i]} $all \
-        > ${files[$i]}$ext
-    if [ $? != 0 ]; then
-        exit $?
-    fi
-    y=$(($y + ${heights[$i]}))
-    i=$(($i + 1))
-done
diff --git a/editor/ppmquantall.csh b/editor/ppmquantall.csh
deleted file mode 100644
index 9a89bca0..00000000
--- a/editor/ppmquantall.csh
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/csh -f
-#
-# ppmquantall - run ppmquant on a bunch of files all at once, so they share
-#               a common colormap
-#
-# WARNING: overwrites the source files with the results!!!
-#
-# Verbose explanation: Let's say you've got a dozen pixmaps that you want
-# to display on the screen all at the same time.  Your screen can only
-# display 256 different colors, but the pixmaps have a total of a thousand
-# or so different colors.  For a single pixmap you solve this problem with
-# ppmquant; this script solves it for multiple pixmaps.  All it does is
-# concatenate them together into one big pixmap, run ppmquant on that, and
-# then split it up into little pixmaps again.
-
-if ( $#argv < 3 ) then
-    echo "usage:  ppmquantall <newcolors> <ppmfile> <ppmfile> ..."
-    exit 1
-endif
-
-set newcolors=$argv[1]
-set files=( $argv[2-] )
-
-# Extract the width and height of each of the images.
-# Here, we make the assumption that the width and height are on the
-# second line, even though the PPM format doesn't require that.
-# To be robust, we need to use Pnmfile to get that information, or 
-# Put this program in C and use ppm_readppminit().
-
-set widths=()
-set heights=()
-foreach i ( $files )
-    set widths=( $widths `sed '1d; s/ .*//; 2q' $i` )
-    set heights=( $heights `sed '1d; s/.* //; 2q' $i` )
-end
-
-set all=/tmp/pqa.all.$$
-rm -f $all
-pnmcat -topbottom -jleft -white $files | ppmquant -quiet $newcolors > $all
-if ( $status != 0 ) exit $status
-
-@ y = 0
-@ i = 1
-while ( $i <= $#files )
-    pnmcut -left 0 -top $y -width $widths[$i] -height $heights[$i] $all \
-       > $files[$i]
-    if ( $status != 0 ) exit $status
-    @ y = $y + $heights[$i]
-    @ i++
-end
-
-rm -f $all
-
-
-
-
-
diff --git a/editor/ppmshadow b/editor/ppmshadow
index 2a32fca0..62cdf8b8 100755
--- a/editor/ppmshadow
+++ b/editor/ppmshadow
@@ -1,15 +1,40 @@
-#!/usr/bin/perl -w
+#!/bin/sh
 
-#                         P P M S H A D O W
+##############################################################################
+# This is essentially a Perl program.  We exec the Perl interpreter specifying
+# this same file as the Perl program and use the -x option to cause the Perl
+# interpreter to skip down to the Perl code.  The reason we do this instead of
+# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is
+# that the user may have multiple Perl interpreters and the one he wants to
+# use is properly located in the PATH.  The user's choice of Perl interpreter
+# may be crucial, such as when the user also has a PERL5LIB environment
+# variable and it selects modules that work with only a certain main
+# interpreter program.
+#
+# An alternative some people use is to have /usr/bin/env as the script
+# interpreter.  We don't do that because we think the existence and
+# compatibility of /bin/sh is more reliable.
+#
+# Note that we aren't concerned about efficiency because the user who needs
+# high efficiency can use directly the programs that this program invokes.
+#
+##############################################################################
+
+exec perl -w -x -S -- "$0" "$@"
 
+#!/usr/bin/perl
+##############################################################################
+#                              ppmshadow
+##############################################################################
+#
 #            by John Walker  --  http://www.fourmilab.ch/
 #                          version = 1.2;
 #   --> with minor changes by Bryan Henderson to adapt to Netbpm.  
 #   See above web site for the real John Walker work, named pnmshadow.
-
+#
 #   Bryan Henderson later made some major style changes (use strict, etc) and
 #   eliminated most use of shells.  See Netbpm HISTORY file.
-
+#
 #   Pnmshadow is a brutal sledgehammer implemented in Perl which
 #   adds attractive shadows to images, as often seen in titles
 #   of World-Wide Web pages.  This program does not actually
@@ -20,7 +45,7 @@
 #
 #               This program is in the public domain.
 #
-#
+##############################################################################
 
 use strict;
 require 5.0;
diff --git a/editor/specialty/Makefile b/editor/specialty/Makefile
index 76befbb4..427c2c8f 100644
--- a/editor/specialty/Makefile
+++ b/editor/specialty/Makefile
@@ -11,11 +11,13 @@ PORTBINARIES = pamdeinterlace \
 	       pammixinterlace \
 	       pamoil \
 	       pampop9 \
+	       pampaintspill \
 	       pbmlife \
 	       pgmabel \
 	       pgmbentley \
 	       pgmmorphconv \
 	       pnmindex \
+	       pnmmercator \
 	       ppm3d \
 	       ppmglobe \
 	       ppmntsc \
diff --git a/editor/specialty/pamdeinterlace.c b/editor/specialty/pamdeinterlace.c
index 7c6b123c..d6f6aee1 100644
--- a/editor/specialty/pamdeinterlace.c
+++ b/editor/specialty/pamdeinterlace.c
@@ -47,7 +47,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     free(option_def);
diff --git a/editor/specialty/pammixinterlace.c b/editor/specialty/pammixinterlace.c
index 9f98b406..7410a8f1 100644
--- a/editor/specialty/pammixinterlace.c
+++ b/editor/specialty/pammixinterlace.c
@@ -200,7 +200,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
     /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!filterSpec)
diff --git a/editor/specialty/pampaintspill.c b/editor/specialty/pampaintspill.c
new file mode 100644
index 00000000..745c9b68
--- /dev/null
+++ b/editor/specialty/pampaintspill.c
@@ -0,0 +1,500 @@
+/* ----------------------------------------------------------------------
+ *
+ * Bleed colors from non-background colors into the background
+ *
+ * By Scott Pakin <scott+pbm@pakin.org>
+ *
+ * ----------------------------------------------------------------------
+ *
+ * Copyright (C) 2010 Scott Pakin <scott+pbm@pakin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+/*
+  This program contains code to work with Openmp, so that it can process
+  multiple columns at once, using multiple threads on multiple CPU cores,
+  and thus take less elapsed time to run.
+
+  But that code is dead in a normal Netpbm build, as it does not use the
+  required compiler options or link with the required library in any
+  conventional environment we know of.  One can exploit this code with a
+  modified build, e.g. with CADD and LADD make variables.
+
+  10.04.14
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+#include "pammap.h"
+
+
+static time_t const timeUpdateDelta = 30;
+    /* Seconds between progress updates */
+static int const    minUpdates = 4;
+    /* Minimum number of progress updates to output */
+
+
+struct cmdlineInfo {
+    /* This structure represents all of the information the user
+       supplied in the command line but in a form that's easy for the
+       program to use.
+    */
+    const char * inputFilename;  /* '-' if stdin */
+    const char * bgcolor;
+    unsigned int wrap;
+    unsigned int all;
+    float        power;
+    unsigned int downsample;
+};
+
+struct coords {
+    /* This structure represents an (x,y) coordinate within an image and
+       the color at that coordinate. */
+    unsigned int x;
+    unsigned int y;
+    tuple        color;
+};
+
+typedef double distFunc_t(const struct coords * const p0,
+                          const struct coords * const p1,
+                          unsigned int          const width,
+                          unsigned int          const height);
+    /* Distance function */
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct cmdlineInfo * const cmdlineP ) {
+
+    optEntry     * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options */
+    optStruct3     opt;
+    unsigned int   option_def_index;
+    unsigned int bgcolorSpec, powerSpec, downsampleSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+    option_def_index = 0;          /* Incremented by OPTENTRY */
+
+    OPTENT3(0, "bgcolor",    OPT_STRING, &cmdlineP->bgcolor,    
+            &bgcolorSpec, 0);
+    OPTENT3(0, "wrap",       OPT_FLAG,   NULL,
+            &cmdlineP->wrap,       0);
+    OPTENT3(0, "all",        OPT_FLAG,   NULL,
+            &cmdlineP->all,        0);
+    OPTENT3(0, "power",      OPT_FLOAT,  &cmdlineP->power,      
+            &powerSpec, 0);
+    OPTENT3(0, "downsample", OPT_UINT,   &cmdlineP->downsample, 
+            &downsampleSpec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = 0;
+    opt.allowNegNum = 1;
+
+    pm_optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0 );
+
+    if (!bgcolorSpec)
+        cmdlineP->bgcolor = NULL;
+
+    if (!powerSpec)
+        cmdlineP->power = -2.0;
+
+    if (!downsampleSpec)
+        cmdlineP->downsample = 0;
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilename = "-";
+    else {
+        cmdlineP->inputFilename = argv[1];
+        if (argc-1 > 1)
+            pm_error("Too many arguments: %u.  The only argument is the "
+                     "optional input file name", argc-1);
+    }
+}
+
+
+static bool
+tupleEqualColor(const struct pam * const pamP,
+                tuple              const comparand,
+                tuple              const comparator) {
+/*----------------------------------------------------------------------------
+  Report whether two tuples have equal color, regardless of alpha.
+----------------------------------------------------------------------------*/
+    unsigned int const nColorPlanes = pamP->depth >= 3 ? 3 : 1;
+
+    unsigned int plane;
+
+    for (plane = 0; plane < nColorPlanes; ++plane)
+        if (comparand[plane] != comparator[plane])
+            return false;
+
+    return true;
+}
+
+
+
+struct paintSourceSet {
+    struct coords * list;  /* malloc'ed */
+        /* The list of places in the image from which paint comes */
+    unsigned int size;
+        /* Number of entries in sources[] */
+    unsigned int alloc;
+        /* Number of slots for entries of allocated memory */
+};
+
+
+
+static void
+setPaintSourceColors(struct pam *          const pamP,
+                     tuple **              const tuples,
+                     struct paintSourceSet const paintSources) {
+/*----------------------------------------------------------------------------
+   Set the 'color' member of each source in 'paintSources'.
+
+   Set it to the color of the source pixel in tuples[][], indicated by
+   'paintSources'.
+
+   Malloc memory to store these colors -- a contiguous block of memory for all
+   of them.
+-----------------------------------------------------------------------------*/
+    struct pam pamPaint;
+        /* numPaintSources-wide PAM for use by pnm_allocpamrow() */
+    tuple * paintColor;
+        /* Points to storage for the color tuples */
+    unsigned int    i;
+
+    pamPaint = *pamP;
+    pamPaint.width = paintSources.size;
+    paintColor = pnm_allocpamrow(&pamPaint);
+
+    for (i = 0; i < paintSources.size; ++i) {
+        struct coords * const thisSourceP = &paintSources.list[i];
+
+        thisSourceP->color = paintColor[i];
+        pnm_assigntuple(pamP, thisSourceP->color,
+                        tuples[thisSourceP->y][thisSourceP->x]);
+    }
+}
+
+
+
+static void
+addPaintSource(unsigned int            const row,
+               unsigned int            const col,
+               struct paintSourceSet * const paintSourcesP) {
+
+    if (paintSourcesP->size == paintSourcesP->alloc) {
+        paintSourcesP->alloc += 1024;
+        REALLOCARRAY(paintSourcesP->list, paintSourcesP->alloc);
+        if (!paintSourcesP->list)
+            pm_error("Out of memory");
+    }
+    paintSourcesP->list[paintSourcesP->size].x = col;
+    paintSourcesP->list[paintSourcesP->size].y = row;
+    ++paintSourcesP->size;
+}
+
+
+
+static void
+locatePaintSources(struct pam *            const pamP,
+                   tuple **                const tuples,
+                   tuple                   const bgColor,
+                   unsigned int            const downsample,
+                   struct paintSourceSet * const paintSourcesP) {
+/*--------------------------------------------------------------------
+  Construct a list of all pixel coordinates in the input image that
+  represent a non-background color.
+  ----------------------------------------------------------------------*/
+    struct paintSourceSet paintSources;
+    int row;  /* signed so it works with Openmp */
+
+    paintSources.list  = NULL;
+    paintSources.size  = 0;
+    paintSources.alloc = 0;
+
+    #pragma omp parallel for
+    for (row = 0; row < pamP->height; ++row) {
+        unsigned int col;
+        for (col = 0; col < pamP->width; ++col) {
+            if (!tupleEqualColor(pamP, tuples[row][col], bgColor))
+                #pragma omp critical (addPaintSource)
+                addPaintSource(row, col, &paintSources);
+        }
+    }
+
+    pm_message("Image contains %u background + %u non-background pixels",
+               pamP->width * pamP->height - paintSources.size,
+               paintSources.size);
+    
+    /* Reduce the number of paint sources to reduce execution time. */
+    if (downsample > 0 && downsample < paintSources.size) {
+        unsigned int i;
+
+        srand(time(NULL));
+
+        for (i = 0; i < downsample; ++i) {
+            unsigned int const swapIdx =
+                i + rand() % (paintSources.size - i);
+            struct coords const swapVal = paintSources.list[i];
+
+            paintSources.list[i] = paintSources.list[swapIdx];
+            paintSources.list[swapIdx] = swapVal;
+        }
+        paintSources.size = downsample;
+    }
+
+    setPaintSourceColors(pamP, tuples, paintSources);
+
+    *paintSourcesP    = paintSources;
+}
+
+
+
+static distFunc_t euclideanDistanceSqr;
+
+static double
+euclideanDistanceSqr(const struct coords * const p0,
+                     const struct coords * const p1,
+                     unsigned int          const width,
+                     unsigned int          const height) {
+/*----------------------------------------------------------------------------
+   Return the square of the Euclidian distance between p0 and p1.
+-----------------------------------------------------------------------------*/
+    double const deltax = (double) (int) (p1->x - p0->x);
+    double const deltay = (double) (int) (p1->y - p0->y);
+
+    return SQR(deltax) + SQR(deltay);
+}
+
+
+
+static distFunc_t euclideanDistanceTorusSqr;
+
+static double
+euclideanDistanceTorusSqr(const struct coords * const p0,
+                          const struct coords * const p1,
+                          unsigned int          const width,
+                          unsigned int          const height) {
+/*----------------------------------------------------------------------------
+   Return the square of the Euclidian distance between p0 and p1, assuming
+   it's a toroidal surface on which the top row curves around to meet the
+   bottom and the left column to the right.
+-----------------------------------------------------------------------------*/
+    struct coords p0Adj, p1Adj;
+
+    if (p1->x >= p0->x + width / 2) {
+        p0Adj.x = p0->x + width;
+        p1Adj.x = p1->x;
+    } else if (p0->x >= p1->x + width / 2) {
+        p0Adj.x = p0->x;
+        p1Adj.x = p1->x + width;
+    } else {
+        p0Adj.x = p0->x;
+        p1Adj.x = p1->x;
+    }
+    if (p1->y >= p0->y + height / 2) {
+        p0Adj.y = p0->y + height;
+        p1Adj.y = p1->y;
+    } else if (p0->y >= p1->y + height / 2) {
+        p0Adj.y = p0->y;
+        p1Adj.y = p1->y + height;
+    } else {
+        p0Adj.y = p0->y;
+        p1Adj.y = p1->y;
+    }
+
+    return euclideanDistanceSqr(&p0Adj, &p1Adj, 0, 0);
+}
+
+
+
+static void
+reportProgress(unsigned int const rowsComplete,
+               unsigned int const height) {
+
+    static time_t prevOutputTime = 0;
+    time_t        now;                  /* Current time in seconds */
+
+    if (prevOutputTime == 0)
+        prevOutputTime = time(NULL);
+
+    /* Output our progress only every timeUpdateDelta seconds. */
+    now = time(NULL);
+    if (prevOutputTime) {
+        if (now - prevOutputTime >= timeUpdateDelta
+            || rowsComplete % (height/minUpdates) == 0) {
+            pm_message("%5.1f%% complete",
+                       rowsComplete * 100.0 / height);
+            prevOutputTime = now;
+        }
+    } else
+        prevOutputTime = now;
+}
+
+
+
+static void
+spillOnePixel(struct pam *          const pamP,
+              struct coords         const target,
+              struct paintSourceSet const paintSources,
+              distFunc_t *          const distFunc,
+              double                const distPower,
+              tuple                 const outTuple,
+              double *              const newColor) {
+
+    unsigned int plane;
+    unsigned int ps;
+    double       totalWeight;
+
+    for (plane = 0; plane < pamP->depth; ++plane)
+        newColor[plane] = 0.0;
+    totalWeight = 0.0;
+    for (ps = 0; ps < paintSources.size; ++ps) {
+        struct coords const source = paintSources.list[ps];
+        double const distSqr =
+            (*distFunc)(&target, &source,
+                        pamP->width, pamP->height);
+
+        if (distSqr > 0.0) {
+            /* We do special cases for some common cases with code
+               that is much faster than pow().
+            */
+            double const weight =
+                distPower == -2.0 ? 1.0 / distSqr :
+                distPower == -1.0 ? 1.0 / sqrt(distSqr):
+                pow(distSqr, distPower/2);
+
+            unsigned int plane;
+
+            for (plane = 0; plane < pamP->depth; ++plane)
+                newColor[plane] += weight * source.color[plane];
+
+            totalWeight += weight;
+        }
+    }
+    for (plane = 0; plane < pamP->depth; ++plane)
+        outTuple[plane] = (sample) (newColor[plane] / totalWeight);
+}
+
+
+
+static void
+produceOutputImage(struct pam *          const pamP,
+                   tuple **              const intuples,
+                   tuple                 const bgColor,
+                   struct paintSourceSet const paintSources,
+                   distFunc_t *          const distFunc,
+                   double                const distPower,
+                   bool                  const all,
+                   tuple ***             const outtuplesP) {
+/*--------------------------------------------------------------------
+  Color each background pixel (or, if allPixels is 1, all pixels)
+  using a fraction of each paint source as determined by its distance
+  to the background pixel.
+----------------------------------------------------------------------*/
+    int row;   /* signed so it works with Openmp */
+    unsigned int rowsComplete;
+    tuple ** outtuples;
+
+    outtuples = pnm_allocpamarray(pamP);
+
+    rowsComplete = 0;
+    #pragma omp parallel for
+    for (row = 0; row < pamP->height; ++row) {
+        struct coords   target;
+        double        * newColor;
+        
+        MALLOCARRAY(newColor, pamP->depth);
+
+        target.y = row;
+        for (target.x = 0; target.x < pamP->width; ++target.x) {
+            tuple const targetTuple = intuples[target.y][target.x];
+            tuple const outputTuple = outtuples[target.y][target.x];
+
+            if (all || tupleEqualColor(pamP, targetTuple, bgColor))
+                spillOnePixel(pamP, target, paintSources, distFunc, distPower,
+                              outputTuple, newColor);
+            else
+                pnm_assigntuple(pamP,  outputTuple, targetTuple);
+        }
+        #pragma omp critical (rowTally)
+        reportProgress(++rowsComplete, pamP->height);
+
+        free(newColor);
+    }
+    *outtuplesP = outtuples;
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+    FILE *             ifP;
+    struct cmdlineInfo cmdline;          /* Command-line parameters */
+    tuple              bgColor;          /* Input image's background color */
+    struct paintSourceSet paintSources;
+        /* The set of paint-source indexes into 'tuples' */
+    distFunc_t *       distFunc;         /* The distance function */
+    struct pam inPam;
+    struct pam outPam;
+    tuple ** inTuples;
+    tuple ** outTuples;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilename);
+
+    inTuples = pnm_readpam(ifP, &inPam, PAM_STRUCT_SIZE(allocation_depth));
+
+    pm_close(ifP);
+
+    distFunc = cmdline.wrap ? euclideanDistanceTorusSqr : euclideanDistanceSqr;
+
+    if (cmdline.bgcolor)
+        bgColor = pnm_parsecolor(cmdline.bgcolor, inPam.maxval) ;
+    else
+        bgColor = pnm_backgroundtuple(&inPam, inTuples);
+
+    pm_message("Treating %s as the background color",
+               pnm_colorname(&inPam, bgColor, PAM_COLORNAME_HEXOK));
+
+    locatePaintSources(&inPam, inTuples, bgColor, cmdline.downsample,
+                       &paintSources);
+
+    produceOutputImage(&inPam, inTuples, bgColor, paintSources, distFunc,
+                       cmdline.power, cmdline.all, &outTuples);
+
+    outPam = inPam;
+    outPam.file = stdout;
+    pnm_writepam(&outPam, outTuples);
+
+    pnm_freepamarray(outTuples, &inPam);
+    pnm_freepamarray(inTuples, &outPam);
+
+    return 0;
+}
diff --git a/editor/specialty/pgmabel.c b/editor/specialty/pgmabel.c
index 4914c4be..1764c5d7 100644
--- a/editor/specialty/pgmabel.c
+++ b/editor/specialty/pgmabel.c
@@ -38,15 +38,15 @@ static const char* const version="$VER: pgmabel 1.009 (24 Jan 2002)";
 
 #include <math.h>
 #include <stdlib.h>   /* for calloc */
-#include "pgm.h"
+
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "pgm.h"
 
 #ifndef PID2          /*  PI/2 (on AMIGA always defined) */
 #define PID2    1.57079632679489661923  
 #endif
 
-#define TRUE 1
-#define FALSE 0
 
 /* some global variables */
 static double *aldl, *ardl;                /* pointer for weighting factors */
diff --git a/editor/specialty/pgmmorphconv.c b/editor/specialty/pgmmorphconv.c
index abc4e718..2ba2d62d 100644
--- a/editor/specialty/pgmmorphconv.c
+++ b/editor/specialty/pgmmorphconv.c
@@ -17,237 +17,403 @@
 */
 
 #include "pm_c_util.h"
+#include "shhopt.h"
+#include "mallocvar.h"
 #include "pgm.h"
 
 
-/************************************************************
- * Dilate 
- ************************************************************/
 
-static int 
-dilate( bit** template, int trowso2, int tcolso2, 
-        gray** in_image, gray** out_image, 
-        int rows, int cols ){
+enum Operation { ERODE, DILATE, OPEN, CLOSE, GRADIENT };
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;  /* File name of input file */
+    const char * templateFileName;  /* File name of template file */
 
-  int c, r, tc, tr;
-  int templatecount;
-  gray source;
+    enum Operation operation;
+};
 
-  for( c=0; c<cols; ++c)
-    for( r=0; r<rows; ++r )
-      out_image[r][c] = 0;   /* only difference with erode is here and below */
-  
-  /* 
-   *  for each non-black pixel of the template
-   *  add in to out
-   */
 
-  templatecount=0;
 
-  for( tr=-trowso2; tr<=trowso2; ++tr ){
-    for( tc=-tcolso2; tc<=tcolso2; ++tc ){
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+    unsigned int option_def_index;
+    unsigned int erode, dilate, open, close, gradient;
 
-      if( template[trowso2+tr][tcolso2+tc] == PBM_BLACK ) continue;
+    MALLOCARRAY_NOFAIL(option_def, 100);
 
-      ++templatecount;
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "erode",        OPT_FLAG,   NULL, &erode,           0);
+    OPTENT3(0,   "dilate",       OPT_FLAG,   NULL, &dilate,          0);
+    OPTENT3(0,   "open",         OPT_FLAG,   NULL, &open,            0);
+    OPTENT3(0,   "close",        OPT_FLAG,   NULL, &close,           0);
+    OPTENT3(0,   "gradient",     OPT_FLAG,   NULL, &gradient,        0);
 
-      for( r= ((tr>0)?0:-tr) ; r< ((tr>0)?(rows-tr):rows) ; ++r ){
-        for( c= ((tc>0)?0:-tc) ; c< ((tc>0)?(cols-tc):cols) ; ++c ){
-          source = in_image[r+tr][c+tc];
-          out_image[r][c] = MAX(source, out_image[r][c]);
-        } /* for c */
-      } /* for r */
-    } /* for tr */
-  } /* for tc */
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = TRUE;  /* We may have parms that are negative numbers */
 
-  return templatecount;
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-} /* dilate */
+    if (erode + dilate + open + close + gradient > 1)
+        pm_error("You may specify at most one of -erode, -dilate, "
+                 "-open, -close, or -gradient");
 
+    if (erode)
+        cmdlineP->operation = ERODE;
+    else if (dilate)
+        cmdlineP->operation = DILATE;
+    else if (open)
+        cmdlineP->operation = OPEN;
+    else if (close)
+        cmdlineP->operation = CLOSE;
+    else if (gradient)
+        cmdlineP->operation = GRADIENT;
+    else
+        cmdlineP->operation = DILATE;
+
+    if (argc-1 < 1)
+        pm_error("You must specify the template file name as an argument");
+    else {
+        cmdlineP->templateFileName = argv[1];
+
+        if (argc-1 < 2)
+            cmdlineP->inputFileName = "-";
+        else {
+            cmdlineP->inputFileName = argv[2];
+            
+            if (argc-1 > 2)
+                pm_error("Too many arguments: %u.  "
+                         "The only possible arguments "
+                         "are the template file name and the input file name",
+                         argc-1);
+        }
+    }
+}
+
+
+
+static void
+readTemplateMatrix(const char *   const fileName,
+                   bit ***        const templateP,
+                   unsigned int * const rowsP,
+                   unsigned int * const colsP) {
+/*----------------------------------------------------------------------------
+  Read in the template matrix.
+-----------------------------------------------------------------------------*/
+    FILE * templateFileP;
+    int cols, rows;
+
+    templateFileP = pm_openr(fileName);
+
+    *templateP = pbm_readpbm(templateFileP, &cols, &rows);
+
+    pm_close(templateFileP);
+
+    if (cols % 2 != 1 || rows % 2 != 1)
+        pm_error("the template matrix must have an odd number of "
+                 "rows and columns" );
+
+        /* the reason is that we want the middle pixel to be the origin */
+    *rowsP = rows;
+    *colsP = cols;
+}
+
+
+
+static void
+setAllPixel(gray **      const image,
+            unsigned int const rows,
+            unsigned int const cols,
+            gray         const value) {
+
+    unsigned int col;
+
+    for (col = 0; col < cols; ++col) {
+        unsigned int row;
+        for (row = 0; row < rows; ++row)
+            image[row][col] = value; 
+    }
+}
+
+
+
+static void
+dilate(bit **         const template,
+       int            const trowso2,
+       int            const tcolso2, 
+       gray **        const inImage,
+       gray **        const outImage, 
+       unsigned int   const rows,
+       unsigned int   const cols,
+       unsigned int * const templateCountP) {
+
+    unsigned int templateCount;
+    int tr;
+
+    setAllPixel(outImage, rows, cols, 0);
+
+    /* for each non-black pixel of the template add in to out */
+
+    for (tr = -trowso2, templateCount = 0; tr <= trowso2; ++tr) {
+        int tc;
+        for (tc = -tcolso2; tc <= tcolso2; ++tc) {
+            int r;
+            if (template[trowso2+tr][tcolso2+tc] != PBM_BLACK) {
+                ++templateCount;
+
+                for (r = ((tr > 0) ? 0 : -tr);
+                     r < ((tr > 0) ? (rows-tr) : rows);
+                     ++r) {
+                    int c;
+                    for (c = ((tc > 0) ? 0 : -tc);
+                         c < ((tc > 0) ? (cols-tc) : cols);
+                         ++c) {
+                        gray const source = inImage[r+tr][c+tc];
+                        outImage[r][c] = MAX(source, outImage[r][c]);
+                    }
+                }
+            }
+        }
+    }
+    *templateCountP = templateCount;
+}
+
+
+
+static void
+erode(bit **         const template,
+      int            const trowso2,
+      int            const tcolso2, 
+      gray **        const inImage,
+      gray **        const outImage, 
+      unsigned int   const rows,
+      unsigned int   const cols,
+      unsigned int * const templateCountP) {
+
+    unsigned int templateCount;
+    int tr;
+
+    setAllPixel(outImage, rows, cols, PGM_MAXMAXVAL);
+
+    /* For each non-black pixel of the template add in to out */
+
+    for (tr = -trowso2, templateCount = 0; tr <= trowso2; ++tr) {
+        int tc;
+        for (tc = -tcolso2; tc <= tcolso2; ++tc) {
+            if (template[trowso2+tr][tcolso2+tc] != PBM_BLACK) {
+                int r;
+                ++templateCount;
+
+                for (r = ((tr > 0) ? 0 : -tr);
+                     r < ((tr > 0) ? (rows-tr) : rows);
+                     ++r){
+                    int c;
+
+                    for (c = ((tc > 0) ? 0 : -tc);
+                         c < ((tc > 0) ? (cols-tc) : cols);
+                         ++c) {
+                        
+                        gray const source = inImage[r+tr][c+tc];
+                        outImage[r][c] = MIN(source, outImage[r][c]);
+      
+                    }
+                }
+            }
+        }
+    }
+    *templateCountP = templateCount;
+}
 
 
-/************************************************************
- * Erode: same as dilate except !!!!
- ************************************************************/
 
-static int 
-erode( bit** template, int trowso2, int tcolso2, 
-       gray** in_image, gray** out_image, 
-       int rows, int cols ){
+static void
+openMorph(bit **         const template,
+          int            const trowso2,
+          int            const tcolso2, 
+          gray **        const inputImage,
+          gray **        const outputImage, 
+          unsigned int   const rows,
+          unsigned int   const cols,
+          unsigned int * const templateCountP) {
 
-  int c, r, tc, tr;
-  int templatecount;
-  gray source;
+    gray ** erodedImage;
+    unsigned int erodedTemplateCount;
 
-  for( c=0; c<cols; ++c)
-    for( r=0; r<rows; ++r )
-      out_image[r][c] = PGM_MAXMAXVAL; /* !!!! */
-  
-  /* 
-   *  for each non-black pixel of the template
-   *  add in to out
-   */
+    erodedImage = pgm_allocarray(cols, rows);
+    
+    erode(template, trowso2, tcolso2, 
+          inputImage, erodedImage, rows, cols, &erodedTemplateCount);
 
-  templatecount=0;
+    dilate(template, trowso2, tcolso2, 
+           erodedImage, outputImage, rows, cols, templateCountP);
 
-  for( tr=-trowso2; tr<=trowso2; ++tr ){
-    for( tc=-tcolso2; tc<=tcolso2; ++tc ){
+    pgm_freearray(erodedImage, rows);
+}
 
-      if( template[trowso2+tr][tcolso2+tc] == PBM_BLACK ) continue;
 
-      ++templatecount;
 
-      for( r= ((tr>0)?0:-tr) ; r< ((tr>0)?(rows-tr):rows) ; ++r ){
-    for( c= ((tc>0)?0:-tc) ; c< ((tc>0)?(cols-tc):cols) ; ++c ){
+static void
+closeMorph(bit **         const template,
+           int            const trowso2,
+           int            const tcolso2, 
+           gray **        const inputImage,
+           gray **        const outputImage, 
+           unsigned int   const rows,
+           unsigned int   const cols,
+           unsigned int * const templateCountP) {
 
-      source = in_image[r+tr][c+tc];
-      out_image[r][c] = MIN(source, out_image[r][c]);
-      
-    } /* for c */
-      } /* for r */
+    gray ** dilatedImage;
+    unsigned int dilatedTemplateCount;
 
+    dilatedImage = pgm_allocarray(cols, rows);
 
+    dilate(template, trowso2, tcolso2, 
+           inputImage, dilatedImage, rows, cols, &dilatedTemplateCount);
 
-    } /* for tr */
-  } /* for tc */
+    erode(template, trowso2, tcolso2, 
+          dilatedImage, outputImage, rows, cols, templateCountP);
 
-  return templatecount;
+    pgm_freearray(dilatedImage, rows);
+}
 
-} /* erode */
 
 
+static void
+subtract(gray **      const subtrahendImage,
+         gray **      const subtractorImage,
+         gray **      const outImage, 
+         unsigned int const rows,
+         unsigned int const cols ) {
 
-/************************************************************
- *  Main
- ************************************************************/
+    /* (I call the minuend the subtrahend and the subtrahend the subtractor,
+       to be consistent with other arithmetic terminology).
+    */
 
+    unsigned int c;
 
-int main( int argc, char* argv[] ){
+    for (c = 0; c < cols; ++c) {
+        unsigned int r;
+        for (r = 0; r < rows; ++r)
+            outImage[r][c] = subtrahendImage[r][c] - subtractorImage[r][c];
+    }
+}
 
-  int argn;
-  char operation;
-  const char* usage = "-dilate|-erode|-open|-close <templatefile> [pgmfile]";
 
-  FILE* tifp;   /* template */
-  int tcols, trows;
-  int tcolso2, trowso2;
-  bit** template;
 
+static void
+gradient(bit **         const template,
+         int            const trowso2,
+         int            const tcolso2, 
+         gray **        const inputImage,
+         gray **        const outputImage, 
+         unsigned int   const rows,
+         unsigned int   const cols,
+         unsigned int * const templateCountP) {
 
-  FILE*  ifp;   /* input image */
-  int cols, rows;
-  gray maxval;
+    gray ** dilatedImage;
+    gray ** erodedImage;
+    unsigned int dilatedTemplateCount;
+    
+    dilatedImage = pgm_allocarray(cols, rows);
+    erodedImage = pgm_allocarray(cols, rows);
 
-  gray** in_image;
-  gray** out_image;
+    dilate(template, trowso2, tcolso2, 
+           inputImage, dilatedImage, rows, cols, &dilatedTemplateCount);
 
-  int templatecount=0;
+    erode(template, trowso2, tcolso2, 
+          inputImage, erodedImage, rows, cols, templateCountP);
 
-  pgm_init( &argc, argv );
+    subtract(dilatedImage, erodedImage, outputImage, rows, cols);
 
-  /*
-   *  parse arguments
-   */ 
-  
-  ifp = stdin;
-  operation = 'd';
+    pgm_freearray(erodedImage, rows );
+    pgm_freearray(dilatedImage, rows );
+}
 
-  argn=1;
-  
-  if( argn == argc ) pm_usage( usage );
-  
-  if( pm_keymatch( argv[argn], "-erode", 2  )) { operation='e'; argn++; }
-  else
-  if( pm_keymatch( argv[argn], "-dilate", 2 )) { operation='d'; argn++; }
-  else
-  if( pm_keymatch( argv[argn], "-open", 2   )) { operation='o'; argn++; }
-  else
-  if( pm_keymatch( argv[argn], "-close", 2  )) { operation='c'; argn++; }
-  
-  if( argn == argc ) pm_usage( usage );
-  
-  tifp = pm_openr( argv[argn++] );
-  
-  if( argn != argc ) ifp = pm_openr( argv[argn++] );
 
-  if( argn != argc ) pm_usage( usage );
 
-  
-  /* 
-   * Read in the template matrix.
-   */
+int
+main(int argc, const char ** argv) {
 
-  template = pbm_readpbm( tifp, &tcols, &trows );
-  pm_close( tifp );
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    bit ** template;
+    unsigned int templateCols, templateRows;
+    int cols, rows;
+    gray maxval;
+    gray ** inputImage;
+    gray ** outputImage;
+    unsigned int templateCount;
 
-  if( tcols % 2 != 1 || trows % 2 != 1 )
-    pm_error("the template matrix must have an odd number of "
-             "rows and columns" );
+    pm_proginit(&argc, argv);
 
-  /* the reason is that we want the middle pixel to be the origin */
-  tcolso2 = tcols / 2; /* template coords run from -tcols/2 .. 0 .. +tcols/2 */
-  trowso2 = trows / 2;
+    parseCommandLine(argc, argv, &cmdline);
 
-#if 0
-  fprintf(stderr, "template: %d  x %d\n", trows, tcols);
-  fprintf(stderr, "half: %d  x %d\n", trowso2, tcolso2);
-#endif
+    ifP = pm_openr(cmdline.inputFileName);
 
-  /*
-   * Read in the image
-   */
-  
-  in_image = pgm_readpgm( ifp, &cols, &rows, &maxval);
+    readTemplateMatrix(cmdline.templateFileName,
+                       &template, &templateRows, &templateCols);
 
-  if( cols < tcols || rows < trows )
-    pm_error("the image is smaller than the convolution matrix" );
+    /* Template coords run from -templateCols/2 .. 0 .. + templateCols/2 */
   
-#if 0
-  fprintf(stderr, "image: %d  x %d (%d)\n", rows, cols, maxval);
-#endif
-
-  /* 
-   * Allocate  output buffer and initialize with min or max value 
-   */
+    inputImage = pgm_readpgm(ifP, &cols, &rows, &maxval);
 
-  out_image = pgm_allocarray( cols, rows );
+    if (cols < templateCols || rows < templateRows)
+        pm_error("the image is smaller than the convolution matrix" );
   
-  if( operation == 'd' ){
-    templatecount = dilate(template, trowso2, tcolso2, 
-               in_image, out_image, rows, cols);
-  } 
-  else if( operation == 'e' ){
-    templatecount = erode(template, trowso2, tcolso2, 
-              in_image, out_image, rows, cols);
-  }
-  else if( operation == 'o' ){
-    gray ** eroded_image;
-    eroded_image = pgm_allocarray( cols, rows );
-    templatecount = erode(template, trowso2, tcolso2, 
-                          in_image, eroded_image, rows, cols);
-    templatecount = dilate(template, trowso2, tcolso2, 
-                           eroded_image, out_image, rows, cols);
-    pgm_freearray( eroded_image, rows );
-  }
-  else if( operation == 'c' ){
-    gray ** dilated_image;
-    dilated_image = pgm_allocarray( cols, rows );
-    templatecount = dilate(template, trowso2, tcolso2, 
-                           in_image, dilated_image, rows, cols);
-    templatecount = erode(template, trowso2, tcolso2, 
-                          dilated_image, out_image, rows, cols);
-    pgm_freearray( dilated_image, rows );
-  }
+    outputImage = pgm_allocarray(cols, rows);
   
-  if(templatecount == 0 ) pm_error( "The template was empty!" );
-
-  pgm_writepgm( stdout, out_image, cols, rows, maxval, 1 );
-
-  pgm_freearray( out_image, rows );
-  pgm_freearray( in_image, rows );
-  pm_close( ifp );
-
-  exit( 0 );
-
-} /* main */
+    switch (cmdline.operation) {
+    case DILATE:
+        dilate(template, templateRows/2, templateCols/2,
+               inputImage, outputImage, rows, cols,
+               &templateCount);
+        break;
+    case ERODE:
+        erode(template, templateRows/2, templateCols/2,
+              inputImage, outputImage, rows, cols,
+              &templateCount);
+        break;
+    case OPEN:
+        openMorph(template, templateRows/2, templateCols/2,
+              inputImage, outputImage, rows, cols,
+              &templateCount);
+        break;
+    case CLOSE:
+        closeMorph(template, templateRows/2, templateCols/2,
+                   inputImage, outputImage, rows, cols,
+                   &templateCount);
+        break;
+    case GRADIENT:
+        gradient(template, templateRows/2, templateCols/2,
+                 inputImage, outputImage, rows, cols,
+                 &templateCount);
+        break;
+    }
+
+    if (templateCount == 0)
+        pm_error( "The template was empty!" );
+
+    pgm_writepgm(stdout, outputImage, cols, rows, maxval, 0);
+
+    pgm_freearray(outputImage, rows);
+    pgm_freearray(inputImage, rows);
+    pm_close(ifP);
+
+    return 0;
+}
 
diff --git a/editor/specialty/pnmindex.c b/editor/specialty/pnmindex.c
index ca1da18c..4ec9edaa 100644
--- a/editor/specialty/pnmindex.c
+++ b/editor/specialty/pnmindex.c
@@ -14,6 +14,7 @@
 
 ============================================================================*/
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup is in string.h */
 
 #include <assert.h>
@@ -59,7 +60,7 @@ systemf(const char * const fmt,
     
     va_start(varargs, fmt);
     
-    vsnprintfN(NULL, 0, fmt, varargs, &dryRunLen);
+    pm_vsnprintf(NULL, 0, fmt, varargs, &dryRunLen);
 
     va_end(varargs);
 
@@ -72,7 +73,7 @@ systemf(const char * const fmt,
         shellCommand = malloc(allocSize);
         if (shellCommand == NULL)
             pm_error("Can't get storage for %u-character command",
-                     allocSize);
+                     (unsigned)allocSize);
         else {
             va_list varargs;
             size_t realLen;
@@ -80,7 +81,7 @@ systemf(const char * const fmt,
 
             va_start(varargs, fmt);
 
-            vsnprintfN(shellCommand, allocSize, fmt, varargs, &realLen);
+            pm_vsnprintf(shellCommand, allocSize, fmt, varargs, &realLen);
                 
             assert(realLen == dryRunLen);
             va_end(varargs);
@@ -93,7 +94,7 @@ systemf(const char * const fmt,
                 pm_error("shell command '%s' failed.  rc %d",
                          shellCommand, rc);
             
-            strfree(shellCommand);
+            pm_strfree(shellCommand);
         }
     }
 }
@@ -106,7 +107,7 @@ parseCommandLine(int argc, char ** argv,
 
     unsigned int option_def_index;
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -137,7 +138,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE; 
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (quant && cmdlineP->noquant)
@@ -184,7 +185,7 @@ freeCmdline(struct cmdlineInfo const cmdline) {
     unsigned int i;
 
     for (i = 0; i < cmdline.inputFileCount; ++i)
-        strfree(cmdline.inputFileName[i]);
+        pm_strfree(cmdline.inputFileName[i]);
 
     free(cmdline.inputFileName);
 
@@ -200,7 +201,7 @@ makeTempDir(const char ** const tempDirP) {
     const char * mytmpdir;
     int rc;
 
-    asprintfN(&mytmpdir, "%s/pnmindex_%d", tmpdir, getpid());
+    pm_asprintf(&mytmpdir, "%s/pnmindex_%d", tmpdir, getpid());
 
     rc = pm_mkdir(mytmpdir, 0700);
     if (rc != 0)
@@ -232,7 +233,7 @@ rowFileName(const char * const dirName,
 
     const char * fileName;
     
-    asprintfN(&fileName, "%s/pi.%u", dirName, row);
+    pm_asprintf(&fileName, "%s/pi.%u", dirName, row);
     
     return fileName;
 }
@@ -259,7 +260,7 @@ makeTitle(const char * const title,
             "> %s", 
             title, invertStage, fileName);
 
-    strfree(fileName);
+    pm_strfree(fileName);
 }
 
 
@@ -285,28 +286,28 @@ copyScaleQuantImage(const char * const inputFileName,
 
     switch (PNM_FORMAT_TYPE(format)) {
     case PBM_TYPE:
-        asprintfN(&scaleCommand, 
-                  "pamscale -quiet -xysize %u %u %s "
-                  "| pgmtopbm > %s",
-                  size, size, inputFileName, outputFileName);
+        pm_asprintf(&scaleCommand, 
+                    "pamscale -quiet -xysize %u %u %s "
+                    "| pgmtopbm > %s",
+                    size, size, inputFileName, outputFileName);
         break;
         
     case PGM_TYPE:
-        asprintfN(&scaleCommand, 
-                  "pamscale -quiet -xysize %u %u %s >%s",
-                  size, size, inputFileName, outputFileName);
+        pm_asprintf(&scaleCommand, 
+                    "pamscale -quiet -xysize %u %u %s >%s",
+                    size, size, inputFileName, outputFileName);
         break;
         
     case PPM_TYPE:
         if (quant)
-            asprintfN(&scaleCommand, 
-                      "pamscale -quiet -xysize %u %u %s "
-                      "| pnmquant -quiet %u > %s",
-                      size, size, inputFileName, colors, outputFileName);
+            pm_asprintf(&scaleCommand, 
+                        "pamscale -quiet -xysize %u %u %s "
+                        "| pnmquant -quiet %u > %s",
+                        size, size, inputFileName, colors, outputFileName);
         else
-            asprintfN(&scaleCommand, 
-                      "pamscale -quiet -xysize %u %u %s >%s",
-                      size, size, inputFileName, outputFileName);
+            pm_asprintf(&scaleCommand, 
+                        "pamscale -quiet -xysize %u %u %s >%s",
+                        size, size, inputFileName, outputFileName);
         break;
     default:
         pm_error("Unrecognized Netpbm format: %d", format);
@@ -314,7 +315,7 @@ copyScaleQuantImage(const char * const inputFileName,
 
     systemf("%s", scaleCommand);
 
-    strfree(scaleCommand);
+    pm_strfree(scaleCommand);
 }
 
 
@@ -340,7 +341,7 @@ thumbnailFileName(const char * const dirName,
 
     const char * fileName;
     
-    asprintfN(&fileName, "%s/pi.%u.%u", dirName, row, col);
+    pm_asprintf(&fileName, "%s/pi.%u.%u", dirName, row, col);
     
     return fileName;
 }
@@ -372,7 +373,7 @@ thumbnailFileList(const char * const dirName,
             strcat(list, " ");
             strcat(list, fileName);
         }
-        strfree(fileName);
+        pm_strfree(fileName);
     }
 
     return list;
@@ -420,7 +421,7 @@ makeThumbnail(const char *  const inputFileName,
     pnm_readpnminit(ifP, &imageCols, &imageRows, &maxval, &format);
     pm_close(ifP);
     
-    asprintfN(&tmpfile, "%s/pi.tmp", tempDir);
+    pm_asprintf(&tmpfile, "%s/pi.tmp", tempDir);
 
     if (imageCols < size && imageRows < size)
         copyImage(inputFileName, tmpfile);
@@ -434,8 +435,8 @@ makeThumbnail(const char *  const inputFileName,
 
     unlink(tmpfile);
 
-    strfree(fileName);
-    strfree(tmpfile);
+    pm_strfree(fileName);
+    pm_strfree(tmpfile);
 
     *formatP = format;
 }
@@ -454,7 +455,7 @@ unlinkThumbnailFiles(const char * const dirName,
 
         unlink(fileName);
 
-        strfree(fileName);
+        pm_strfree(fileName);
     }
 }
 
@@ -471,7 +472,7 @@ unlinkRowFiles(const char * const dirName,
 
         unlink(fileName);
 
-        strfree(fileName);
+        pm_strfree(fileName);
     }
 }
 
@@ -497,7 +498,7 @@ combineIntoRowAndDelete(unsigned int const row,
     unlink(fileName);
 
     if (maxFormatType == PPM_TYPE && quant)
-        asprintfN(&quantStage, "| pnmquant -quiet %u ", colors);
+        pm_asprintf(&quantStage, "| pnmquant -quiet %u ", colors);
     else
         quantStage = strdup("");
 
@@ -508,9 +509,9 @@ combineIntoRowAndDelete(unsigned int const row,
             ">%s",
             blackWhiteOpt, fileList, quantStage, fileName);
 
-    strfree(fileList);
-    strfree(quantStage);
-    strfree(fileName);
+    pm_strfree(fileList);
+    pm_strfree(quantStage);
+    pm_strfree(fileName);
 
     unlinkThumbnailFiles(tempDir, row, cols);
 }
@@ -542,7 +543,7 @@ rowFileList(const char * const dirName,
             strcat(list, " ");
             strcat(list, fileName);
         }
-        strfree(fileName);
+        pm_strfree(fileName);
     }
 
     return list;
@@ -564,7 +565,7 @@ writeRowsAndDelete(unsigned int const rows,
     const char * fileList;
     
     if (maxFormatType == PPM_TYPE && quant)
-        asprintfN(&quantStage, "| pnmquant -quiet %u ", colors);
+        pm_asprintf(&quantStage, "| pnmquant -quiet %u ", colors);
     else
         quantStage = strdup("");
 
@@ -573,8 +574,8 @@ writeRowsAndDelete(unsigned int const rows,
     systemf("pnmcat %s -topbottom %s %s",
             blackWhiteOpt, fileList, quantStage);
 
-    strfree(fileList);
-    strfree(quantStage);
+    pm_strfree(fileList);
+    pm_strfree(quantStage);
 
     unlinkRowFiles(tempDir, rows);
 }
diff --git a/editor/specialty/pnmmercator.c b/editor/specialty/pnmmercator.c
new file mode 100644
index 00000000..cd9ff19b
--- /dev/null
+++ b/editor/specialty/pnmmercator.c
@@ -0,0 +1,430 @@
+/* pammercator.c - convert a map in PNM image format from 360x180 degrees
+**                 to Mercator projection or vice versa
+**
+** This program borrowed a lot of code from PnmScale which again was derived
+** from PpmScale.
+**
+** Copyright (C) 2009 by Willem van Schaik <willem@schaik.com>
+** Copyright (C) 1989, 1991 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.
+**
+*/
+
+#define _XOPEN_SOURCE  /* Make sure M_PI is in <math.h> */
+#include <math.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pnm.h"
+
+/* The pnm library allows us to code this program without branching cases for
+   PGM and PPM, but we do the branch anyway to speed up processing of PGM
+   images. 
+*/
+
+
+
+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;  /* Filespecs of input files */
+    const char * inputFileName;  /* Filespec of input file */
+    unsigned int inverse; /* from Mercator to Degrees */
+    unsigned int nomix;
+    unsigned int verbose;
+    unsigned int vverbose;
+};
+
+
+
+static void
+parseCommandLine(int                        argc, 
+                 const char **              argv,
+                 struct cmdlineInfo * const cmdlineP ) {
+
+    optEntry * option_def;
+    optStruct3 opt;
+        /* Instructions to pm_optParseOptions3 on how to parse our options. */
+    
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "inverse",   OPT_FLAG,    NULL,       &cmdlineP->inverse,   0);
+    OPTENT3(0, "nomix",     OPT_FLAG,    NULL,       &cmdlineP->nomix,     0);
+    OPTENT3(0, "verbose",   OPT_FLAG,    NULL,       &cmdlineP->verbose,   0);
+    OPTENT3(0, "vverbose",  OPT_FLAG,    NULL,       &cmdlineP->vverbose,  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 */
+
+    /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    pm_optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0 );
+
+    /* Only parameter allowed is optional filespec */
+    if (argc-1 < 1)
+        cmdlineP->input_filespec = "-";
+    else
+        cmdlineP->input_filespec = argv[1];
+}
+
+
+
+static void
+computeOutputDimensions(const struct cmdlineInfo cmdline, 
+                        const int rows, const int cols,
+                        int * newrowsP, int * newcolsP) 
+{
+    *newcolsP = cols;
+    if (!cmdline.inverse)
+        *newrowsP = 2 * rows;
+    else
+        *newrowsP = rows / 2;
+
+    if (*newcolsP < 1) *newcolsP = 1;
+    if (*newrowsP < 1) *newrowsP = 1;
+
+    if (cmdline.verbose) {
+        if (!cmdline.inverse)
+            pm_message("Creating Mercator map, new size is %dx%d",
+                       *newcolsP, *newrowsP);
+        else
+            pm_message("Creating Degrees map, new size is %dx%d",
+                       *newcolsP, *newrowsP);
+    }
+}        
+
+
+
+static void
+transformWithMixing(FILE * const ifP,
+                    int const cols, int const rows,
+                    xelval const maxval, int const format,
+                    int const newcols, int const newrows,
+                    xelval const newmaxval, int const newformat,
+                    bool const inverse,
+                    bool const verbose, bool const vverbose) 
+{
+    /* 
+    Transform the map image on input file 'ifP' (which is described by 
+    'cols', 'rows', 'format', and 'maxval') to a Mercator projection
+    and write the result to standard output as format 'newformat' and 
+    with maxval 'newmaxval'.
+
+    We'll blend colors from subsequent rows in the output pixels. 
+    */
+
+    xel* oddxelrow;  /* an input row */
+    xel* evenxelrow;
+    xel* newxelrow;  /* the output row */
+
+    int row;
+    double fRow;
+    int rowInXelrow;
+    int inputRow;
+    int col;
+
+    double fFract;
+    double fLatRad;
+    double fLatMerc;
+
+    oddxelrow = pnm_allocrow(cols); 
+    evenxelrow = pnm_allocrow(cols); 
+    rowInXelrow = 0;
+
+    newxelrow = pnm_allocrow(newcols);
+
+    for (row = 0; row < newrows; ++row) {
+        
+        fRow = (double)row + 0.5; /* center on half the pixel */
+        if (!inverse) {
+            /* the result is mercator, calculate back to degrees */
+            fLatMerc = 2.0 * M_PI * (fRow / (double)newrows - 0.5);
+                /* merc goes from -pi to +pi */
+            fLatRad = 2.0 * (atan(exp(fLatMerc)) - M_PI / 4.0);
+            fRow = ((fLatRad / M_PI) + 0.5) * (double)rows;
+                /* lat goes from -pi/2 to +pi/2 */
+        } else {
+            /* the result is in degrees, calculate back to mercator */
+            fLatRad = M_PI * (fRow / (double)newrows - 0.5);
+                /* lat goes from -pi/2 to +pi/2 */
+            fLatMerc = log(tan((M_PI / 4.0 + fLatRad / 2.0)));
+            fRow = ((fLatMerc / M_PI / 2.0) + 0.5) * (double)rows;
+                /* merc goes from -pi to +pi */
+        }
+        fRow -= 0.5;
+
+        inputRow = (int) floor(fRow);
+        if (inputRow < 0)
+            inputRow = 0;
+        if (inputRow > rows - 1)
+            inputRow = rows - 1;
+
+        /* calculate the fraction */
+        fFract = 1.0 - ((fRow) - (double) inputRow);
+
+        if (vverbose) {
+            if (!inverse) {
+#ifdef DBG_EDGES
+                if ((row < 10) || (row > (newrows - 10)))
+#else
+  #ifdef DBG_MIDDLE
+                if ((row > (newrows/2 - 10)) && (row < (newrows/2 + 10)))
+  #else
+                if (row % 20 == 0)
+  #endif
+#endif
+                    pm_message("outputRow=%d latMerc=%f latRad=%f inputRow=%d "
+                               "fRow=%f fFract=%f",
+                               row, fLatMerc, fLatRad, inputRow, fRow, fFract);
+            } else {
+#ifdef DBG_EDGES
+                if ((row < 10) || (row > (newrows - 10)))
+#else
+  #ifdef DBG_MIDDLE
+                if ((row > (newrows/2 - 10)) && (row < (newrows/2 + 10)))
+  #else
+                if (row % 10 == 0)
+  #endif
+#endif
+                    pm_message("outputRow=%d latRad=%f latMerc=%f inputRow=%d"
+                               "fRow=%f fFract=%f",
+                               row, fLatRad, fLatMerc, inputRow, fRow, fFract);
+            }
+        }
+
+        while ((rowInXelrow <= inputRow + 1) && 
+               (rowInXelrow < rows)) {
+            /* we need to read one row ahead */
+            if (rowInXelrow % 2 == 0) {
+#ifdef DBG_EDGES
+                if ((row < 10) || (row > (newrows - 10)))
+                    pm_message("read even row - rowInXelrow=%d inputRow=%d",
+                               rowInXelrow, inputRow);
+#endif
+                pnm_readpnmrow(ifP, evenxelrow, cols, newmaxval, format);
+            } else {
+#ifdef DBG_EDGES
+                if ((row < 10) || (row > (newrows - 10)))
+                    pm_message("read odd row - rowInXelrow=%d inputRow=%d",
+                               rowInXelrow, inputRow);
+#endif
+                pnm_readpnmrow(ifP, oddxelrow, cols, newmaxval, format);
+            }
+            ++rowInXelrow;
+        }
+
+        for (col = 0; col < newcols; ++col) {
+            if (inputRow == 0)
+                newxelrow[col] = evenxelrow[col];
+            else if (inputRow == rows - 1)
+                newxelrow[col] = oddxelrow[col];
+            else if (inputRow % 2 == 0) {
+                /* the even row is the low one, the odd the high one */
+                newxelrow[col].r = fFract * evenxelrow[col].r +
+                    (1.0 - fFract) * oddxelrow[col].r;
+                newxelrow[col].g = fFract * evenxelrow[col].g +
+                    (1.0 - fFract) * oddxelrow[col].g;
+                newxelrow[col].b = fFract * evenxelrow[col].b +
+                    (1.0 - fFract) * oddxelrow[col].b;
+            } else {
+                newxelrow[col].r = fFract * oddxelrow[col].r +
+                    (1.0 - fFract) * evenxelrow[col].r;
+                newxelrow[col].g = fFract * oddxelrow[col].g +
+                    (1.0 - fFract) * evenxelrow[col].g;
+                newxelrow[col].b = fFract * oddxelrow[col].b +
+                    (1.0 - fFract) * evenxelrow[col].b;
+            }
+        }
+
+        pnm_writepnmrow(stdout, newxelrow, newcols, newmaxval, newformat, 0 );
+    }
+
+    pnm_freerow(oddxelrow);
+    pnm_freerow(evenxelrow);
+    pnm_freerow(newxelrow);
+}
+
+
+
+static void
+transformNoneMixing(FILE * const ifP,
+                   int const cols, int const rows,
+                   xelval const maxval, int const format,
+                   int const newcols, int const newrows,
+                   xelval const newmaxval, int const newformat,
+                   bool const inverse,
+                   bool const verbose, bool const vverbose) 
+{
+    /*
+    Transform the map image on input file 'ifP' (which is described by 
+    'cols', 'rows', 'format', and 'maxval') to a Mercator projection and
+    write the result to standard output as format 'newformat' and with 
+    maxval 'newmaxval'.
+
+    Don't mix colors from different input pixels together in the output
+    pixels.  Each output pixel is an exact copy of some corresponding 
+    input pixel.
+    */
+
+    xel* xelrow;          /* an input row */
+    xel* newxelrow;         /* the output row */
+
+    int row;
+    double fRow;
+    int rowInXelrow;
+    int inputRow;
+    int col;
+
+    double fLatRad;
+    double fLatMerc;
+
+    xelrow = pnm_allocrow(cols); 
+    rowInXelrow = 0;
+
+    newxelrow = pnm_allocrow(newcols);
+
+    for (row = 0; row < newrows; ++row) {
+        
+        fRow = (double)row + 0.5; /* center on half the pixel */
+        if (!inverse) {
+            /* the result is mercator, calculate back to degrees */
+            fLatMerc = 2.0 * M_PI * (fRow / (double)newrows - 0.5);
+                /* merc goes from -pi to +pi */
+            fLatRad = 2.0 * (atan(exp(fLatMerc)) - M_PI / 4.0);
+            fRow = ((fLatRad / M_PI) + 0.5) * (double)rows;
+                /* lat goes from -pi/2 to +pi/2 */
+        } else {
+            /* the result is in degrees, calculate back to mercator */
+            fLatRad = M_PI * (fRow / (double)newrows - 0.5);
+                /* lat goes from -pi/2 to +pi/2 */
+            fLatMerc = log(tan((M_PI / 4.0 + fLatRad / 2.0)));
+            fRow = ((fLatMerc / M_PI / 2.0) + 0.5) * (double)rows;
+                /* merc goes from -pi to +pi */
+        }
+        /* fRow -= 0.5; */        /* it's weird that this isn't needed */
+
+        inputRow = (int) floor(fRow);
+        if (inputRow < 0)
+            inputRow = 0;
+        if (inputRow > rows - 1)
+            inputRow = rows - 1;
+
+        if (vverbose) {
+            if (!inverse) {
+#ifdef DBG_EDGES
+                if ((row < 10) || (row > (newrows - 10)))
+#else
+  #ifdef DBG_MIDDLE
+                if ((row > (newrows/2 - 10)) && (row < (newrows/2 + 10)))
+  #else
+                if (row % 20 == 0)
+  #endif
+#endif
+                    pm_message("outputRow=%d latMerc=%f latRad=%f inputRow=%d"
+                               "fRow=%f",
+                               row, fLatMerc, fLatRad, inputRow, fRow);
+            } else {
+#ifdef DBG_EDGES
+                if ((row < 10) || (row > (newrows - 10)))
+#else
+  #ifdef DBG_MIDDLE
+                if ((row > (newrows/2 - 10)) && (row < (newrows/2 + 10)))
+  #else
+                if (row % 10 == 0)
+  #endif
+#endif
+                    pm_message("outputRow=%d latRad=%f latMerc=%f inputRow=%d"
+                               "fRow=%f",
+                               row, fLatRad, fLatMerc, inputRow, fRow);
+            }
+        }
+
+        while ((rowInXelrow <= inputRow) && (rowInXelrow < rows)) {
+#ifdef DBG_EDGES
+            if ((row < 10) || (row > (newrows - 10)))
+                pm_message("read row - rowInXelrow=%d inputRow=%d",
+                           rowInXelrow, inputRow);
+#endif
+            pnm_readpnmrow(ifP, xelrow, cols, newmaxval, format);
+            ++rowInXelrow;
+        }
+        for (col = 0; col < newcols; ++col) {
+            newxelrow[col] = xelrow[col];
+        }
+
+        pnm_writepnmrow(stdout, newxelrow, newcols, newmaxval, newformat, 0 );
+    }
+
+    pnm_freerow(xelrow);
+    pnm_freerow(newxelrow);
+}
+
+
+
+int
+main(int argc, const char ** argv ) 
+{
+    struct cmdlineInfo cmdline;
+    FILE* ifP;
+    int rows, cols, format, newformat, newrows, newcols;
+    xelval maxval, newmaxval;
+    bool verbose;
+
+    pm_proginit( &argc, argv );
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose || cmdline.vverbose;
+
+    ifP = pm_openr(cmdline.input_filespec);
+
+    pnm_readpnminit( ifP, &cols, &rows, &maxval, &format );
+
+    /* Promote PBM file to PGM. */
+    if ( PNM_FORMAT_TYPE(format) == PBM_TYPE ) {
+        newformat = PGM_TYPE;
+        newmaxval = PGM_MAXMAXVAL;
+        pm_message( "promoting from PBM to PGM" );
+    } else {
+        newformat = format;
+        newmaxval = maxval;
+    }
+
+    computeOutputDimensions(cmdline, rows, cols, &newrows, &newcols);
+
+    pnm_writepnminit(stdout, newcols, newrows, newmaxval, newformat, 0);
+
+    if (cmdline.nomix) {
+        if (verbose)
+            pm_message("Transforming map without mixing/blending colors");
+        transformNoneMixing(ifP, cols, rows, maxval, format,
+                            newcols, newrows, newmaxval, newformat, 
+                            cmdline.inverse, verbose, cmdline.vverbose);
+    } else {
+        if (verbose)
+            pm_message("Transforming map while using intermediate colors");
+        transformWithMixing(ifP, cols, rows, maxval, format,
+                            newcols, newrows, newmaxval, newformat, 
+                            cmdline.inverse, verbose, cmdline.vverbose);
+    }
+
+    pm_close(ifP);
+    pm_close(stdout);
+    
+    return 0;
+}
diff --git a/editor/specialty/ppm3d.c b/editor/specialty/ppm3d.c
index d9ada365..a6faa341 100644
--- a/editor/specialty/ppm3d.c
+++ b/editor/specialty/ppm3d.c
@@ -42,7 +42,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -62,7 +62,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
     
     if (argc-1 < 2)
diff --git a/editor/specialty/ppmglobe.c b/editor/specialty/ppmglobe.c
index 82fae5fb..92e22746 100644
--- a/editor/specialty/ppmglobe.c
+++ b/editor/specialty/ppmglobe.c
@@ -42,7 +42,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -62,7 +62,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!backgroundSpec)
diff --git a/editor/specialty/ppmntsc.c b/editor/specialty/ppmntsc.c
index ae3bcfe9..a721b891 100644
--- a/editor/specialty/ppmntsc.c
+++ b/editor/specialty/ppmntsc.c
@@ -51,9 +51,6 @@
 #include "mallocvar.h"
 #include "shhopt.h"
 
-#define TRUE 1
-#define FALSE 0
-
 enum legalize {RAISE_SAT, LOWER_SAT, ALREADY_LEGAL};
    /* The actions that make a legal pixel */
 
@@ -70,6 +67,61 @@ struct cmdlineInfo {
 
 
 
+static void
+parseCommandLine(int argc, const char ** 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
+   sometimes, one of these strings is actually just a suffix of an entry
+   in argv!
+-----------------------------------------------------------------------------*/
+    optStruct3 opt;
+    optEntry *option_def;
+        /* Instructions to OptParseOptions on how to parse our options.
+         */
+    unsigned int option_def_index;
+    unsigned int legalonly, illegalonly, correctedonly;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3('v', "verbose",        OPT_FLAG, NULL,  &cmdlineP->verbose,  0);
+    OPTENT3('V', "debug",          OPT_FLAG, NULL,  &cmdlineP->debug,    0);
+    OPTENT3('p', "pal",            OPT_FLAG, NULL,  &cmdlineP->pal,      0);
+    OPTENT3('l', "legalonly",      OPT_FLAG, NULL,  &legalonly,           0);
+    OPTENT3('i', "illegalonly",    OPT_FLAG, NULL,  &illegalonly,         0);
+    OPTENT3('c', "correctedonly",  OPT_FLAG, NULL,  &correctedonly,       0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = true;
+    opt.allowNegNum = false;
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+
+    if (argc - 1 == 0)
+        cmdlineP->inputFilename = "-";  /* he wants stdin */
+    else if (argc - 1 == 1)
+        cmdlineP->inputFilename = argv[1];
+    else 
+        pm_error("Too many arguments.  The only arguments accepted "
+                 "are the mask color and optional input file specification");
+
+    if (legalonly + illegalonly + correctedonly > 1)
+        pm_error("--legalonly, --illegalonly, and --correctedonly are "
+                 "conflicting options.  Specify at most one of these.");
+        
+    if (legalonly) 
+        cmdlineP->output = LEGAL_ONLY;
+    else if (illegalonly) 
+        cmdlineP->output = ILLEGAL_ONLY;
+    else if (correctedonly) 
+        cmdlineP->output = CORRECTED_ONLY;
+    else 
+        cmdlineP->output = ALL;
+}
+
+
 
 static void 
 rgbtoyiq(const int r, const int g, const int b, 
@@ -118,39 +170,39 @@ yuvtorgb(const double y, const double u, const double v,
 
 
 static void
-make_legal_yiq(const double y, const double i, const double q, 
-               double * const y_new_p, 
-               double * const i_new_p, 
-               double * const q_new_p,
-               enum legalize * const action_p
-    ) {
+makeLegalYiq(double          const y,
+             double          const i,
+             double          const q, 
+             double *        const yNewP, 
+             double *        const iNewP, 
+             double *        const qNewP,
+             enum legalize * const actionP) {
     
-    double sat_old, sat_new;
+    double satOld, satNew;
     /*
      * I and Q are legs of a right triangle.  Saturation is the hypotenuse.
      */
-    sat_old = sqrt(i*i + q*q);
-    if (y+sat_old > 1.0) {
-        const double diff = 0.5*((y+sat_old) - 1.0);
-        *y_new_p = y - diff;
-        sat_new = 1.0 - *y_new_p;
-        *i_new_p = i*(sat_new/sat_old);
-        *q_new_p = q*(sat_new/sat_old);
-        *action_p = LOWER_SAT;
-    } else if (y-sat_old <= -0.251) {
-        const double diff = 0.5*((sat_old-y) - 0.251);
-        *y_new_p = y + diff;
-        sat_new = 0.250 + *y_new_p;
-        *i_new_p = i*(sat_new/sat_old);
-        *q_new_p = q*(sat_new/sat_old);
-        *action_p = RAISE_SAT;
+    satOld = sqrt(SQR(i) + SQR(q));
+    if (y+satOld > 1.0) {
+        const double diff = 0.5*((y + satOld) - 1.0);
+        *yNewP = y - diff;
+        satNew = 1.0 - *yNewP;
+        *iNewP = i * (satNew/satOld);
+        *qNewP = q * (satNew/satOld);
+        *actionP = LOWER_SAT;
+    } else if (y - satOld <= -0.251) {
+        const double diff = 0.5*((satOld - y) - 0.251);
+        *yNewP = y + diff;
+        satNew = 0.250 + *yNewP;
+        *iNewP = i * (satNew/satOld);
+        *qNewP = q * (satNew/satOld);
+        *actionP = RAISE_SAT;
     } else {
-        *y_new_p = y;
-        *i_new_p = i;
-        *q_new_p = q;
-        *action_p = ALREADY_LEGAL;
+        *yNewP = y;
+        *iNewP = i;
+        *qNewP = q;
+        *actionP = ALREADY_LEGAL;
     }
-    return;
 }
 
 
@@ -206,7 +258,7 @@ make_legal_yiq_i(const int r_in, const int g_in, const int b_in,
      * Convert to YIQ and compute the new saturation.
      */
     rgbtoyiq(r_in, g_in, b_in, &y, &i, &q);
-    make_legal_yiq(y, i, q, &y_new, &i_new, &q_new, action_p);
+    makeLegalYiq(y, i, q, &y_new, &i_new, &q_new, action_p);
     if (*action_p != ALREADY_LEGAL)
         /*
          * Given the new I and Q, compute new RGB values.
@@ -295,204 +347,155 @@ make_legal_yuv_b(const pixel input,
 
 
 static void 
-report_mapping(const pixel old_pixel, const pixel new_pixel) {
+reportMapping(pixel const oldPixel,
+              pixel const newPixel) {
 /*----------------------------------------------------------------------------
-  Assuming old_pixel and new_pixel are input and output pixels,
+  Assuming oldPixel and newPixel are input and output pixels,
   tell the user that we changed a pixel to make it legal, if in fact we
   did and it isn't the same change that we just reported.
 -----------------------------------------------------------------------------*/
-    static pixel last_changed_pixel;
-    static int first_time = TRUE;
-
-    if (!PPM_EQUAL(old_pixel, new_pixel) && 
-        (first_time || PPM_EQUAL(old_pixel, last_changed_pixel))) {
-        pm_message("Mapping %d %d %d -> %d %d %d\n",
-                   PPM_GETR(old_pixel),
-                   PPM_GETG(old_pixel),
-                   PPM_GETB(old_pixel),
-                   PPM_GETR(new_pixel),
-                   PPM_GETG(new_pixel),
-                   PPM_GETB(new_pixel)
+    static pixel lastChangedPixel;
+    static bool firstTime = true;
+
+    if (!PPM_EQUAL(oldPixel, newPixel) && 
+        (firstTime || PPM_EQUAL(oldPixel, lastChangedPixel))) {
+        pm_message("Mapping %u %u %u -> %u %u %u\n",
+                   PPM_GETR(oldPixel),
+                   PPM_GETG(oldPixel),
+                   PPM_GETB(oldPixel),
+                   PPM_GETR(newPixel),
+                   PPM_GETG(newPixel),
+                   PPM_GETB(newPixel)
             );
 
-        last_changed_pixel = old_pixel;
-        first_time = FALSE;
+        lastChangedPixel = oldPixel;
+        firstTime = false;
     }    
 }
 
 
 
 static void
-convert_one_image(FILE * const ifp, struct cmdlineInfo const cmdline, 
-                  bool * const eofP, 
-                  int * const hicountP, int * const locountP) {
+convertOneImage(FILE *             const ifP,
+                struct cmdlineInfo const cmdline, 
+                unsigned int *     const hiCountP,
+                unsigned int *     const loCountP) {
 
     /* Parameters of input image: */
     int rows, cols;
     pixval maxval;
     int format;
 
-    ppm_readppminit(ifp, &cols, &rows, &maxval, &format);
-    ppm_writeppminit(stdout, cols, rows, maxval, FALSE);
+    ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
+    ppm_writeppminit(stdout, cols, rows, maxval, 0);
     {
-        pixel* const input_row = ppm_allocrow(cols);
-        pixel* const output_row = ppm_allocrow(cols);
-        pixel last_illegal_pixel;
-        /* Value of the illegal pixel we most recently processed */
+        pixel * const inputRow = ppm_allocrow(cols);
+        pixel * const outputRow = ppm_allocrow(cols);
+
+        pixel lastIllegalPixel;
+            /* Value of the illegal pixel we most recently processed */
         pixel black;
-        /* A constant - black pixel */
+            /* A constant - black pixel */
 
         PPM_ASSIGN(black, 0, 0, 0);
 
-        PPM_ASSIGN(last_illegal_pixel, 0, 0, 0);  /* initial value */
+        PPM_ASSIGN(lastIllegalPixel, 0, 0, 0);  /* initial value */
         {
-            int row;
+            unsigned int row;
 
-            *hicountP = 0; *locountP = 0;  /* initial values */
+            *hiCountP = 0; *loCountP = 0;  /* initial values */
 
             for (row = 0; row < rows; ++row) {
-                int col;
-                ppm_readppmrow(ifp, input_row, cols, maxval, format);
+                unsigned int col;
+                ppm_readppmrow(ifP, inputRow, cols, maxval, format);
                 for (col = 0; col < cols; ++col) {
                     pixel corrected;
-                    /* Corrected or would-be corrected value for pixel */
+                        /* Corrected or would-be corrected value for pixel */
                     enum legalize action;
-                    /* What action was used to make pixel legal */
+                        /* What action was used to make pixel legal */
                     if (cmdline.pal)
-                        make_legal_yuv_b(input_row[col],
+                        make_legal_yuv_b(inputRow[col],
                                          &corrected,
                                          &action);
                     else
-                        make_legal_yiq_b(input_row[col],
+                        make_legal_yiq_b(inputRow[col],
                                          &corrected,
                                          &action);
                         
                     if (action == LOWER_SAT) 
-                        (*hicountP)++;
+                        ++*hiCountP;
                     if (action == RAISE_SAT)
-                        (*locountP)++;
-                    if (cmdline.debug) report_mapping(input_row[col],
-                                                      corrected);
+                        ++*loCountP;
+                    if (cmdline.debug)
+                        reportMapping(inputRow[col], corrected);
                     switch (cmdline.output) {
                     case ALL:
-                        output_row[col] = corrected;
+                        outputRow[col] = corrected;
                         break;
                     case LEGAL_ONLY:
-                        output_row[col] = (action == ALREADY_LEGAL) ?
-                            input_row[col] : black;
+                        outputRow[col] = (action == ALREADY_LEGAL) ?
+                            inputRow[col] : black;
                         break;
                     case ILLEGAL_ONLY:
-                        output_row[col] = (action != ALREADY_LEGAL) ?
-                            input_row[col] : black;
+                        outputRow[col] = (action != ALREADY_LEGAL) ?
+                            inputRow[col] : black;
                         break;
                     case CORRECTED_ONLY:
-                        output_row[col] = (action != ALREADY_LEGAL) ?
+                        outputRow[col] = (action != ALREADY_LEGAL) ?
                             corrected : black;
                         break;
                     }
                 }
-                ppm_writeppmrow(stdout, output_row, cols, maxval, FALSE);
+                ppm_writeppmrow(stdout, outputRow, cols, maxval, 0);
             }
         }
-        ppm_freerow(output_row);
-        ppm_freerow(input_row);
+        ppm_freerow(outputRow);
+        ppm_freerow(inputRow);
     }
 }
 
 
-static void
-parseCommandLine(int argc, char ** 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
-   sometimes, one of these strings is actually just a suffix of an entry
-   in argv!
------------------------------------------------------------------------------*/
-    optStruct3 opt;
-    optEntry *option_def;
-        /* Instructions to OptParseOptions on how to parse our options.
-         */
-    unsigned int option_def_index;
-    unsigned int legalonly, illegalonly, correctedonly;
-
-    MALLOCARRAY(option_def, 100);
-
-    option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3('v', "verbose",        OPT_FLAG, NULL,  &cmdlineP->verbose,  0);
-    OPTENT3('V', "debug",          OPT_FLAG, NULL,  &cmdlineP->debug,    0);
-    OPTENT3('p', "pal",            OPT_FLAG, NULL,  &cmdlineP->pal,      0);
-    OPTENT3('l', "legalonly",      OPT_FLAG, NULL,  &legalonly,           0);
-    OPTENT3('i', "illegalonly",    OPT_FLAG, NULL,  &illegalonly,         0);
-    OPTENT3('c', "correctedonly",  OPT_FLAG, NULL,  &correctedonly,       0);
-
-    opt.opt_table = option_def;
-    opt.short_allowed = TRUE;
-    opt.allowNegNum = FALSE;
-
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
-
-    if (argc - 1 == 0)
-        cmdlineP->inputFilename = "-";  /* he wants stdin */
-    else if (argc - 1 == 1)
-        cmdlineP->inputFilename = argv[1];
-    else 
-        pm_error("Too many arguments.  The only arguments accepted "
-                 "are the mask color and optional input file specification");
-
-    if (legalonly + illegalonly + correctedonly > 1)
-        pm_error("--legalonly, --illegalonly, and --correctedonly are "
-                 "conflicting options.  Specify at most one of these.");
-        
-    if (legalonly) 
-        cmdlineP->output = LEGAL_ONLY;
-    else if (illegalonly) 
-        cmdlineP->output = ILLEGAL_ONLY;
-    else if (correctedonly) 
-        cmdlineP->output = CORRECTED_ONLY;
-    else 
-        cmdlineP->output = ALL;
-}
-
-
 
 int
-main(int argc, char **argv) {
+main(int argc, const char **argv) {
     
     struct cmdlineInfo cmdline;
     FILE * ifP;
-    int total_hicount, total_locount;
-    int image_count;
+    unsigned int totalHiCount, totalLoCount;
+    unsigned int imageCount;
 
-    bool eof;
+    int eof;
 
-    ppm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.inputFilename);
+    
+    imageCount = 0;    /* initial value */
+    totalHiCount = 0;  /* initial value */
+    totalLoCount = 0;  /* initial value */
 
-    image_count = 0;    /* initial value */
-    total_hicount = 0;  /* initial value */
-    total_locount = 0;  /* initial value */
-
-    eof = FALSE;
+    eof = false;
     while (!eof) {
-        int hicount, locount;
-        convert_one_image(ifP, cmdline, &eof, &hicount, &locount);
-        image_count++;
-        total_hicount += hicount;
-        total_locount += locount;
+        unsigned int hiCount, loCount;
+
+        convertOneImage(ifP, cmdline, &hiCount, &loCount);
+
+        ++imageCount;
+        totalHiCount += hiCount;
+        totalLoCount += loCount;
+
         ppm_nextimage(ifP, &eof);
     }
 
 
 	if (cmdline.verbose) {
-        pm_message("%d images processed.", image_count);
-        pm_message("%d pixels were above the saturation limit.", 
-                   total_hicount);
-        pm_message("%d pixels were below the saturation limit.", 
-                   total_locount);
+        pm_message("%u images processed.", imageCount);
+        pm_message("%u pixels were above the saturation limit.", 
+                   totalHiCount);
+        pm_message("%u pixels were below the saturation limit.", 
+                   totalLoCount);
     }
     
     pm_close(ifP);
diff --git a/editor/specialty/ppmrelief.c b/editor/specialty/ppmrelief.c
index 1c408aec..14a6d0a8 100644
--- a/editor/specialty/ppmrelief.c
+++ b/editor/specialty/ppmrelief.c
@@ -1,4 +1,4 @@
-/* ppmrelief.c - generate a relief map of a portable pixmap
+/* ppmrelief.c - generate a relief map of a PPM image
 **
 ** Copyright (C) 1990 by Wilson H. Bent, Jr.
 **
@@ -11,86 +11,104 @@
 */
 
 #include <stdio.h>
+
 #include "pm_c_util.h"
 #include "ppm.h"
 
+
+
+static pixval
+clip(int    const p,
+     pixval const maxval) {
+
+    return MAX(0, MIN(maxval, p));
+}
+
+
+
 int
-main(int argc, char * argv[]) {
-
-    FILE* ifp;
-    pixel** inputbuf;
-    pixel* outputrow;
-    int argn, rows, cols, format, row;
-    register int col;
-    pixval maxval, mv2;
+main(int argc, const char * argv[]) {
+
+    FILE * ifP;
+    pixel ** inputbuf;
+    pixel * outputrow;
+    int argn, format, rows, cols;
+    unsigned int row, col;
+    pixval maxval;
     const char* const usage = "[ppmfile]";
 
-    ppm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     argn = 1;
 
     if ( argn != argc ) {
-        ifp = pm_openr( argv[argn] );
+        ifP = pm_openr( argv[argn] );
         ++argn;
     } else
-        ifp = stdin;
+        ifP = stdin;
 
     if ( argn != argc )
         pm_usage( usage );
-    
-    ppm_readppminit( ifp, &cols, &rows, &maxval, &format );
+
+    ppm_readppminit(ifP, &cols, &rows, &maxval, &format );
 
     if (cols < 3 || rows < 3 )
         pm_error("Input image too small: %u x %u.  Must be at least 3x3",
                   cols, rows);
 
-    mv2 = maxval / 2;
-
     /* Allocate space for 3 input rows, plus an output row. */
-    inputbuf = ppm_allocarray( cols, 3 );
-    outputrow = ppm_allocrow( cols );
+    inputbuf  = ppm_allocarray(cols, 3);
+    outputrow = ppm_allocrow(cols);
 
-    ppm_writeppminit( stdout, cols, rows, maxval, 0 );
+    ppm_writeppminit(stdout, cols, rows, maxval, 0);
 
     /* Read in the first two rows. */
-    ppm_readppmrow( ifp, inputbuf[0], cols, maxval, format );
-    ppm_readppmrow( ifp, inputbuf[1], cols, maxval, format );
+    ppm_readppmrow(ifP, inputbuf[0], cols, maxval, format);
+    ppm_readppmrow(ifP, inputbuf[1], cols, maxval, format);
 
     /* Write out the first row, all zeros. */
-    for ( col = 0; col < cols; ++col )
+    for (col = 0; col < cols; ++col)
         PPM_ASSIGN( outputrow[col], 0, 0, 0 );
-    ppm_writeppmrow( stdout, outputrow, cols, maxval, 0 );
+
+    ppm_writeppmrow(stdout, outputrow, cols, maxval, 0);
 
     /* Now the rest of the image - read in the 3rd row of inputbuf,
-    ** and convolve with the first row into the output buffer.
+       and convolve with the first row into the output buffer.
     */
-    for ( row = 2 ; row < rows; ++row ) {
-        pixval r, g, b;
-        int rowa, rowb;
-
-        rowa = row % 3;
-        rowb = (row + 2) % 3;
-        ppm_readppmrow( ifp, inputbuf[rowa], cols, maxval, format );
-        
-        for ( col = 0; col < cols - 2; ++col ) {
-            r = MAX(0, MIN(maxval, PPM_GETR( inputbuf[rowa][col] ) +
-                           ( mv2 - PPM_GETR( inputbuf[rowb][col + 2] ) )));
-            g = MAX(0, MIN(maxval, PPM_GETG( inputbuf[rowa][col] ) +
-                           ( mv2 - PPM_GETG( inputbuf[rowb][col + 2] ) )));
-            b = MAX(0, MIN(maxval, PPM_GETB( inputbuf[rowa][col] ) +
-                           ( mv2 - PPM_GETB( inputbuf[rowb][col + 2] ) )));
-            PPM_ASSIGN( outputrow[col + 1], r, g, b );
+    for (row = 2 ; row < rows; ++row) {
+        pixval       const mv2 = maxval / 2;
+        unsigned int const rowa = row % 3;
+        unsigned int const rowb = (rowa + 2) % 3;
+
+        ppm_readppmrow(ifP, inputbuf[rowa], cols, maxval, format);
+
+        for (col = 0; col < cols - 2; ++col) {
+            pixel const inputA = inputbuf[rowa][col];
+            pixel const inputB = inputbuf[rowb][col + 2];
+            
+            pixval const r =
+                clip(PPM_GETR(inputA) + (mv2 - PPM_GETR(inputB)), maxval);
+            pixval const g =
+                clip(PPM_GETG(inputA) + (mv2 - PPM_GETG(inputB)), maxval);
+            pixval const b =
+                clip(PPM_GETB(inputA) + (mv2 - PPM_GETB(inputB)), maxval);
+
+            PPM_ASSIGN(outputrow[col + 1], r, g, b);
         }
-        ppm_writeppmrow( stdout, outputrow, cols, maxval, 0 );
+        ppm_writeppmrow(stdout, outputrow, cols, maxval, 0);
     }
 
     /* And write the last row, zeros again. */
-    for ( col = 0; col < cols; ++col )
-        PPM_ASSIGN( outputrow[col], 0, 0, 0 );
-    ppm_writeppmrow( stdout, outputrow, cols, maxval, 0 );
+    for (col = 0; col < cols; ++col)
+        PPM_ASSIGN(outputrow[col], 0, 0, 0);
+
+    ppm_writeppmrow(stdout, outputrow, cols, maxval, 0);
+
+    ppm_freerow(outputrow);
+    ppm_freearray(inputbuf, 3);
 
-    pm_close( ifp );
-    pm_close( stdout );
+    pm_close(ifP);
+    pm_close(stdout);
 
-    exit( 0 );
+    return 0;
 }
diff --git a/generator/Makefile b/generator/Makefile
index 3c30cdd0..d0ea6b60 100644
--- a/generator/Makefile
+++ b/generator/Makefile
@@ -14,9 +14,10 @@ include $(BUILDDIR)/config.mk
 # This package is so big, it's useful even when some parts won't 
 # build.
 
-PORTBINARIES = pamgauss pamgradient pamseq pamstereogram \
+PORTBINARIES = pamcrater pamgauss pamgradient \
+	       pamseq pamshadedrelief pamstereogram \
 	       pbmpage pbmmake pbmtext pbmtextps pbmupc \
-	       pgmcrater pgmkernel pgmmake pgmnoise pgmramp \
+	       pgmkernel pgmmake pgmnoise pgmramp \
 	       ppmcie ppmcolors ppmforge ppmmake ppmpat ppmrough ppmwheel \
 
 # We don't include programs that have special library dependencies in the
@@ -28,7 +29,7 @@ MERGEBINARIES = $(PORTBINARIES)
 
 
 BINARIES = $(MERGEBINARIES) $(NOMERGEBINARIES)
-SCRIPTS = ppmrainbow
+SCRIPTS = pgmcrater ppmrainbow
 
 OBJECTS = $(BINARIES:%=%.o)
 
diff --git a/generator/pamcrater.c b/generator/pamcrater.c
new file mode 100644
index 00000000..8c1fda40
--- /dev/null
+++ b/generator/pamcrater.c
@@ -0,0 +1,514 @@
+/*=============================================================================
+                               pamcrater
+===============================================================================
+  Fractal cratering
+
+  This is derived from John Walker's 'pgmcrater' which not only creates
+  the terrain map as this program does, but then does a relief filter to
+  convert it to a shaded visual image.
+
+  The  algorithm  used  to  determine crater size is as described on
+  pages 31 and 32 of:
+
+  Peitgen, H.-O., and Saupe, D. eds., The Science Of Fractal
+      Images, New York: Springer Verlag, 1988.
+
+  The  mathematical  technique  used  to calculate crater radii that
+  obey the proper area law distribution from a uniformly distributed
+  pseudorandom sequence was developed by Rudy Rucker.
+
+  The original program carried this attribution and license:
+
+       Designed and implemented in November of 1989 by:
+
+        John Walker
+        Autodesk SA
+        Avenue des Champs-Montants 14b
+        CH-2074 MARIN
+        Switzerland
+        Usenet: kelvin@Autodesk.com
+        Fax:    038/33 88 15
+        Voice:  038/33 76 33
+
+  Permission  to  use, copy, modify, and distribute this software and
+  its documentation  for  any  purpose  and  without  fee  is  hereby
+  granted,  without any conditions or restrictions.  This software is
+  provided "as is" without express or implied warranty.
+
+=============================================================================*/
+
+/* Modifications by Arjen Bax, 2001-06-21: Remove black vertical line at
+   right edge. Make craters wrap around the image (enables tiling of image).
+ */
+
+#define _XOPEN_SOURCE   /* get M_PI in math.h */
+
+#include <assert.h>
+#include <math.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "pam.h"
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    unsigned int number;
+    unsigned int height;
+    unsigned int width;
+    unsigned int randomseedSpec;
+    unsigned int randomseed;
+    unsigned int verbose;
+    unsigned int test;
+    unsigned int radius;
+    int          offset;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+    unsigned int option_def_index;
+
+    unsigned int numberSpec, heightSpec, widthSpec, radiusSpec, offsetSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "number",     OPT_UINT,    &cmdlineP->number,
+            &numberSpec,                 0);
+    OPTENT3(0,   "height",     OPT_UINT,    &cmdlineP->height,
+            &heightSpec,                 0);
+    OPTENT3(0,   "width",      OPT_UINT,    &cmdlineP->width,
+            &widthSpec,                  0);
+    OPTENT3(0,   "randomseed", OPT_UINT,    &cmdlineP->randomseed,
+            &cmdlineP->randomseedSpec,   0);
+    OPTENT3(0,   "verbose",    OPT_FLAG,    NULL,
+            &cmdlineP->verbose,          0);
+    OPTENT3(0,   "test",       OPT_FLAG,    NULL,
+            &cmdlineP->test,       0);
+    OPTENT3(0,   "radius",     OPT_UINT,    &cmdlineP->radius,
+            &radiusSpec,           0);
+    OPTENT3(0,   "offset",     OPT_INT,     &cmdlineP->offset,
+            &offsetSpec,           0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 > 0)
+        pm_error("There are no non-option arguments.  You specified %u",
+                 argc-1);
+
+    if (!heightSpec)
+        cmdlineP->height = 256;
+
+    if (cmdlineP->height == 0)
+        pm_error("-height must be positive");
+
+    if (!widthSpec)
+        cmdlineP->width = 256;
+
+    if (cmdlineP->width == 0)
+        pm_error("-width must be positive");
+
+    if (!offsetSpec)
+        cmdlineP->offset=0;
+
+    if (cmdlineP->test) {
+        if (!radiusSpec)
+            pm_error("With -test, you must specify -radius");
+        else {
+            if(MAX(cmdlineP->height, cmdlineP->width) * 2 < cmdlineP->radius)
+                pm_error("Radius (%u) too large", cmdlineP->radius);
+
+            if (numberSpec)
+                pm_error("-number is meaningless with -test");
+
+            if (cmdlineP->randomseedSpec)
+                pm_error("-randomseed is meaningless with -test");
+        }
+    } else {
+        if (radiusSpec)
+            pm_error("-radius is meaningful only with -test");
+
+        if (offsetSpec)
+            pm_error("-offset is meaningful only with -test");
+
+        if (!numberSpec)
+            cmdlineP->number = 50000;
+
+        if (cmdlineP->number == 0)
+            pm_error("-number must be positive");
+    }
+    free(option_def);
+}
+
+
+
+static double const arand       = 32767.0;  /* Random number parameters */
+static double const CdepthPower = 1.5;      /* Crater depth power factor */
+static double const DepthBias2  = 0.5;      /* Square of depth bias */
+
+
+
+static double const
+cast(double const high) {
+/*----------------------------------------------------------------------------
+   A random number in the range [0, 'high'].
+-----------------------------------------------------------------------------*/
+    return high * ((rand() & 0x7FFF) / arand);
+}
+
+
+
+static unsigned int
+mod(int          const t,
+    unsigned int const n) {
+
+    /* This is used to transform coordinates beyond bounds into ones
+       within: craters "wrap around" the edges.  This enables tiling
+       of the image.
+
+       Produces strange effects when crater radius is very large compared
+       to image size.
+    */
+
+    int m;
+
+    m = t % (int)n;
+
+    if (m < 0)
+        m += n;
+
+    return m;
+}
+
+
+
+static sample *
+terrainModP(struct pam * const pamP,
+            tuple **     const terrain,
+            int          const x,
+            int          const y) {
+/*----------------------------------------------------------------------------
+   A pointer to the sample in 'terrain' of an image described by *pamP that is
+   at Column 'x' of Row 'y', but modulus the image size.
+
+   So e.g. if the image is 10 x 10 and 'x' and 'y' are both 12, our value
+   would be a pointer to the sample at Column 2 or Row 2.  If they are both
+   -1, we would point to Column 9, Row 9.
+-----------------------------------------------------------------------------*/
+    return &terrain[mod(y, pamP->height)][mod(x, pamP->width)][0];
+}
+
+
+
+
+static sample
+terrainMod(struct pam * const pamP,
+           tuple **     const terrain,
+           int          const x,
+           int          const y) {
+/*----------------------------------------------------------------------------
+   The value of the sample in 'terrain' of an image described by *pamP that is
+   at Column 'x' of Row 'y', but modulus the image size.
+
+   So e.g. if the image is 10 x 10 and 'x' and 'y' are both 12, our value
+   would be the value of the sample at Column 2 or Row 2.  If they are both
+   -1, we would return Column 9, Row 9.
+-----------------------------------------------------------------------------*/
+    return *terrainModP(pamP, terrain, x, y);
+}
+
+
+
+static void
+setElev(struct pam * const pamP,
+        tuple **     const terrain,
+        int          const cx,
+        int          const cy,
+        unsigned int const elevation) {
+
+    *terrainModP(pamP, terrain, cx, cy) = MIN(pamP->maxval, elevation);
+}
+
+
+
+static void
+smallCrater(struct pam * const pamP,
+            tuple **     const terrain,
+            int          const cx,
+            int          const cy,
+            double       const radius) {
+/*----------------------------------------------------------------------------
+   Generate a crater with a special method for tiny craters.
+
+   Center the crater at Column 'cx', Row 'cy'; wrap as necessary to get them
+   on the canvas.  These might even be negative.
+-----------------------------------------------------------------------------*/
+    int y;
+    unsigned int amptot;
+    unsigned int npatch;
+
+    assert(radius < 3);
+
+    /* Set pixel to the average of its Moore neighborhood. */
+
+    for (y = cy - 1, amptot = 0, npatch = 0; y <= cy + 1; ++y) {
+        int x;
+        for (x = cx - 1; x <= cx + 1; ++x) {
+            amptot += terrainMod(pamP, terrain, x, y);
+            ++npatch;
+        }
+    }
+    {
+        unsigned int const axelev = amptot / npatch;
+            /* The mean elevation of the Moore neighborhood (9 pixels
+               centered on the crater location).
+            */
+        
+        /* Perturb the mean elevation by a small random factor. */
+
+        int const x = radius >= 1 ? ((rand() >> 8) & 0x3) - 1 : 0;
+
+        assert(axelev > 0);
+
+        setElev(pamP, terrain, cx, cy, axelev + x);
+    }
+}
+
+
+
+static unsigned int
+meanElev(struct pam * const pamP,
+         tuple **     const terrain,
+         int          const cx,
+         int          const cy,
+         double       const radius) {
+/*----------------------------------------------------------------------------
+   The mean elevation in 'terrain', which is described by *pamP, within
+   'radius' pixels vertically and horizontally of (cx, cy).
+
+   We assume the area is a fraction the whole 'terrain'.
+-----------------------------------------------------------------------------*/
+    unsigned int amptot;
+    unsigned int npatch;
+    int y;
+
+    for (y = cy - radius, amptot = 0, npatch = 0; y <= cy + radius; ++y) {
+        int x;
+        for (x = cx - radius; x <= cx + radius; ++x) {
+            amptot += terrainMod(pamP, terrain, x, y);
+            ++npatch;
+        }
+    }
+    assert(npatch > 0);
+
+    return amptot / npatch;
+}
+
+
+
+static void
+normalCrater(struct pam * const pamP,
+             tuple **     const terrain,
+             int          const cx,
+             int          const cy,
+             double       const radius) {
+/*----------------------------------------------------------------------------
+   Generate a regular (not tiny) crater.
+
+   Generate an impact feature of the correct size and shape.
+----------------------------------------------------------------------------*/
+    int    const impactRadius = (int) MAX(2, (radius / 3));
+    int    const craterRadius = (int) radius;
+    double const rollmin      = 0.9;
+
+    int y;
+
+    unsigned int const axelev = meanElev(pamP, terrain, cx, cy, impactRadius);
+        /* The mean elevation of the impact area, before impact */
+
+    for (y = cy - craterRadius; y <= cy + craterRadius; ++y) {
+        int const dysq = SQR(cy - y);
+
+        int x;
+
+        for (x = cx - craterRadius; x <= cx + craterRadius; ++x) {
+            int  const dxsq = SQR(cx - x);
+            double const cd = (dxsq + dysq) / (double) SQR(craterRadius);
+            double const cd2 = cd * 2.25;
+            double const tcz = sqrt(DepthBias2) - sqrt(fabs(1 - cd2));
+            double cz;
+            double roll;
+
+            cz = MAX((cd2 > 1) ? 0.0 : -10, tcz);  /* Initial value */
+
+            cz *= pow(craterRadius, CdepthPower);
+            if (dysq == 0 && dxsq == 0 && ((int) cz) == 0) {
+                cz = cz < 0 ? -1 : 1;
+            }
+
+            roll = (((1 / (1 - MIN(rollmin, cd))) /
+                     (1 / (1 - rollmin))) - (1 - rollmin)) / rollmin;
+
+            {
+                unsigned int av;
+                av = (axelev + cz) * (1 - roll) +
+                    (terrainMod(pamP, terrain, x, y) + cz) * roll;
+                av = MAX(1000, MIN(64000, av));
+                
+                setElev(pamP, terrain, x, y, av);
+            }
+        }
+    }
+}
+
+
+
+/* We should also have largeCrater() */
+
+
+
+static void
+plopCrater(struct pam * const pamP,
+           tuple **     const terrain,
+           int          const cx,
+           int          const cy,
+           double       const radius,
+           bool         const verbose) {
+
+    if (verbose && pm_have_float_format())
+        pm_message("Plopping crater at (%4d, %4d) with radius %g",
+                   cx, cy, radius);
+
+    if (radius < 3)
+        smallCrater (pamP, terrain, cx, cy, radius);
+    else
+        normalCrater(pamP, terrain, cx, cy, radius);
+}
+
+
+
+static void
+initCanvas(unsigned int const width,
+           unsigned int const height,
+           struct pam * const pamP,
+           tuple ***    const terrainP) {
+/*----------------------------------------------------------------------------
+   Initialize the output image to a flat area of middle elevation.
+-----------------------------------------------------------------------------*/
+    tuple ** terrain;    /* elevation array */
+    unsigned int row;
+
+    pamP->size   = sizeof(*pamP);
+    pamP->len    = PAM_STRUCT_SIZE(tuple_type);
+    pamP->file   = stdout;
+    pamP->format = PAM_FORMAT;
+    pamP->height = height;
+    pamP->width  = width;
+    pamP->depth  = 1;
+    pamP->maxval = 65535;
+    pamP->bytes_per_sample = 2;
+    STRSCPY(pamP->tuple_type, "elevation");
+
+    terrain = pnm_allocpamarray(pamP);
+
+    for (row = 0; row < pamP->height; ++row) {
+        unsigned int col;
+        for (col = 0; col < pamP->width; ++col)
+            terrain[row][col][0] = pamP->maxval / 2;
+    }
+    *terrainP = terrain;
+}
+
+
+
+static void
+genCraters(struct CmdlineInfo const cmdline) {
+/*----------------------------------------------------------------------------
+   Generate cratered terrain
+-----------------------------------------------------------------------------*/
+    tuple ** terrain;    /* elevation array */
+    struct pam pam;
+
+    /* Allocate the elevation array and initialize it to mean surface
+       elevation.
+    */
+
+    initCanvas(cmdline.width, cmdline.height, &pam, &terrain);
+
+    if (cmdline.test)
+        plopCrater(&pam, terrain,
+                   pam.width/2 + cmdline.offset,
+                   pam.height/2 + cmdline.offset,
+                   (double) cmdline.radius, cmdline.verbose);
+    else {
+        unsigned int const ncraters = cmdline.number; /* num of craters */
+        unsigned int l;
+
+        for (l = 0; l < ncraters; ++l) {
+            int const cx = cast((double) pam.width  - 1);
+            int const cy = cast((double) pam.height - 1);
+
+            /* Thanks, Rudy, for this equation that maps the uniformly
+               distributed numbers from cast() into an area-law distribution
+               as observed on cratered bodies.
+
+               Produces values within the interval:
+               0.56419 <= radius <= 56.419
+            */
+            double const radius = sqrt(1 / (M_PI * (1 - cast(0.9999))));
+
+            plopCrater(&pam, terrain, cx, cy, radius, cmdline.verbose);
+
+            if (((l + 1) % 100000) == 0)
+                pm_message("%u craters generated of %u (%u%% done)",
+                           l + 1, ncraters, ((l + 1) * 100) / ncraters);
+        }
+    }
+
+    pnm_writepam(&pam, terrain);
+
+    pnm_freepamarray(terrain, &pam);
+
+    pm_close(stdout);
+}
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    struct CmdlineInfo cmdline;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
+
+    genCraters(cmdline);
+
+    return 0;
+}
+
+
+
diff --git a/generator/pamgauss.c b/generator/pamgauss.c
index 1fb47af6..2dd6a726 100644
--- a/generator/pamgauss.c
+++ b/generator/pamgauss.c
@@ -9,11 +9,9 @@
 #include "mallocvar.h"
 #include "pam.h"
 
-#define true (1)
-#define false (0)
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -27,8 +25,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
   Convert program invocation arguments (argc,argv) into a format the 
   program can use easily, struct cmdlineInfo.  Validate arguments along
@@ -59,7 +57,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!tupletypeSpec)
@@ -68,7 +66,8 @@ parseCommandLine(int argc, char ** argv,
         struct pam pam;
         if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type))
             pm_error("The tuple type you specified is too long.  "
-                     "Maximum %d characters.", sizeof(pam.tuple_type)-1);
+                     "Maximum %u characters.",
+                     (unsigned)sizeof(pam.tuple_type)-1);
     }        
 
     if (!sigmaSpec)
@@ -102,6 +101,7 @@ parseCommandLine(int argc, char ** argv,
             pm_error("height argument must be a positive number.  You "
                      "specified '%s'", argv[2]);
     }
+    free(option_def);
 }
 
 
@@ -161,15 +161,15 @@ imageNormalizer(struct pam * const pamP,
 
 
 int
-main(int argc, char **argv) {
+main(int argc, const char **argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     struct pam pam;
     int row;
     double normalizer;
     tuplen * tuplerown;
     
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
    
     parseCommandLine(argc, argv, &cmdline);
 
diff --git a/generator/pamgradient.c b/generator/pamgradient.c
index aa559d27..57e78288 100644
--- a/generator/pamgradient.c
+++ b/generator/pamgradient.c
@@ -18,7 +18,7 @@ struct cmdlineInfo {
 };
 
 static void
-parseCommandLine(int argc, char **argv,
+parseCommandLine(int argc, const char **argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
   Convert program invocation arguments (argc,argv) into a format the 
@@ -45,7 +45,7 @@ parseCommandLine(int argc, char **argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!maxvalSpec)
@@ -153,7 +153,7 @@ createEdge(const struct pam * const pamP,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;
     struct pam pam;
@@ -162,7 +162,7 @@ main(int argc, char *argv[]) {
     tuple * rightEdge;
     unsigned int row;
     
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
diff --git a/generator/pamseq.c b/generator/pamseq.c
index 98eac1cc..1af5252a 100644
--- a/generator/pamseq.c
+++ b/generator/pamseq.c
@@ -7,8 +7,6 @@
 #include "pam.h"
 #include "shhopt.h"
 
-#define true (1)
-#define false (0)
 
 
 struct cmdlineInfo {
@@ -50,7 +48,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!tupletypeSpec)
@@ -59,7 +57,8 @@ parseCommandLine(int argc, char ** argv,
         struct pam pam;
         if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type))
             pm_error("The tuple type you specified is too long.  "
-                     "Maximum %d characters.", sizeof(pam.tuple_type)-1);
+                     "Maximum %u characters.", 
+                     (unsigned)sizeof(pam.tuple_type)-1);
     }        
 
     if (argc-1 < 2)
diff --git a/generator/pamshadedrelief.c b/generator/pamshadedrelief.c
new file mode 100644
index 00000000..89996c83
--- /dev/null
+++ b/generator/pamshadedrelief.c
@@ -0,0 +1,250 @@
+/*=============================================================================
+                               pamshaderelief
+===============================================================================
+  Generate a shaded relief image of terrain, given a terrain map - a two
+  dimensional map of elevations.  A shaded relief image is an image of
+  what terrain with the given elevations would look like illuminated by
+  oblique light.
+
+  The input array is a one-channel PAM image.  The sample values are
+  elevations of terrain.
+  
+  This is derived from John Walker's 'pgmcrater' which not only does this
+  shading, but first generates a terrain map of fractal craters on which to
+  run it.
+
+
+  The original program carried this attribution and license:
+
+       Designed and implemented in November of 1989 by:
+
+        John Walker
+        Autodesk SA
+        Avenue des Champs-Montants 14b
+        CH-2074 MARIN
+        Switzerland
+        Usenet: kelvin@Autodesk.com
+        Fax:    038/33 88 15
+        Voice:  038/33 76 33
+
+  Permission  to  use, copy, modify, and distribute this software and
+  its documentation  for  any  purpose  and  without  fee  is  hereby
+  granted,  without any conditions or restrictions.  This software is
+  provided "as is" without express or implied warranty.
+
+=============================================================================*/
+
+/* Modifications by Arjen Bax, 2001-06-21: Remove black vertical line at right
+   edge.
+*/
+
+#define _XOPEN_SOURCE   /* get M_PI in math.h */
+
+#include <assert.h>
+#include <math.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+    float        gamma;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+    unsigned int option_def_index;
+
+    unsigned int gammaSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "gamma",    OPT_FLOAT,   &cmdlineP->gamma,
+            &gammaSpec,       0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!gammaSpec)
+        cmdlineP->gamma = 1.0;
+
+    if (cmdlineP->gamma <= 0.0)
+        pm_error("gamma correction must be greater than 0");
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %u", argc-1);
+    else
+        cmdlineP->inputFileName = argv[1];
+
+    free(option_def);
+}
+
+
+
+/* Definitions for obtaining random numbers. */
+
+/*  Display parameters  */
+
+static double const ImageGamma = 0.5;     /* Inherent gamma of mapped image */
+static int    const slopemin   = -52;
+static int    const slopemax   = 52;
+
+
+
+static void
+generateSlopeGrayMap(sample * const slopeGrayMap,
+                     double   const dgamma) {
+/*----------------------------------------------------------------------------
+   Map each possible slope to the brightness that terrain with that
+   left-to-right slope should have in the shaded relief.
+
+   The brightness is what would result from light incident from the left
+   falling on the terrain.
+-----------------------------------------------------------------------------*/
+    double const gamma = dgamma * ImageGamma;
+
+    int i;
+
+    for (i = slopemin; i <= 0; ++i) {   /* Negative, downhill, dark */
+        slopeGrayMap[i - slopemin] =
+            128 - 127.0 * pow(sin((M_PI / 2) * i / slopemin), gamma);
+    }
+    for (i = 0; i <= slopemax; ++i) {   /* Positive, uphill, bright */
+        slopeGrayMap[i - slopemin] =
+            128 + 127.0 * pow(sin((M_PI / 2) * i / slopemax), gamma);
+    }
+
+    /* Confused?   OK,  we're using the  left-to-right slope to
+       calculate a shade based on the sine of  the  angle  with
+       respect  to the vertical (light incident from the left).
+       Then, with one exponentiation, we account for  both  the
+       inherent   gamma   of   the   image  (ad-hoc),  and  the
+       user-specified display gamma, using the identity:
+       (x^y)^z = (x^(y*z))
+    */
+}
+
+
+
+static gray
+brightnessOfSlope(int      const slope,
+                  sample * const slopeGrayMap) {
+
+    return slopeGrayMap[MIN(MAX(slopemin, slope), slopemax) - slopemin];
+}
+
+
+
+static void
+writeShadedRelief(struct pam * const terrainPamP,
+                  tuple **     const terrain,
+                  double       const dgamma,
+                  FILE *       const ofP) {
+
+    unsigned int row;
+    tuple * outrow;
+    sample * slopeGrayMap; /* Slope to gray value map */
+    struct pam outpam;
+
+    outpam.size   = sizeof(outpam);
+    outpam.len    = PAM_STRUCT_SIZE(tuple_type);
+    outpam.file   = ofP;
+    outpam.format = PAM_FORMAT;
+    outpam.height = terrainPamP->height;
+    outpam.width  = terrainPamP->width;
+    outpam.depth  = 1;
+    outpam.maxval = 255;
+    outpam.bytes_per_sample = 1;
+    STRSCPY(outpam.tuple_type, "GRAYSCALE");
+
+    outrow = pnm_allocpamrow(&outpam);
+
+    pnm_writepaminit(&outpam);
+
+    MALLOCARRAY(slopeGrayMap, slopemax - slopemin + 1);
+
+    generateSlopeGrayMap(slopeGrayMap, dgamma);
+
+    for (row = 0; row < terrainPamP->height; ++row) {
+        unsigned int col;
+
+        for (col = 0; col < terrainPamP->width - 1; ++col) {
+            int const slope = terrain[row][col+1][0] - terrain[row][col][0];
+            outrow[col][0] = brightnessOfSlope(slope, slopeGrayMap);
+        }
+        {
+            /* Wrap around to determine shade of pixel on right edge */
+            int const slope = 
+                terrain[row][0][0] - terrain[row][outpam.width-1][0];
+            outrow[outpam.width - 1][0] =
+                brightnessOfSlope(slope, slopeGrayMap);
+        }
+        pnm_writepamrow(&outpam, outrow);
+    }
+
+    free(slopeGrayMap);
+    pnm_freepamrow(outrow);
+}
+
+
+
+static void
+readTerrain(FILE *       const ifP,
+            struct pam * const pamP,
+            tuple ***    const tuplesP) {
+
+    *tuplesP = pnm_readpam(ifP, pamP, PAM_STRUCT_SIZE(tuple_type));
+}
+            
+            
+
+int
+main(int argc, const char ** argv) {
+
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+    struct pam terrainPam;
+    tuple ** terrain;
+        /* Array of elevations */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    readTerrain(ifP, &terrainPam, &terrain);
+
+    writeShadedRelief(&terrainPam, terrain, cmdline.gamma, stdout);
+    
+    return 0;
+}
+
+
diff --git a/generator/pamstereogram.c b/generator/pamstereogram.c
index 0ce63853..6e5f5ce0 100644
--- a/generator/pamstereogram.c
+++ b/generator/pamstereogram.c
@@ -8,12 +8,15 @@
  * The core of this program is a simple adaptation of the code in
  * "Displaying 3D Images: Algorithms for Single Image Random Dot
  * Stereograms" by Harold W. Thimbleby, Stuart Inglis, and Ian
- * H. Witten in IEEE Computer, 27(10):38-48, October 1994.  See that
- * paper for a thorough explanation of what's going on here.
+ * H. Witten in IEEE Computer, 27(10):38-48, October 1994 plus some
+ * enhancements presented in "Stereograms: Technical Details" by
+ * W. A. Steer at http://www.techmind.org/stereo/stech.html.  See
+ * those references for a thorough explanation of what's going on
+ * here.
  *
  * ----------------------------------------------------------------------
  *
- * Copyright (C) 2006 Scott Pakin <scott+pbm@pakin.org>
+ * Copyright (C) 2006-2015 Scott Pakin <scott+pbm@pakin.org>
  *
  * All rights reserved.
  *
@@ -43,20 +46,21 @@
  * ----------------------------------------------------------------------
  */
 
+#define _ISOC99_SOURCE  /* Make sure strtof() is in <stdlib.h> */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#include <limits.h>
 #include <assert.h>
 
 #include "pm_config.h"
 #include "pm_c_util.h"
-#include "pam.h"
-#include "shhopt.h"
 #include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
 
-/* Define a few helper macros. */
-#define round2int(X) ((int)((X)+0.5))      /* Nonnegative numbers only */
 
 enum outputType {OUTPUT_BW, OUTPUT_GRAYSCALE, OUTPUT_COLOR};
 
@@ -71,25 +75,65 @@ struct cmdlineInfo {
     unsigned int crosseyed;      /* -crosseyed option */
     unsigned int makemask;       /* -makemask option */
     unsigned int dpi;            /* -dpi option */
-    float eyesep;                /* -eyesep option */
-    float depth;                 /* -depth option */
+    float        eyesep;         /* -eyesep option */
+    float        depth;          /* -depth option */
     unsigned int maxvalSpec;     /* -maxval option count */
-    unsigned int maxval;         /* -maxval option value x*/
-    int guidesize;               /* -guidesize option */
+    unsigned int maxval;         /* -maxval option value */
+    unsigned int guidetop;       /* -guidetop option count */
+    unsigned int guidebottom;    /* -guidebottom option count */
+    unsigned int guidesize;      /* -guidesize option value */
     unsigned int magnifypat;     /* -magnifypat option */
-    unsigned int xshift;         /* -xshift option */
-    unsigned int yshift;         /* -yshift option */
-    const char * patFilespec;    /* -patfile option.  Null if none */
+    int xshift;                  /* -xshift option */
+    int yshift;                  /* -yshift option */
+    const char * patfile;        /* -patfile option.  Null if none */
+    const char * texfile;        /* -texfile option.  Null if none */
+    const char * bgcolor;        /* -bgcolor option */
+    unsigned int smoothing;      /* -smoothing option */
     unsigned int randomseed;     /* -randomseed option */
+    unsigned int randomseedSpec; /* -randomseed option count */
     enum outputType outputType;  /* Type of output file */
+    unsigned int xbegin;         /* -xbegin option */
+    unsigned int xbeginSpec;     /* -xbegin option count */
 };
 
 
 
 static void
-parseCommandLine(int                 argc,
-                 char **             argv,
-                 struct cmdlineInfo *cmdlineP ) {
+parseNearFarPlanes(const char ** const nearFarPlanes,
+                   float *       const nearPlaneP,
+                   float *       const farPlaneP) {
+/*----------------------------------------------------------------------------
+  Parse nearFarPlanes option value into exactly two positive numbers
+-----------------------------------------------------------------------------*/
+    float nearPlane, farPlane;
+
+    if (nearFarPlanes == NULL || nearFarPlanes[0] == NULL ||
+        nearFarPlanes[1] == NULL || nearFarPlanes[2] != NULL)
+        pm_error("-planes requires exactly two positive numbers");
+
+    errno = 0;
+    nearPlane = strtof(nearFarPlanes[0], NULL);
+    if (errno != 0 || nearPlane <= 0.0)
+        pm_error("-planes requires exactly two positive numbers");
+
+    farPlane = strtof(nearFarPlanes[1], NULL);
+    if (errno != 0 || farPlane <= 0.0)
+        pm_error("-planes requires exactly two positive numbers");
+
+    if (nearPlane >= farPlane)
+        pm_error("-planes requires the near-plane value "
+                 "to be less than the far-plane value");
+
+    *nearPlaneP = nearPlane;
+    *farPlaneP  = farPlane;
+}
+
+
+
+static void
+parseCommandLine(int                  argc,
+                 const char **        argv,
+                 struct cmdlineInfo * cmdlineP ) {
 /*----------------------------------------------------------------------------
    Parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.
@@ -101,17 +145,18 @@ parseCommandLine(int                 argc,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
     unsigned int option_def_index;
 
-    unsigned int patfileSpec, dpiSpec, eyesepSpec, depthSpec,
-        guidesizeSpec, magnifypatSpec, xshiftSpec, yshiftSpec, randomseedSpec;
+    unsigned int patfileSpec, texfileSpec, dpiSpec, eyesepSpec, depthSpec,
+        guidesizeSpec, magnifypatSpec, xshiftSpec, yshiftSpec,
+      bgcolorSpec, smoothingSpec, planesSpec;
 
     unsigned int blackandwhite, grayscale, color;
-
+    const char ** planes;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
@@ -136,27 +181,40 @@ parseCommandLine(int                 argc,
             &depthSpec,               0);
     OPTENT3(0, "maxval",          OPT_UINT,   &cmdlineP->maxval,
             &cmdlineP->maxvalSpec,    0);
-    OPTENT3(0, "guidesize",       OPT_INT,    &cmdlineP->guidesize,
+    OPTENT3(0, "guidetop",        OPT_FLAG,   NULL,
+            &cmdlineP->guidetop,      0);
+    OPTENT3(0, "guidebottom",     OPT_FLAG,   NULL,
+            &cmdlineP->guidebottom,   0);
+    OPTENT3(0, "guidesize",       OPT_UINT,   &cmdlineP->guidesize,
             &guidesizeSpec,           0);
     OPTENT3(0, "magnifypat",      OPT_UINT,   &cmdlineP->magnifypat,
             &magnifypatSpec,          0);
-    OPTENT3(0, "xshift",          OPT_UINT,   &cmdlineP->xshift,
+    OPTENT3(0, "xshift",          OPT_INT,    &cmdlineP->xshift,
             &xshiftSpec,              0);
-    OPTENT3(0, "yshift",          OPT_UINT,   &cmdlineP->yshift,
+    OPTENT3(0, "yshift",          OPT_INT,    &cmdlineP->yshift,
             &yshiftSpec,              0);
-    OPTENT3(0, "patfile",         OPT_STRING, &cmdlineP->patFilespec,
+    OPTENT3(0, "patfile",         OPT_STRING, &cmdlineP->patfile,
             &patfileSpec,             0);
+    OPTENT3(0, "texfile",         OPT_STRING, &cmdlineP->texfile,
+            &texfileSpec,             0);
+    OPTENT3(0, "bgcolor",         OPT_STRING, &cmdlineP->bgcolor,
+            &bgcolorSpec,             0);
     OPTENT3(0, "randomseed",      OPT_UINT,   &cmdlineP->randomseed,
-            &randomseedSpec,          0);
+            &cmdlineP->randomseedSpec, 0);
+    OPTENT3(0, "smoothing",       OPT_UINT,   &cmdlineP->smoothing,
+            &smoothingSpec,           0);
+    OPTENT3(0, "planes",          OPT_STRINGLIST, &planes,
+            &planesSpec,              0);
+    OPTENT3(0, "xbegin",          OPT_UINT,   &cmdlineP->xbegin,
+            &cmdlineP->xbeginSpec,    0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-
     if (blackandwhite + grayscale + color == 0)
         cmdlineP->outputType = OUTPUT_BW;
     else if (blackandwhite + grayscale + color > 1)
@@ -173,15 +231,21 @@ parseCommandLine(int                 argc,
         }
     }
     if (!patfileSpec)
-        cmdlineP->patFilespec = NULL;
+        cmdlineP->patfile = NULL;
+    if (!texfileSpec)
+        cmdlineP->texfile = NULL;
+    if (!bgcolorSpec)
+        cmdlineP->bgcolor = NULL;
+    if (!smoothingSpec)
+        cmdlineP->smoothing = 0;
 
     if (!dpiSpec)
-        cmdlineP->dpi = 96;
+        cmdlineP->dpi = 100;
     else if (cmdlineP->dpi < 1)
         pm_error("The argument to -dpi must be a positive integer");
 
     if (!eyesepSpec)
-        cmdlineP->eyesep = 2.5;
+        cmdlineP->eyesep = 2.56;
     else if (cmdlineP->eyesep <= 0.0)
         pm_error("The argument to -eyesep must be a positive number");
 
@@ -197,9 +261,16 @@ parseCommandLine(int                 argc,
             pm_error("-maxval must be at most %u.  You specified %u",
                      PNM_OVERALLMAXVAL, cmdlineP->maxval);
     }
+    if (bgcolorSpec && !texfileSpec)
+        pm_message("warning: -bgcolor has no effect "
+                   "except in conjunction with -texfile");
+
+    if (guidesizeSpec && !(cmdlineP->guidetop || cmdlineP->guidebottom))
+        pm_error("-guidesize has no meaning "
+                 "without -guidetop or -guidebottom");
 
     if (!guidesizeSpec)
-        cmdlineP->guidesize = 0;
+        cmdlineP->guidesize = 20;
 
     if (!magnifypatSpec)
         cmdlineP->magnifypat = 1;
@@ -212,26 +283,42 @@ parseCommandLine(int                 argc,
     if (!yshiftSpec)
         cmdlineP->yshift = 0;
 
-    if (!randomseedSpec)
-        cmdlineP->randomseed = time(NULL);
-
-    if (xshiftSpec && !cmdlineP->patFilespec)
+    if (xshiftSpec && !cmdlineP->patfile)
         pm_error("-xshift is valid only with -patfile");
-    if (yshiftSpec && !cmdlineP->patFilespec)
+    if (yshiftSpec && !cmdlineP->patfile)
         pm_error("-yshift is valid only with -patfile");
 
-    if (cmdlineP->makemask && cmdlineP->patFilespec)
+    if (cmdlineP->makemask && cmdlineP->patfile)
         pm_error("You may not specify both -makemask and -patfile");
 
-    if (cmdlineP->patFilespec && blackandwhite)
+    if (cmdlineP->patfile && blackandwhite)
         pm_error("-blackandwhite is not valid with -patfile");
-    if (cmdlineP->patFilespec && grayscale)
+    if (cmdlineP->patfile && grayscale)
         pm_error("-grayscale is not valid with -patfile");
-    if (cmdlineP->patFilespec && color)
+    if (cmdlineP->patfile && color)
         pm_error("-color is not valid with -patfile");
-    if (cmdlineP->patFilespec && cmdlineP->maxvalSpec)
+    if (cmdlineP->patfile && cmdlineP->maxvalSpec)
         pm_error("-maxval is not valid with -patfile");
 
+    if (cmdlineP->texfile && blackandwhite)
+        pm_error("-blackandwhite is not valid with -texfile");
+    if (cmdlineP->texfile && grayscale)
+        pm_error("-grayscale is not valid with -texfile");
+    if (cmdlineP->texfile && color)
+        pm_error("-color is not valid with -texfile");
+    if (cmdlineP->texfile && cmdlineP->maxvalSpec)
+        pm_error("-maxval is not valid with -texfile");
+    if (planesSpec && eyesepSpec)
+        pm_error("-planes is not valid with -eyesep");
+    if (planesSpec && depthSpec)
+        pm_error("-planes is not valid with -depth");
+
+    if (planesSpec) {
+        float nearPlane, farPlane;
+        parseNearFarPlanes(planes, &nearPlane, &farPlane);
+        cmdlineP->eyesep = 2.0*farPlane/cmdlineP->dpi;
+        cmdlineP->depth = 2.0*(farPlane-nearPlane) / (2.0*farPlane-nearPlane);
+    }
 
     if (argc-1 < 1)
         cmdlineP->inputFilespec = "-";
@@ -244,7 +331,7 @@ parseCommandLine(int                 argc,
 
 
 
-static int
+static unsigned int
 separation(double             const dist,
            double             const eyesep,
            unsigned int       const dpi,
@@ -254,9 +341,9 @@ separation(double             const dist,
   Return a separation in pixels which corresponds to a 3-D distance
   between the viewer's eyes and a point on an object.
 -----------------------------------------------------------------------------*/
-    int const pixelEyesep = round2int(eyesep * dpi);
+    unsigned int const pixelEyesep = ROUNDU(eyesep * dpi);
 
-    return round2int((1.0 - dof * dist) * pixelEyesep / (2.0 - dof * dist));
+    return ROUNDU((1.0 - dof * dist) * pixelEyesep / (2.0 - dof * dist));
 }
 
 
@@ -282,13 +369,27 @@ typedef tuple coord2Color(struct outGenerator *, int, int);
     /* A type to use for functions that map a 2-D coordinate to a color. */
 typedef void outGenStateTerm(struct outGenerator *);
 
+typedef struct {
+    struct pam   pam;
+    tuple **     imageData;
+    tuple        bgColor;
+    bool         replaceBgColor;
+        /* replace background color with pattern color */
+    unsigned int smoothing;
+        /* Number of background-smoothing iterations to perform */
+} texState;
 
 typedef struct outGenerator {
-    struct pam pam;
-    coord2Color * getTuple;
+    struct pam        pam;
+    coord2Color *     getTuple;
         /* Map from a height-map (x,y) coordinate to a tuple */
     outGenStateTerm * terminateState;
-    void * stateP;
+    void *            stateP;
+    texState *        textureP;
+        /* Mapped-texture part of the state of operation.  Null
+           means we are doing an ordinary stereogram instead of a
+           mapped-texture one.
+        */
 } outGenerator;
 
 
@@ -401,8 +502,8 @@ struct patternPixelState {
     /* This is the state of a patternPixel generator.*/
     struct pam   patPam;     /* Descriptor of pattern image */
     tuple **     patTuples;  /* Entire image read from the pattern file */
-    unsigned int xshift;
-    unsigned int yshift;
+    int xshift;
+    int yshift;
     unsigned int magnifypat;
 };
 
@@ -465,9 +566,9 @@ initPatternPixel(outGenerator *     const outGenP,
 
     MALLOCVAR_NOFAIL(stateP);
 
-    assert(cmdline.patFilespec);
-    
-    patternFileP = pm_openr(cmdline.patFilespec);
+    assert(cmdline.patfile);
+
+    patternFileP = pm_openr(cmdline.patfile);
 
     stateP->patTuples =
         pnm_readpam(patternFileP,
@@ -495,22 +596,92 @@ initPatternPixel(outGenerator *     const outGenP,
 
 
 static void
+readTextureImage(struct cmdlineInfo const cmdline,
+                 const struct pam * const inpamP,
+                 const struct pam * const outpamP,
+                 texState **        const texturePP) {
+
+    FILE *       textureFileP;
+    texState *   textureP;
+    struct pam * texPamP;
+
+    MALLOCVAR_NOFAIL(textureP);
+    texPamP = &textureP->pam;
+
+    textureFileP = pm_openr(cmdline.texfile);
+    textureP->imageData =
+        pnm_readpam(textureFileP, texPamP, PAM_STRUCT_SIZE(tuple_type));
+    pm_close(textureFileP);
+
+    if (cmdline.bgcolor)
+        textureP->bgColor =
+            pnm_parsecolor(cmdline.bgcolor, texPamP->maxval);
+    else
+        textureP->bgColor =
+            pnm_backgroundtuple(texPamP, textureP->imageData);
+    textureP->replaceBgColor = (cmdline.patfile != NULL);
+    textureP->smoothing = cmdline.smoothing;
+
+    if (cmdline.verbose) {
+        const char * const colorname =
+            pnm_colorname(texPamP, textureP->bgColor, 1);
+
+        reportImageParameters("Texture file", texPamP);
+        if (cmdline.bgcolor && strcmp(colorname, cmdline.bgcolor))
+            pm_message("Texture background color: %s (%s)",
+                       cmdline.bgcolor, colorname);
+        else
+            pm_message("Texture background color: %s", colorname);
+        pm_strfree(colorname);
+    }
+
+    if (texPamP->width != inpamP->width || texPamP->height != inpamP->height)
+        pm_error("The texture image must have the same width and height "
+                 "as the input image");
+    if (cmdline.patfile &&
+        (!streq(texPamP->tuple_type, outpamP->tuple_type) ||
+         texPamP->maxval != outpamP->maxval))
+        pm_error("The texture image must be of the same tuple type "
+                 "and maxval as the pattern image");
+
+    textureP->pam.file = outpamP->file;
+
+    *texturePP = textureP;
+}
+
+
+
+static unsigned int
+totalGuideHeight(struct cmdlineInfo const cmdline) {
+
+    /* Each pair of guides is cmdline.guidesize high, and we add that much
+       white above and below as well, so the total vertical space is three
+       times cmdline.giudesize.
+    */
+
+    return
+        (cmdline.guidetop ? 3 * cmdline.guidesize : 0) +
+        (cmdline.guidebottom ? 3 * cmdline.guidesize : 0);
+}
+
+
+
+static void
 createoutputGenerator(struct cmdlineInfo const cmdline,
                       const struct pam * const inPamP,
                       outGenerator **    const outputGeneratorPP) {
 
     outGenerator * outGenP;
-    
+
     MALLOCVAR_NOFAIL(outGenP);
 
     outGenP->pam.size   = sizeof(struct pam);
     outGenP->pam.len    = PAM_STRUCT_SIZE(tuple_type);
     outGenP->pam.file   = stdout;
-    outGenP->pam.height = inPamP->height + 3 * abs(cmdline.guidesize);
-        /* Allow room for guides. */
+    outGenP->pam.height = inPamP->height + totalGuideHeight(cmdline);
     outGenP->pam.width  = inPamP->width;
 
-    if (cmdline.patFilespec) {
+    if (cmdline.patfile) {
         /* Background pixels should come from the pattern file. */
 
         initPatternPixel(outGenP, cmdline);
@@ -522,6 +693,12 @@ createoutputGenerator(struct cmdlineInfo const cmdline,
 
     outGenP->pam.bytes_per_sample = pnm_bytespersample(outGenP->pam.maxval);
 
+    if (cmdline.texfile) {
+        readTextureImage(cmdline, inPamP, &outGenP->pam, &outGenP->textureP);
+        outGenP->pam = outGenP->textureP->pam;
+    } else
+        outGenP->textureP = NULL;
+
     *outputGeneratorPP = outGenP;
 }
 
@@ -544,7 +721,7 @@ static void
 makeWhiteRow(const struct pam * const pamP,
              tuple *            const tuplerow) {
 
-    int col;
+    unsigned int col;
 
     for (col = 0; col < pamP->width; ++col) {
         unsigned int plane;
@@ -561,144 +738,205 @@ writeRowCopies(const struct pam *  const outPamP,
                unsigned int        const copyCount) {
 
     unsigned int i;
+
     for (i = 0; i < copyCount; ++i)
         pnm_writepamrow(outPamP, outrow);
 }
 
 
 
-/* Draw a pair of guide boxes. */
 static void
-drawguides(int                const guidesize,
+writeWhiteRows(const struct pam * const outPamP,
+               unsigned int       const count) {
+
+    tuple * outrow;             /* One row of output data */
+
+    outrow = pnm_allocpamrow(outPamP);
+
+    makeWhiteRow(outPamP, outrow);
+
+    writeRowCopies(outPamP, outrow, count);
+
+    pnm_freerow(outrow);
+}
+
+
+
+static void
+drawguides(unsigned int       const guidesize,
            const struct pam * const outPamP,
            double             const eyesep,
            unsigned int       const dpi,
            double             const depthOfField) {
-
-    int const far = separation(0, eyesep, dpi, depthOfField);
+/*----------------------------------------------------------------------------
+   Draw a pair of guide boxes, left and right.
+-----------------------------------------------------------------------------*/
+    unsigned int const far = separation(0, eyesep, dpi, depthOfField);
         /* Space between the two guide boxes. */
-    int const width = outPamP->width;    /* Width of the output image */
+    unsigned int const width = outPamP->width;  /* Width of the output image */
 
-    tuple *outrow;             /* One row of output data */
+    tuple * outrow;             /* One row of output data */
     tuple blackTuple;
-    int col;
+    unsigned int col;
 
     pnm_createBlackTuple(outPamP, &blackTuple);
 
     outrow = pnm_allocpamrow(outPamP);
 
-    /* Leave some blank rows before the guides. */
+    /* Put some white rows before the guides */
+    writeWhiteRows(outPamP, guidesize);
+
+    /* Initialize the row buffer to white */
     makeWhiteRow(outPamP, outrow);
-    writeRowCopies(outPamP, outrow, guidesize);
 
-    /* Draw the guides. */
-    if ((width - far + guidesize)/2 < 0 ||
-        (width + far - guidesize)/2 >= width)
+    if (far > width + guidesize)
         pm_message("warning: the guide boxes are completely out of bounds "
-                   "at %d DPI", dpi);
-    else if ((width - far - guidesize)/2 < 0 ||
-             (width + far + guidesize)/2 >= width)
-        pm_message("warning: the guide boxes are partially out of bounds "
-                   "at %d DPI", dpi);
-
-    for (col = (width - far - guidesize)/2;
-         col < (width - far + guidesize)/2;
-         ++col)
-        if (col >= 0 && col < width)
-            pnm_assigntuple(outPamP, outrow[col], blackTuple);
+                   "at %u DPI", dpi);
+    else {
+        unsigned int leftBeg, leftEnd, rightBeg, rightEnd;
+
+        assert(far <= width + guidesize);
+        leftEnd  = (width - far + guidesize)/2;
+        assert(guidesize <= width + far);
+        rightBeg = (width + far - guidesize)/2;
+
+        if (far + guidesize > width) {
+            pm_message("warning: the guide boxes are partially out of bounds "
+                       "at %u DPI", dpi);
+
+            leftBeg  = 0;
+            rightEnd = width;
+        } else {
+            assert(far + guidesize <= width);
+            leftBeg  = (width - far - guidesize)/2;
+            rightEnd = (width + far + guidesize)/2;
+        }
 
-    for (col = (width + far - guidesize)/2;
-         col < (width + far + guidesize)/2;
-         ++col)
-        if (col >= 0 && col < width)
+        /* Draw the left guide black in the buffer */
+        assert(leftEnd < outPamP->width);
+        for (col = leftBeg; col < leftEnd; ++col)
             pnm_assigntuple(outPamP, outrow[col], blackTuple);
 
-    writeRowCopies(outPamP,outrow, guidesize);
+        /* Draw the right guide black in the buffer */
+        assert(rightEnd <= outPamP->width);
+        for (col = rightBeg; col < rightEnd; ++col)
+            pnm_assigntuple(outPamP, outrow[col], blackTuple);
+    }
+    /* Write out the guide rows */
 
-    /* Leave some blank rows after the guides. */
-    makeWhiteRow(outPamP, outrow);
     writeRowCopies(outPamP, outrow, guidesize);
 
+    /* Put some white rows after the guides */
+    writeWhiteRows(outPamP, guidesize);
+
     pnm_freerow(outrow);
 }
 
 
 
-/* Do the bulk of the work.  See the paper cited above for code
- * comments.  All I (Scott) did was transcribe the code and make
- * minimal changes for Netpbm.  And some style changes by Bryan to
- * match Netpbm style.
- */
 static void
 makeStereoRow(const struct pam * const inPamP,
               tuple *            const inRow,
-              int *              const same,
+              unsigned int *     const sameL,
+              unsigned int *     const sameR,
               double             const depthOfField,
               double             const eyesep,
-              unsigned int       const dpi) {
+              unsigned int       const dpi,
+              unsigned int       const optWidth,
+              unsigned int       const smoothing) {
 
-#define Z(X) (1.0-inRow[X][0]/(double)inPamP->maxval)
+/* Given a row of the depth map, compute the sameL and sameR arrays,
+ * which indicate for each pixel which pixel to its left and right it
+ * should be colored the same as.
+ */
+#define Z(X) (inRow[X][0]/(double)inPamP->maxval)
 
-    int const width       = inPamP->width;
-    int const pixelEyesep = round2int(eyesep * dpi);
-        /* Separation in pixels between the viewer's eyes */
+    unsigned int col;
 
-    int col;
+    for (col = 0; col < inPamP->width; ++col) {
+        sameL[col] = col;
+        sameR[col] = col;
+    }
 
-    for (col = 0; col < width; ++col)
-        same[col] = col;
-
-    for (col = 0; col < width; ++col) {
-        int const s = separation(Z(col), eyesep, dpi, depthOfField);
-        int left, right;
-
-        left  = col - s/2;  /* initial value */
-        right = left + s;   /* initial value */
-
-        if (0 <= left && right < width) {
-            int visible;
-            int t;
-            double zt;
-
-            t = 1;  /* initial value */
-
-            do {
-                double const dof = depthOfField;
-                zt = Z(col) + 2.0*(2.0 - dof*Z(col))*t/(dof*pixelEyesep);
-                visible = Z(col-t) < zt && Z(col+t) < zt;
-                ++t;
-            } while (visible && zt < 1);
-            if (visible) {
-                int l;
-
-                l = same[left];
-                while (l != left && l != right) {
-                    if (l < right) {
-                        left = l;
-                        l = same[left];
-                    } else {
-                        same[left] = right;
-                        left = right;
-                        l = same[left];
-                        right = l;
-                    }
-                }
-                same[left] = right;
+    for (col = 0; col < inPamP->width; ++col) {
+        unsigned int const sep = separation(Z(col), eyesep, dpi, depthOfField);
+        int const left = col - sep/2;
+        int const right = left + sep;
+
+        if (left >= 0 && right < inPamP->width) {
+            bool isVisible;
+        
+            if (sameL[right] != right) {
+                /* Right point already linked */
+                if (sameL[right] < left) {
+                    /* Deeper than current */
+                    sameR[sameL[right]] = sameL[right];  /* Break old links. */
+                    sameL[right] = right;
+                    isVisible = TRUE;
+                } else
+                    isVisible = FALSE;
+            } else
+                isVisible = TRUE;
+
+            if (sameR[left] != left) {
+                /* Left point already linked */
+                if (sameR[left] > right) {
+                    /* Deeper than current */
+                    sameL[sameR[left]] = sameR[left];  /* Break old links. */
+                    sameR[left] = left;
+                    isVisible = TRUE;
+                } else
+                    isVisible = FALSE;
+            } else
+                isVisible = TRUE;
+
+            if (isVisible) {
+                /* Make a link. */
+                sameL[right] = left;
+                sameR[left] = right;
             }
         }
     }
+
+    /* If smoothing is enabled, replace each non-duplicate pixel with
+       the pixel adjacent to its right neighbor.
+    */
+    if (smoothing > 0) {
+        int const baseCol = inPamP->width - optWidth - 1;
+
+        int col;
+
+        for (col = inPamP->width - 1; col >= 0; --col)
+            sameR[col] = sameR[sameR[col]];
+        for (col = baseCol; col >= 0; --col) {
+            if (sameR[col] == col)
+                sameR[col] = sameR[col+1] - 1;
+        }
+    }
 }
 
 
 
 static void
-makeMaskRow(const struct pam * const outPamP,
-            const int *        const same,
-            const tuple *      const outRow) {
+makeMaskRow(const struct pam *   const outPamP,
+            unsigned int         const xbegin,
+            const unsigned int * const sameL,
+            const unsigned int * const sameR,
+            const tuple *        const outRow) {
     int col;
 
-    for (col = outPamP->width-1; col >= 0; --col) {
-        bool const duplicate = (same[col] != col);
+    for (col = (int)xbegin; col < outPamP->width; ++col) {
+        bool const duplicate = (sameL[col] != col && sameL[col] >= xbegin);
+
+        unsigned int plane;
+
+        for (plane = 0; plane < outPamP->depth; ++plane)
+            outRow[col][plane] = duplicate ? outPamP->maxval : 0;
+    }
+
+    for (col = (int)xbegin - 1; col >= 0; --col) {
+        bool const duplicate = (sameR[col] != col);
+
         unsigned int plane;
 
         for (plane = 0; plane < outPamP->depth; ++plane)
@@ -709,37 +947,284 @@ makeMaskRow(const struct pam * const outPamP,
 
 
 static void
-makeImageRow(outGenerator * const outGenP,
-             int            const row,
-             const int *    const same,
-             const tuple *  const outRow) {
+computeFixedPoint(const unsigned int * const same,
+                  unsigned int *       const sameFp,
+                  unsigned int         const width) {
 /*----------------------------------------------------------------------------
-  same[N] is one of two things:
+  Compute the fixed point of same[] (i.e., sameFp[x] is
+  same[same[same[...[same[x]]...]]]).
+-----------------------------------------------------------------------------*/
+    int col;
 
-  same[N] == N means to generate a value for Column N independent of
+    for (col = width-1; col >= 0; --col) {
+        if (same[col] != col)
+            sameFp[col] = sameFp[same[col]];
+        else {
+            if (col < width-1)
+                sameFp[col] = sameFp[col + 1] - 1;
+            else
+                sameFp[col] = col;
+        }
+    }
+}
+
+
+
+static void
+averageFromPattern(struct pam *         const pamP,
+                   tuple                const bgColor,
+                   const tuple *        const textureRow,
+                   const unsigned int * const same,
+                   unsigned int *       const sameFp,
+                   const tuple *        const outRow,
+                   unsigned int *       const tuplesInCol) {
+/*----------------------------------------------------------------------------
+  Average the color of each non-background pattern tuple to every column that
+  should have the same color.
+-----------------------------------------------------------------------------*/
+    int col;
+
+    /* Initialize the tuple sums to zero. */
+
+    for (col = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane)
+            outRow[col][plane] = 0;
+        tuplesInCol[col] = 0;
+    }
+
+    /* Accumulate the color of each non-background pattern tuple to
+       every column that should have the same color.
+    */
+    for (col = pamP->width-1; col >= 0; --col) {
+        tuple const onetuple = textureRow[(col+same[col])/2];
+        unsigned int const targetcol = sameFp[col];
+        int eqcol;
+
+        if (!pnm_tupleequal(pamP, onetuple, bgColor)) {
+            for (eqcol = pamP->width-1; eqcol >= 0; --eqcol) {
+                if (sameFp[eqcol] == targetcol) {
+                    unsigned int plane;
+                    for (plane = 0; plane < pamP->depth; ++plane)
+                        outRow[eqcol][plane] += onetuple[plane];
+                    tuplesInCol[eqcol]++;
+                }
+            }
+        }
+    }
+    /* Take the average of all colors associated with each column.
+       Tuples that can be any color are assigned the same color as was
+       previously assigned to their fixed-point column.
+    */
+    for (col = 0; col < pamP->width; ++col) {
+        if (tuplesInCol[col] > 0) {
+            unsigned int plane;
+            for (plane = 0; plane < pamP->depth; ++plane)
+                outRow[col][plane] /= tuplesInCol[col];
+        } else
+            pnm_assigntuple(pamP, outRow[col], bgColor);
+    }
+}
+
+
+
+static void
+smoothOutSpeckles(struct pam *   const pamP,
+                  tuple          const bgColor,
+                  unsigned int   const smoothing,
+                  unsigned int * const tuplesInCol,
+                  tuple *        const rowBuffer,
+                  const tuple *  const outRow) {
+/*----------------------------------------------------------------------------
+  Smooth out small speckles of the background color lying between other
+  colors.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+
+    for (i = 0; i < smoothing; ++i) {
+        int col;
+        tuple * const scratchrow = rowBuffer;
+        for (col = pamP->width-2; col >= 1; --col) {
+            if (tuplesInCol[col] == 0) {
+                /* Replace a background tuple with the average of its
+                   left and right neighbors.
+                */
+                unsigned int plane;
+                for (plane = 0; plane < pamP->depth; ++plane)
+                    scratchrow[col][plane] = 0;
+                if (!pnm_tupleequal(pamP, outRow[col-1], bgColor)) {
+                    for (plane = 0; plane < pamP->depth; ++plane)
+                        scratchrow[col][plane] += outRow[col-1][plane];
+                    ++tuplesInCol[col];
+                }
+                if (!pnm_tupleequal(pamP, outRow[col+1], bgColor)) {
+                    for (plane = 0; plane < pamP->depth; ++plane)
+                        scratchrow[col][plane] += outRow[col+1][plane];
+                    ++tuplesInCol[col];
+                }
+                if (tuplesInCol[col] > 0)
+                    for (plane = 0; plane < pamP->depth; ++plane)
+                        scratchrow[col][plane] /= tuplesInCol[col];
+                else
+                    pnm_assigntuple(pamP, scratchrow[col], outRow[col]);
+            } else
+                pnm_assigntuple(pamP, scratchrow[col], outRow[col]);
+        }
+        for (col = 1; col < pamP->width-1; ++col)
+            pnm_assigntuple(pamP, outRow[col], scratchrow[col]);
+    }
+}
+
+
+
+static void
+replaceRemainingBackgroundWithPattern(outGenerator *       const outGenP,
+                                      const unsigned int * const same,
+                                      unsigned int         const row,
+                                      const tuple *        const outRow) {
+
+    const struct pam * const pamP = &outGenP->pam;
+    tuple const bgColor = outGenP->textureP->bgColor;
+
+    if (outGenP->textureP->replaceBgColor) {
+        int col;
+        for (col = outGenP->pam.width-1; col >= 0; --col) {
+            if (pnm_tupleequal(pamP, outRow[col], bgColor)) {
+                bool const duplicate = (same[col] != col);
+
+                tuple newtuple;
+
+                if (duplicate) {
+                    assert(same[col] > col);
+                    assert(same[col] < outGenP->pam.width);
+
+                    newtuple = outRow[same[col]];
+                } else
+                    newtuple = outGenP->getTuple(outGenP, col, row);
+
+                pnm_assigntuple(pamP, outRow[col], newtuple);
+            }
+        }
+    }
+}
+
+
+
+static void
+makeImageRowMts(outGenerator *       const outGenP,
+                unsigned int         const row,
+                const unsigned int * const same,
+                unsigned int *       const sameFp,
+                tuple *              const rowBuffer,
+                const tuple *        const outRow) {
+/*----------------------------------------------------------------------------
+  Make a row of a mapped-texture stereogram.
+-----------------------------------------------------------------------------*/
+    unsigned int * tuplesInCol;
+        /* tuplesInCol[C] is the number of tuples averaged together to make
+           Column C.
+        */
+    MALLOCARRAY(tuplesInCol, outGenP->pam.width);
+    if (tuplesInCol == NULL)
+        pm_error("Unable to allocate space for \"tuplesInCol\" array.");
+
+    assert(outGenP->textureP);
+    /* This is an original algorithm by Scott Pakin. */
+
+    /*
+      Compute the fixed point of same[] (i.e., sameFp[x] is
+      same[same[same[...[same[x]]...]]]).
+    */
+    computeFixedPoint(same, sameFp, outGenP->pam.width);
+
+    /* Average the color of each non-background pattern tuple to
+       every column that should have the same color.
+    */
+
+    averageFromPattern(&outGenP->pam, outGenP->textureP->bgColor,
+                       outGenP->textureP->imageData[row],
+                       same, sameFp,
+                       outRow, tuplesInCol);
+
+    /* Smooth out small speckles of the background color lying between
+       other colors.
+    */
+    smoothOutSpeckles(&outGenP->pam, outGenP->textureP->bgColor,
+                      outGenP->textureP->smoothing,
+                      tuplesInCol, rowBuffer, outRow);
+
+    /* Replace any remaining background tuples with a pattern tuple. */
+
+    replaceRemainingBackgroundWithPattern(outGenP, same, row, outRow);
+
+    free(tuplesInCol);
+}
+
+
+
+static void
+makeImageRow(outGenerator *       const outGenP,
+             unsigned int         const row,
+             unsigned int         const optWidth,
+             unsigned int         const xbegin,
+             const unsigned int * const sameL,
+             const unsigned int * const sameR,
+             const tuple *        const outRow) {
+/*----------------------------------------------------------------------------
+  sameR[N] is one of two things:
+
+  sameR[N] == N means to generate a value for Column N independent of
   other columns in the row.
 
-  same[N] > N means Column N should be identical to Column same[N].
-  
-  same[N] < N is not allowed.
+  sameR[N] > N means Column N should be identical to Column sameR[N].
+
+  sameR[N] < N is not allowed.
+
+  sameL[N] is one of two things:
+
+  sameL[N] == N means to generate a value for Column N independent of
+  other columns in the row.
+
+  sameL[N] < N means Column N should be identical to Column sameL[N].
+
+  sameL[N] > N is not allowed.
 -----------------------------------------------------------------------------*/
     int col;
-    for (col = outGenP->pam.width-1; col >= 0; --col) {
-        bool const duplicate = (same[col] != col);
-        
+    int lastLinked;
+
+    for (col = (int)xbegin, lastLinked = INT_MIN;
+         col < outGenP->pam.width;
+         ++col) {
+
         tuple newtuple;
-        unsigned int plane;
 
-        if (duplicate) {
-            assert(same[col] > col);
-            assert(same[col] < outGenP->pam.width);
+        if (sameL[col] == col || sameL[col] < (int)xbegin) {
+            if (lastLinked == col - 1)
+                newtuple = outRow[col - 1];
+            else
+                newtuple = outGenP->getTuple(outGenP, col, row);
+        } else {
+          newtuple = outRow[sameL[col]];
+          lastLinked = col;
+              /* Keep track of the last pixel to be constrained. */
+        }
+        pnm_assigntuple(&outGenP->pam, outRow[col], newtuple);
+    }
 
-            newtuple = outRow[same[col]];
-        } else 
-            newtuple = outGenP->getTuple(outGenP, col, row);
+    for (col = (int)xbegin - 1, lastLinked = INT_MIN; col >= 0; --col) {
+        tuple newtuple;
 
-        for (plane = 0; plane < outGenP->pam.depth; ++plane)
-            outRow[col][plane] = newtuple[plane];
+        if (sameR[col] == col) {
+            if (lastLinked == col + 1)
+                newtuple = outRow[col + 1];
+            else
+                newtuple = outGenP->getTuple(outGenP, col, row);
+        } else {
+            newtuple = outRow[sameR[col]];
+            lastLinked = col;
+                /* Keep track of the last pixel to be constrained. */
+        }
+        pnm_assigntuple(&outGenP->pam, outRow[col], newtuple);
     }
 }
 
@@ -764,26 +1249,40 @@ makeImageRows(const struct pam * const inPamP,
               double             const eyesep,
               unsigned int       const dpi,
               bool               const crossEyed,
-              bool               const makeMask) {
+              bool               const makeMask,
+              unsigned int       const magnifypat,
+              unsigned int       const smoothing,
+              unsigned int       const xbegin) {
 
     tuple * inRow;     /* One row of pixels read from the height-map file */
     tuple * outRow;    /* One row of pixels to write to the height-map file */
-    int * same;
-        /* Array: same[N] is the column number of a pixel to the right forced
-           to have the same color as the one in column N
+    unsigned int * sameR;
+        /* Malloced array: sameR[N] is the column number of a pixel to the
+           right forced to have the same color as the one in column N
+        */
+    unsigned int * sameL;
+        /* Malloced array: sameL[N] is the column number of a pixel to the
+           left forced to have the same color as the one in column N
         */
-    int row;           /* Current row in the input and output files */
+    unsigned int * sameRfp;
+        /* Malloced array: Fixed point of sameR[] */
+    tuple * rowBuffer;     /* Scratch row needed for texture manipulation */
+    unsigned int row;      /* Current row in the input and output files */
 
     inRow = pnm_allocpamrow(inPamP);
     outRow = pnm_allocpamrow(&outputGeneratorP->pam);
-    MALLOCARRAY(same, inPamP->width);
-    if (same == NULL)
-        pm_error("Unable to allocate space for \"same\" array.");
-
-    /* See the paper cited above for code comments.  All I (Scott) did was
-     * transcribe the code and make minimal changes for Netpbm.  And some
-     * style changes by Bryan to match Netpbm style.
-     */
+    MALLOCARRAY(sameR, inPamP->width);
+    if (sameR == NULL)
+        pm_error("Unable to allocate space for \"sameR\" array.");
+    MALLOCARRAY(sameL, inPamP->width);
+    if (sameL == NULL)
+        pm_error("Unable to allocate space for \"sameL\" array.");
+
+    MALLOCARRAY(sameRfp, inPamP->width);
+    if (sameRfp == NULL)
+        pm_error("Unable to allocate space for \"sameRfp\" array.");
+    rowBuffer = pnm_allocpamrow(&outputGeneratorP->pam);
+
     for (row = 0; row < inPamP->height; ++row) {
         pnm_readpamrow(inPamP, inRow);
         if (crossEyed)
@@ -793,17 +1292,29 @@ makeImageRows(const struct pam * const inPamP,
             invertHeightRow(inPamP, inRow);
 
         /* Determine color constraints. */
-        makeStereoRow(inPamP, inRow, same, depthOfField, eyesep, dpi);
+        makeStereoRow(inPamP, inRow, sameL, sameR, depthOfField, eyesep, dpi,
+                      ROUNDU(eyesep * dpi)/(magnifypat * 2),
+                      smoothing);
 
         if (makeMask)
-            makeMaskRow(&outputGeneratorP->pam, same, outRow);
-        else
-            makeImageRow(outputGeneratorP, row, same, outRow);
-
+            makeMaskRow(&outputGeneratorP->pam, xbegin, sameL, sameR, outRow);
+        else {
+            if (outputGeneratorP->textureP)
+                makeImageRowMts(outputGeneratorP, row, sameR, sameRfp,
+                                rowBuffer, outRow);
+            else
+                makeImageRow(outputGeneratorP, row,
+                             ROUNDU(eyesep * dpi)/(magnifypat * 2),
+                             xbegin, sameL, sameR, outRow);
+        }
         /* Write the resulting row. */
         pnm_writepamrow(&outputGeneratorP->pam, outRow);
     }
-    free(same);
+
+    pnm_freepamrow(rowBuffer);
+    free(sameRfp);
+    free(sameL);
+    free(sameR);
     pnm_freepamrow(outRow);
     pnm_freepamrow(inRow);
 }
@@ -817,7 +1328,9 @@ produceStereogram(FILE *             const ifP,
     struct pam inPam;    /* PAM information for the height-map file */
     outGenerator * outputGeneratorP;
         /* Handle of an object that generates background pixels */
-    
+    unsigned int xbegin;
+        /* x coordinate separating left-to-right from right-to-left coloring */
+
     pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(tuple_type));
 
     createoutputGenerator(cmdline, &inPam, &outputGeneratorP);
@@ -832,21 +1345,34 @@ produceStereogram(FILE *             const ifP,
 
     pnm_writepaminit(&outputGeneratorP->pam);
 
-    /* Draw guide boxes at the top, if desired. */
-    if (cmdline.guidesize < 0)
-        drawguides(-cmdline.guidesize, &outputGeneratorP->pam,
+    if (cmdline.xbeginSpec == 0)
+        xbegin = outputGeneratorP->pam.width/2;
+    else {
+        xbegin = cmdline.xbegin;
+        if (xbegin >= outputGeneratorP->pam.width)
+            pm_error("-xbegin must be less than the image width (%d)",
+                     outputGeneratorP->pam.width);
+    }
+
+    if (cmdline.guidetop)
+        drawguides(cmdline.guidesize, &outputGeneratorP->pam,
                    cmdline.eyesep,
                    cmdline.dpi, cmdline.depth);
 
     makeImageRows(&inPam, outputGeneratorP,
                   cmdline.depth, cmdline.eyesep, cmdline.dpi,
-                  cmdline.crosseyed, cmdline.makemask);
+                  cmdline.crosseyed, cmdline.makemask, cmdline.magnifypat,
+                  cmdline.smoothing, xbegin);
 
-    /* Draw guide boxes at the bottom, if desired. */
-    if (cmdline.guidesize > 0)
+    if (cmdline.guidebottom)
         drawguides(cmdline.guidesize, &outputGeneratorP->pam,
                    cmdline.eyesep, cmdline.dpi, cmdline.depth);
 
+    if (cmdline.texfile) {
+        pnm_freepamarray(outputGeneratorP->textureP->imageData,
+                         &outputGeneratorP->textureP->pam);
+        free(outputGeneratorP->textureP);
+    }
     destroyoutputGenerator(outputGeneratorP);
 }
 
@@ -855,7 +1381,12 @@ produceStereogram(FILE *             const ifP,
 static void
 reportParameters(struct cmdlineInfo const cmdline) {
 
-    unsigned int const pixelEyesep = round2int(cmdline.eyesep * cmdline.dpi);
+    unsigned int const pixelEyesep =
+        ROUNDU(cmdline.eyesep * cmdline.dpi);
+    unsigned int const sep0 =
+        separation(0, cmdline.eyesep, cmdline.dpi, cmdline.depth);
+    unsigned int const sep1 =
+        separation(1, cmdline.eyesep, cmdline.dpi, cmdline.depth);
 
     pm_message("Eye separation: %.4g inch * %d DPI = %u pixels",
                cmdline.eyesep, cmdline.dpi, pixelEyesep);
@@ -863,40 +1394,42 @@ reportParameters(struct cmdlineInfo const cmdline) {
     if (cmdline.magnifypat > 1)
         pm_message("Background magnification: %uX * %uX",
                    cmdline.magnifypat, cmdline.magnifypat);
-    pm_message("\"Optimal\" pattern width: %u / (%u * 2) = %u pixels",
+    pm_message("\"Optimal\" (far) pattern width: %u / (%u * 2) = %u pixels",
                pixelEyesep, cmdline.magnifypat,
                pixelEyesep/(cmdline.magnifypat * 2));
-    pm_message("Unique 3-D depth levels possible: %u",
-               separation(0, cmdline.eyesep, cmdline.dpi, cmdline.depth) -
-               separation(1, cmdline.eyesep, cmdline.dpi, cmdline.depth) + 1);
-    if (cmdline.patFilespec && (cmdline.xshift || cmdline.yshift))
-        pm_message("Pattern shift: (%u, %u)", cmdline.xshift, cmdline.yshift);
+    pm_message("Near pattern width: %u / %u = %u pixels",
+               sep1, cmdline.magnifypat, sep1 / cmdline.magnifypat);
+    pm_message("Unique 3-D depth levels possible: %u", sep0 - sep1 + 1);
+    if (cmdline.patfile && (cmdline.xshift || cmdline.yshift))
+        pm_message("Pattern shift: (%d, %d)", cmdline.xshift, cmdline.yshift);
 }
 
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;      /* Parsed command line */
     FILE * ifP;
-    
-    /* Parse the command line. */
-    pnm_init(&argc, argv);
+
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
-    
+
     if (cmdline.verbose)
         reportParameters(cmdline);
-    
-    srand(cmdline.randomseed);
+
+    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
 
     ifP = pm_openr(cmdline.inputFilespec);
-        
+
     /* Produce a stereogram. */
     produceStereogram(ifP, cmdline);
 
     pm_close(ifP);
-    
+
     return 0;
 }
+
+
+
diff --git a/generator/pbmmake.c b/generator/pbmmake.c
index 41d80274..600440f0 100644
--- a/generator/pbmmake.c
+++ b/generator/pbmmake.c
@@ -37,7 +37,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -56,7 +56,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (blackOpt + whiteOpt + grayOpt > 1)
@@ -101,13 +101,11 @@ writeGrayRaster(unsigned int const cols,
         bitrow1[i] = (PBM_WHITE*0x55) | (PBM_BLACK*0xaa);
         /* 0xaa = 10101010 ; 0x55 = 01010101 */
     }
-    if (cols % 8 > 0) { 
-        bitrow0[lastCol] >>= 8 - cols % 8;
-        bitrow0[lastCol] <<= 8 - cols % 8;
-        bitrow1[lastCol] >>= 8 - cols % 8;
-        bitrow1[lastCol] <<= 8 - cols % 8;
-    }
-    if (rows > 1) {
+
+    pbm_cleanrowend_packed(bitrow0, cols);
+    pbm_cleanrowend_packed(bitrow1, cols);
+
+  if (rows > 1) {
         unsigned int row;
         for (row = 1; row < rows; row += 2) {
             pbm_writepbmrow_packed(ofP, bitrow0, cols, 0);
@@ -139,11 +137,10 @@ writeSingleColorRaster(unsigned int const cols,
     for (i = 0; i <= lastCol; ++i) 
         bitrow0[i] = color*0xff;
 
-    if (cols % 8 > 0) { 
-        bitrow0[lastCol] >>= 8 - cols % 8;
-        bitrow0[lastCol] <<= 8 - cols % 8;
-        /* row end trimming, really not necessary with white */
-    }
+    if (color != 0)
+        pbm_cleanrowend_packed(bitrow0, cols);
+    /* row end trimming, not necessary with white */
+
     {
         unsigned int row;
         for (row = 0; row < rows; ++row)
diff --git a/generator/pbmmake.test b/generator/pbmmake.test
deleted file mode 100644
index 0fd99ccd..00000000
--- a/generator/pbmmake.test
+++ /dev/null
@@ -1,9 +0,0 @@
-echo Test 1.  Should print 3892756435 12
-./pbmmake -white 16 2 | cksum
-echo Test 2.  Should print 1576602925 8
-./pbmmake -black 1 1  | cksum
-echo Test 3.  Should print 4272952448 14
-./pbmmake -gray 7 7   | cksum
-echo Test 4.  Should print 1634688086 15
-./pbmmake -grey 8 8   | cksum
-echo Tests done.
diff --git a/generator/pbmpage.c b/generator/pbmpage.c
index fcf7af42..a2f47bcc 100644
--- a/generator/pbmpage.c
+++ b/generator/pbmpage.c
@@ -17,6 +17,7 @@
 #include <math.h>
 #include <stdio.h>
 
+#include "pm_c_util.h"
 #include "pbm.h"
 
 /* US is 8.5 in by 11 in */
@@ -31,10 +32,9 @@
 
 
 struct bitmap {
-    int Width;      /* width and height in 600ths of an inch */
-    int Height;
-    int Pwidth;     /* width in bytes */
-    char *bitmap;
+    unsigned int Width;      /* width and height in 600ths of an inch */
+    unsigned int Height;
+    bit ** bitmap;
 };
 
 static struct bitmap bitmap;
@@ -42,11 +42,10 @@ static struct bitmap bitmap;
 
 
 static void
-setpixel(int const x,
-         int const y,
-         int const c) {
+setpixel(unsigned int const x,
+         unsigned int const y,
+         unsigned int const c) {
 
-    int const charidx = y * bitmap.Pwidth + x/8;
     char const bitmask = 128 >> (x % 8);
 
     if (x < 0 || x >= bitmap.Width)
@@ -55,151 +54,148 @@ setpixel(int const x,
         return;
 
     if (c)
-        bitmap.bitmap[charidx] |= bitmask;
+        bitmap.bitmap[y][x/8] |= bitmask;
     else
-        bitmap.bitmap[charidx] &= ~bitmask;
+        bitmap.bitmap[y][x/8] &= ~bitmask;
 }
 
 
 
 static void 
-setplus(int x,int y,int s)
+setplus(unsigned int const x,
+        unsigned int const y,
+        unsigned int const s) {
 /*----------------------------------------------------------------------------
    Draw a black plus sign centered at (x,y) with arms 's' pixels long.  
    Leave the exact center of the plus white.
 -----------------------------------------------------------------------------*/
-{
-  int i;
-
-  for(i=0; i<s; i++)
-  {
-    setpixel(x+i,y,1);
-    setpixel(x-i,y,1);
-    setpixel(x,y+i,1);
-    setpixel(x,y-i,1);
-  }
+    unsigned int i;
+
+    for (i = 0; i < s; ++i) {
+        setpixel(x+i, y,   1);
+        setpixel(x-i, y,   1);
+        setpixel(x,   y+i, 1);
+        setpixel(x,   y-i, 1);
+    }
 }
 
 
 
 static void 
-setblock(int x,int y,int s)
-{
-  int i,j;
+setblock(unsigned int const x,
+         unsigned int const y,
+         unsigned int const s) {
+
+    unsigned int i;
+
+    for (i = 0; i < s; ++i) {
+        unsigned int j;
 
-  for(i=0; i<s; i++)
-    for(j=0; j<s; j++)
-      setpixel(x+i,y+j,1);
+        for (j = 0; j < s; ++j)
+            setpixel(x+i, y+j, 1);
+    }
 }
 
 
 
 static void 
-setchar(int x,int y,char c)
-{
-  int xo,yo;
-  static char charmap[10][5]= { { 0x3e, 0x41, 0x41, 0x41, 0x3e },
-				{ 0x00, 0x42, 0x7f, 0x40, 0x00 },
-				{ 0x42, 0x61, 0x51, 0x49, 0x46 },
-				{ 0x22, 0x41, 0x49, 0x49, 0x36 },
-				{ 0x18, 0x14, 0x12, 0x7f, 0x10 },
-				{ 0x27, 0x45, 0x45, 0x45, 0x39 },
-				{ 0x3e, 0x49, 0x49, 0x49, 0x32 },
-				{ 0x01, 0x01, 0x61, 0x19, 0x07 },
-				{ 0x36, 0x49, 0x49, 0x49, 0x36 },
-				{ 0x26, 0x49, 0x49, 0x49, 0x3e } };
-
-  if(c<='9' && c>='0')
-    for(xo=0; xo<5; xo++)
-      for(yo=0; yo<8; yo++)
-	if((charmap[c-'0'][xo]>>yo)&1)
-	  setblock(x+xo*3,y+yo*3,3);
+setchar(unsigned int const x,
+        unsigned int const y,
+        char         const c) {
+
+    static char const charmap[10][5]= { { 0x3e, 0x41, 0x41, 0x41, 0x3e },
+                                        { 0x00, 0x42, 0x7f, 0x40, 0x00 },
+                                        { 0x42, 0x61, 0x51, 0x49, 0x46 },
+                                        { 0x22, 0x41, 0x49, 0x49, 0x36 },
+                                        { 0x18, 0x14, 0x12, 0x7f, 0x10 },
+                                        { 0x27, 0x45, 0x45, 0x45, 0x39 },
+                                        { 0x3e, 0x49, 0x49, 0x49, 0x32 },
+                                        { 0x01, 0x01, 0x61, 0x19, 0x07 },
+                                        { 0x36, 0x49, 0x49, 0x49, 0x36 },
+                                        { 0x26, 0x49, 0x49, 0x49, 0x3e } };
+    
+    if (c <= '9' && c >= '0') {
+        unsigned int xo;
+
+        for (xo = 0; xo < 5; ++xo) {
+            unsigned int yo;
+
+            for (yo = 0; yo < 8; ++yo) {
+                if ((charmap[c-'0'][xo] >> yo) & 0x01)
+                    setblock(x + xo*3, y + yo*3, 3);
+            }
+        }
+    }
 }
 
 
 
 static void 
-setstring(int x,int y,char* s)
-{
-  char* p;
-  int xo;
+setstring(unsigned int const x,
+          unsigned int const y,
+          const char * const s) {
 
-  for(xo=0, p=s; *p; xo+=21, p++)
-    setchar(x+xo,y,*p);
+    const char * p;
+    unsigned int xo;
+
+    for (xo = 0, p = s; *p; xo += 21, ++p)
+        setchar(x + xo, y, *p);
 }
 
 
 
 static void 
-setCG(int x,int y)
-{
-  int xo,yo,zo;
-
-  for(xo=0; xo<=50; xo++)
-  {
-    yo=sqrt(50.0*50.0-xo*xo);
-    setpixel(x+xo,y+yo,1);
-    setpixel(x+yo,y+xo,1);
-    setpixel(x-1-xo,y-1-yo,1);
-    setpixel(x-1-yo,y-1-xo,1);
-    setpixel(x+xo,y-1-yo,1);
-    setpixel(x-1-xo,y+yo,1);
-    for(zo=0; zo<yo; zo++)
-    {
-      setpixel(x+xo,y-1-zo,1);
-      setpixel(x-1-xo,y+zo,1);
+setCG(unsigned int const x,
+      unsigned int const y) {
+
+    unsigned int xo;
+
+    for (xo = 0; xo <= 50; ++xo)   {
+        unsigned int const yo = sqrt(SQR(50.0) - SQR(xo));
+
+        unsigned int zo;
+
+        setpixel(x + xo, y + yo, 1);
+        setpixel(x+yo,   y + xo, 1);
+        setpixel(x-1-xo, y-1-yo, 1);
+        setpixel(x-1-yo, y-1-xo, 1);
+        setpixel(x+xo,   y-1-yo, 1);
+        setpixel(x-1-xo, y+yo,   1);
+
+        for(zo = 0; zo < yo; ++zo) {
+            setpixel(x + xo, y-1-zo, 1);
+            setpixel(x-1-xo, y+zo,   1);
+        }
     }
-  }
 }
 
 
 
 static void
-outputPbm(FILE *        const file,
+outputPbm(FILE *        const ofP,
           struct bitmap const bitmap) {
 /*----------------------------------------------------------------------------
   Create a pbm file containing the image from the global variable bitmap[].
 -----------------------------------------------------------------------------*/
     int const forceplain = 0;
-    bit *pbmrow;
-    int row;
-    int bitmap_cursor;
+
+    unsigned int row;
     
-    pbm_writepbminit(file, bitmap.Width, bitmap.Height, forceplain);
-  
-    /* We round the allocated row space up to a multiple of 8 so the ugly
-       fast code below can work.
-       */
-    pbmrow = pbm_allocrow(((bitmap.Width+7)/8)*8);
+    pbm_writepbminit(ofP, bitmap.Width, bitmap.Height, forceplain);
     
-    bitmap_cursor = 0;
-    for (row = 0; row < bitmap.Height; row++) {
-        int col;
-        for (col = 0; col < bitmap.Width;) {
-            /* A little ugliness makes a big speed difference here. */
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<7);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<6);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<5);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<4);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<3);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<2);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<1);
-            pbmrow[col++] = bitmap.bitmap[bitmap_cursor] & (1<<0);
-                
-            bitmap_cursor++;
-        }
-        pbm_writepbmrow(file, pbmrow, bitmap.Width, forceplain); 
+    for (row = 0; row < bitmap.Height; ++row) {
+        pbm_writepbmrow_packed(ofP, bitmap.bitmap[row],
+                               bitmap.Width, forceplain); 
     }
-    pbm_freerow(pbmrow);
-    pm_close(file);
 }
 
 
+
 static void
 framePerimeter(unsigned int const Width, 
                unsigned int const Height) {
 
-    unsigned int x,y;
+    unsigned int x, y;
 
     /* Top edge */
     for (x = 0; x < Width; ++x)
@@ -221,57 +217,66 @@ framePerimeter(unsigned int const Width,
 
 
 int 
-main(int argc,char** argv) {
+main(int argc, const char** argv) {
 
-    int TP=1;
-    int x,y;
+    int TP;
+    unsigned int x, y;
     char buf[128];
-    int Width;      /* width and height in 600ths of an inch */
-    int Height;
+    /* width and height in 600ths of an inch */
+    unsigned int Width;
+    unsigned int Height;
 
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     if (argc > 1 && strcmp(argv[1], "-a4") == 0) {
-        Width = A4WIDTH;
+        Width  = A4WIDTH;
         Height = A4HEIGHT;
-        argc--;
-        argv++;
+        --argc;
+        ++argv;
     } else {
-        Width = USWIDTH;
+        Width  = USWIDTH;
         Height = USHEIGHT;
     }
 
-    bitmap.Width = Width;
+    if (argc > 1)
+        TP = atoi(argv[1]);
+    else
+        TP = 1;
+
+    bitmap.Width  = Width;
     bitmap.Height = Height;
-    bitmap.Pwidth = (Width + 7) / 8;
-    bitmap.bitmap = malloc(bitmap.Pwidth * bitmap.Height);
+    bitmap.bitmap = pbm_allocarray_packed(Width, bitmap.Height);
 
-    for (x = 0; x < bitmap.Pwidth * bitmap.Height; ++x)
-        bitmap.bitmap[x] = 0x00;
-    
-    if (argc>1)
-        TP = atoi(argv[1]);
+    for (y = 0; y < bitmap.Height; ++y) {
+        unsigned int x;
+        for (x = 0; x < pbm_packed_bytes(bitmap.Width); ++x) 
+            bitmap.bitmap[y][x] = 0x00; 
+    }
 
     switch (TP) {
     case 1:
         framePerimeter(Width, Height);
-        for (x = 0; x < Width; x += 100)
+        for (x = 0; x < Width; x += 100) {
+            unsigned int y;
             for(y = 0; y < Height; y += 100)
                 setplus(x, y, 4);
+        }
         for(x = 0; x < Width; x += 100) {
             sprintf(buf,"%d", x);
             setstring(x + 3, (Height/200) * 100 + 3, buf);
         }
-        for (y=0; y < Height; y += 100) {
+        for (y = 0; y < Height; y += 100) {
             sprintf(buf, "%d", y);
             setstring((Width/200) * 100 + 3, y + 3, buf);
         }
         for (x = 0; x < Width; x += 10)
             for (y = 0; y < Height; y += 100)
                 setplus(x, y, ((x%100) == 50) ? 2 : 1);
-        for (x=0; x < Width; x += 100)
+        for (x = 0; x < Width; x += 100) {
+            unsigned int y;
             for (y = 0; y < Height; y += 10)
                 setplus(x, y, ((y%100) == 50) ? 2 : 1);
+        }
         setCG(Width/2, Height/2);
         break;
     case 2:
@@ -290,5 +295,9 @@ main(int argc,char** argv) {
 
     outputPbm(stdout, bitmap);
 
+    pbm_freearray(bitmap.bitmap, Height);
+
+    pm_close(stdout);
+
     return 0;
 }
diff --git a/generator/pbmtext.c b/generator/pbmtext.c
index 693c3f59..9f4366d4 100644
--- a/generator/pbmtext.c
+++ b/generator/pbmtext.c
@@ -79,7 +79,7 @@ parseCommandLine(int argc, const char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 == 0)
@@ -236,7 +236,8 @@ fixControlChars(const char *  const input,
     MALLOCARRAY(output, outputSize);
 
     if (output == NULL)
-        pm_error("Couldn't allocate %u bytes for a line of text.", outputSize);
+        pm_error("Couldn't allocate %u bytes for a line of text.",
+                 (unsigned)outputSize);
 
     for (inCursor = 0, outCursor = 0; input[inCursor] != '\0'; ++inCursor) {
         if (outCursor + 1 + tabSize > outputSize) {
@@ -244,7 +245,7 @@ fixControlChars(const char *  const input,
             REALLOCARRAY(output, outputSize);
             if (output == NULL)
                 pm_error("Couldn't allocate %u bytes for a line of text.",
-                         outputSize);
+                         (unsigned)outputSize);
         }
         if (input[inCursor] == '\n' && input[inCursor+1] == '\0') {
             /* This is a terminating newline.  We don't do those. */
@@ -709,7 +710,7 @@ getText(const char          cmdline_text[],
         while (fgets(buf, sizeof(buf), stdin) != NULL) {
             if (strlen(buf) + 1 >= sizeof(buf))
                 pm_error("A line of input text is longer than %u characters."
-                         "Cannot process.", sizeof(buf)-1);
+                         "Cannot process.", (unsigned)sizeof(buf)-1);
             if (lineCount >= maxlines) {
                 maxlines *= 2;
                 REALLOCARRAY(text_array, maxlines);
diff --git a/generator/pbmtextps.c b/generator/pbmtextps.c
index eab6780a..e6367530 100644
--- a/generator/pbmtextps.c
+++ b/generator/pbmtextps.c
@@ -181,7 +181,7 @@ buildTextFromArgs(int           const argc,
         asciiHexEncode(text, asciiHexText);
         *asciiHexTextP = asciiHexText;
     }
-    strfree(text);
+    pm_strfree(text);
 }
 
 
@@ -219,7 +219,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
 
     validateFontName(cmdlineP->font);
 
@@ -231,7 +231,7 @@ parseCommandLine(int argc, char ** argv,
 static void
 termCmdline(struct cmdlineInfo const cmdline) {
 
-    strfree(cmdline.text);
+    pm_strfree(cmdline.text);
 }
 
 
@@ -263,11 +263,11 @@ construct_postscript(struct cmdlineInfo const cmdline) {
             "showpage\n";
 
     if (cmdline.stroke < 0)
-        asprintfN(&retval, template, cmdline.font, cmdline.fontsize, 
-                  cmdline.text);
+        pm_asprintf(&retval, template, cmdline.font, cmdline.fontsize, 
+                    cmdline.text);
     else
-        asprintfN(&retval, template, cmdline.font, cmdline.fontsize, 
-                  cmdline.stroke, cmdline.text);
+        pm_asprintf(&retval, template, cmdline.font, cmdline.fontsize, 
+                    cmdline.stroke, cmdline.text);
 
     return retval;
 }
@@ -362,10 +362,11 @@ gsCommand(const char *       const psFname,
          pm_error("Absurdly fine resolution (%u) and/or huge font size (%u). "
                   "Output height too large.", cmdline.res, cmdline.fontsize);
          
-    asprintfN(&retval, "%s -g%dx%d -r%d -sDEVICE=pbm "
-              "-sOutputFile=%s -q -dBATCH -dNOPAUSE %s </dev/null >/dev/null", 
-              gsExecutableName(), (int) x, (int) y, cmdline.res, 
-              outputFilename, psFname);
+    pm_asprintf(&retval, "%s -g%dx%d -r%d -sDEVICE=pbm "
+                "-sOutputFile=%s -q -dBATCH -dNOPAUSE %s "
+                "</dev/null >/dev/null", 
+                gsExecutableName(), (int) x, (int) y, cmdline.res, 
+                outputFilename, psFname);
 
     return retval;
 }
@@ -379,9 +380,9 @@ cropCommand(const char * const inputFileName) {
     const char * plainOpt = pm_plain_output ? "-plain" : "" ;
     
     if (cropExecutableName()) {
-        asprintfN(&retval, "%s -top -right %s %s", 
-                  cropExecutableName(), plainOpt, inputFileName);
-        if (retval == strsol)
+        pm_asprintf(&retval, "%s -top -right %s %s", 
+                    cropExecutableName(), plainOpt, inputFileName);
+        if (retval == pm_strsol)
             pm_error("Unable to allocate memory");
     } else
         retval = NULL;
@@ -413,7 +414,7 @@ writeProgram(const char *       const psFname,
 
     fclose(psfile);
 
-    strfree(ps);
+    pm_strfree(ps);
 }
 
 
@@ -437,7 +438,7 @@ executeProgram(const char *       const psFname,
     if (rc != 0)
         pm_error("Failed to run Ghostscript process.  rc=%d", rc);
 
-    strfree(com);
+    pm_strfree(com);
 }
 
 
@@ -486,7 +487,7 @@ cropToStdout(const char * const inputFileName,
             }
             fclose(pnmcrop);
         }
-        strfree(com);
+        pm_strfree(com);
     }
 }
 
@@ -500,25 +501,25 @@ createOutputFile(struct cmdlineInfo const cmdline) {
     const char * psFname;
     const char * uncroppedPbmFname;
 
-    asprintfN(&psFname, template, getpid(), "ps");
+    pm_asprintf(&psFname, template, getpid(), "ps");
     if (psFname == NULL)
         pm_error("Unable to allocate memory");
  
     writeProgram(psFname, cmdline);
 
-    asprintfN(&uncroppedPbmFname, template, getpid(), "pbm");
+    pm_asprintf(&uncroppedPbmFname, template, getpid(), "pbm");
     if (uncroppedPbmFname == NULL)
         pm_error("Unable to allocate memory");
  
     executeProgram(psFname, uncroppedPbmFname, cmdline);
 
     unlink(psFname);
-    strfree(psFname);
+    pm_strfree(psFname);
 
     cropToStdout(uncroppedPbmFname, cmdline.verbose);
 
     unlink(uncroppedPbmFname);
-    strfree(uncroppedPbmFname);
+    pm_strfree(uncroppedPbmFname);
 }
 
 
diff --git a/generator/pgmcrater b/generator/pgmcrater
new file mode 100755
index 00000000..1c22ed70
--- /dev/null
+++ b/generator/pgmcrater
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+##############################################################################
+# This is essentially a Perl program.  We exec the Perl interpreter specifying
+# this same file as the Perl program and use the -x option to cause the Perl
+# interpreter to skip down to the Perl code.  The reason we do this instead of
+# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is
+# that the user may have multiple Perl interpreters and the one he wants to
+# use is properly located in the PATH.  The user's choice of Perl interpreter
+# may be crucial, such as when the user also has a PERL5LIB environment
+# variable and it selects modules that work with only a certain main
+# interpreter program.
+#
+# An alternative some people use is to have /usr/bin/env as the script
+# interpreter.  We don't do that because we think the existence and
+# compatibility of /bin/sh is more reliable.
+#
+# Note that we aren't concerned about efficiency because the user who needs
+# high efficiency can use directly the programs that this program invokes.
+#
+##############################################################################
+
+exec perl -w -x -S -- "$0" "$@"
+
+#!/usr/bin/perl
+##############################################################################
+#  This is nothing but a compatibility interface for
+#  Pamcrater/Pamshadedrelief.  An old program coded to call Pgmcrater will
+#  continue working because this interface exists.  All new (or newly
+#  modified) programs should call Pamcrater and Pamshadedrelief instead.
+#
+#  In days past, Pamcrater and Pamshadedrelief did not exist.  Pgmcrater did
+#  both jobs together, with PGM output.
+##############################################################################
+
+use strict;
+
+use Getopt::Long;
+
+my @pgmcraterArgv = @ARGV;
+
+my $validOptions = GetOptions(
+    'number=i'     => \my $numberOpt,
+    'height=i'     => \my $heightOpt,
+    'ysize=i'      => \my $ysizeOpt,
+    'width=i'      => \my $widthOpt,
+    'xsize=i'      => \my $xsizeOpt,
+    'gamma=i'      => \my $gammaOpt,
+    'randomseed=i' => \my $randomseedOpt);
+
+if (!$validOptions) {
+    print STDERR "Invalid syntax\n";
+    exit(100);
+}
+
+my $pamcraterArgs;
+
+$pamcraterArgs = '';  # initial value
+
+if (defined($numberOpt)) {
+    $pamcraterArgs .= "'-number=$numberOpt' ";
+}
+
+if (defined($heightOpt)) {
+    $pamcraterArgs .= "'-height=$heightOpt' ";
+} elsif (defined($ysizeOpt)) {
+    $pamcraterArgs .= "'-height=$ysizeOpt' ";
+}
+
+if (defined($widthOpt)) {
+    $pamcraterArgs .= "'-width=$widthOpt' ";
+} elsif (defined($xsizeOpt)) {
+    $pamcraterArgs .= "'-width=$xsizeOpt' ";
+}
+
+if (defined($randomseedOpt)) {
+    $pamcraterArgs .= "'-randomseed=$randomseedOpt' ";
+}
+
+my $pamshadedreliefArgs;
+
+$pamshadedreliefArgs = '';  # initial value
+
+if (defined($gammaOpt)) {
+    $pamshadedreliefArgs .= "'-gamma=$gammaOpt' ";
+}
+
+my $termStatus =
+    system(
+        "pamcrater $pamcraterArgs | " .
+        "pamshadedrelief $pamshadedreliefArgs |" .
+        "pamtopnm");
+
+exit($termStatus == 0 ? 0 : 1);
diff --git a/generator/pgmcrater.c b/generator/pgmcrater.c
deleted file mode 100644
index ec592381..00000000
--- a/generator/pgmcrater.c
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
-
-			  Fractal cratering
-
-	   Designed and implemented in November of 1989 by:
-
-	    John Walker
-	    Autodesk SA
-	    Avenue des Champs-Montants 14b
-	    CH-2074 MARIN
-	    Switzerland
-	    Usenet: kelvin@Autodesk.com
-	    Fax:    038/33 88 15
-	    Voice:  038/33 76 33
-
-    The  algorithm  used  to  determine crater size is as described on
-    pages 31 and 32 of:
-
-	Peitgen, H.-O., and Saupe, D. eds., The Science Of Fractal
-	    Images, New York: Springer Verlag, 1988.
-
-    The  mathematical  technique  used	to calculate crater radii that
-    obey the proper area law distribution from a uniformly distributed
-    pseudorandom sequence was developed by Rudy Rucker.
-
-    Permission	to  use, copy, modify, and distribute this software and
-    its documentation  for  any  purpose  and  without	fee  is  hereby
-    granted,  without any conditions or restrictions.  This software is
-    provided "as is" without express or implied warranty.
-
-				PLUGWARE!
-
-    If you like this kind of stuff, you may also enjoy "James  Gleick's
-    Chaos--The  Software"  for  MS-DOS,  available for $59.95 from your
-    local software store or directly from Autodesk, Inc., Attn: Science
-    Series,  2320  Marinship Way, Sausalito, CA 94965, USA.  Telephone:
-    (800) 688-2344 toll-free or, outside the  U.S. (415)  332-2344  Ext
-    4886.   Fax: (415) 289-4718.  "Chaos--The Software" includes a more
-    comprehensive   fractal    forgery	  generator    which	creates
-    three-dimensional  landscapes  as  well as clouds and planets, plus
-    five more modules which explore other aspects of Chaos.   The  user
-    guide  of  more  than  200	pages includes an introduction by James
-    Gleick and detailed explanations by Rudy Rucker of the  mathematics
-    and algorithms used by each program.
-
-*/
-
-/* Modifications by Arjen Bax, 2001-06-21: Remove black vertical line at right
- * edge. Make craters wrap around the image (enables tiling of image).
- */
-
-#define _XOPEN_SOURCE   /* get M_PI in math.h */
-
-#include <assert.h>
-#include <math.h>
-
-#include "pm_c_util.h"
-#include "pgm.h"
-#include "mallocvar.h"
-
-static void gencraters ARGS((void));
-static void initseed ARGS((void));
-
-/* Definitions for obtaining random numbers. */
-
-#define Cast(low, high) ((low)+((high)-(low)) * ((rand() & 0x7FFF) / arand))
-
-/*  Data types	*/
-
-typedef int Boolean;
-#define FALSE 0
-#define TRUE 1
-
-#define V   (void)
-
-/*  Display parameters	*/
-
-#define SCRX	screenxsize	      /* Screen width */
-#define SCRY	screenysize	      /* Screen height */
-#define SCRGAMMA 1.0		      /* Display gamma */
-
-/*  Local variables  */
-
-#define ImageGamma  0.5 	      /* Inherent gamma of mapped image */
-
-static int screenxsize = 256;	      /* Screen X size */
-static int screenysize = 256;	      /* Screen Y size */
-static double dgamma = SCRGAMMA;      /* Display gamma */
-static double arand = 32767.0;	      /* Random number parameters */
-static long ncraters = 50000L;	      /* Number of craters to generate */
-static double CdepthPower = 1.5;      /* Crater depth power factor */
-static double DepthBias = 0.707107;   /* Depth bias */
-
-static int modulo(int t, int n)
-{
-    int m;
-    assert(n>0);
-    m = t % n;
-    while (m<0) {
-	m+=n;
-    }
-    return m;
-}
-
-/*  INITSEED  --  Generate initial random seed, if needed.  */
-
-static void initseed()
-{
-    unsigned int i;
-
-    srand(pm_randseed());
-    for (i = 0; i < 7; ++i) 
-        V rand();
-}
-
-/*  GENCRATERS	--  Generate cratered terrain.	*/
-
-static void gencraters()
-{
-    int i, j, x, y;
-    long l;
-    unsigned short *aux;
-    int slopemin = -52, slopemax = 52;
-#define RGBQuant    255
-    unsigned char *slopemap;   /* Slope to pixel map */
-    gray *pixels;	       /* Pixel vector */
-
-#define Auxadr(x, y)  &aux[modulo(y, SCRY)*SCRX+modulo(x, SCRX)]
-
-    /* Acquire the elevation array and initialize it to mean
-       surface elevation. */
-
-    MALLOCARRAY(aux, SCRX * SCRY);
-    if (aux == NULL) 
-        pm_error("out of memory allocating elevation array");
-
-    /* Acquire the elevation buffer and initialize to mean
-       initial elevation. */
-
-    for (i = 0; i < SCRY; i++) {
-	unsigned short *zax = aux + (((long) SCRX) * i);
-
-	for (j = 0; j < SCRX; j++) {
-	    *zax++ = 32767;
-	}
-    }
-
-    /* Every time we go around this loop we plop another crater
-       on the surface.	*/
-
-    for (l = 0; l < ncraters; l++) {
-	double g;
-	int cx = Cast(0.0, ((double) SCRX - 1)),
-	    cy = Cast(0.0, ((double) SCRY - 1)),
-	    gx, gy, x, y;
-	unsigned long amptot = 0, axelev;
-	unsigned int npatch = 0;
-
-
-	/* Phase 1.  Compute the mean elevation of the impact
-		     area.  We assume the impact area is a
-		     fraction of the total crater size. */
-
-	/* Thanks, Rudy, for this equation  that maps the uniformly
-	   distributed	numbers  from	Cast   into   an   area-law
-	   distribution as observed on cratered bodies. */
-
-	g = sqrt(1 / (M_PI * (1 - Cast(0, 0.9999))));
-
-	/* If the crater is tiny, handle it specially. */
-
-#if 0
-	fprintf(stderr, "crater=%lu ", (unsigned long)l);
-	fprintf(stderr, "cx=%d ", cx);
-	fprintf(stderr, "cy=%d ", cy);
-	fprintf(stderr, "size=%g\n", g);
-#endif
-
-	if (g < 3) {
-
-	   /* Set pixel to the average of its Moore neighbourhood. */
-
-	    for (y = cy - 1; y <= cy + 1; y++) {
-		for (x = cx - 1; x <= cx + 1; x++) {
-		    amptot += *Auxadr(x, y);
-		    npatch++;
-		}
-	    }
-	    axelev = amptot / npatch;
-
-	    /* Perturb the mean elevation by a small random factor. */
-
-	    x = (g >= 1) ? ((rand() >> 8) & 3) - 1 : 0;
-	    *Auxadr(cx, cy) = axelev + x;
-
-	    /* Jam repaint sizes to correct patch. */
-
-	    gx = 1;
-	    gy = 0;
-
-	} else {
-
-	    /* Regular crater.	Generate an impact feature of the
-	       correct size and shape. */
-
-	    /* Determine mean elevation around the impact area. */
-
-	    gx = MAX(2, (g / 3));
-	    gy = MAX(2, g / 3);
-
-	    for (y = cy - gy; y <= cy + gy; y++) {
-		for (x = cx-gx; x <= cx + gx; x++) {
-		    amptot += *Auxadr(x,y);
-		    npatch++;
-		}
-	    }
-	    axelev = amptot / npatch;
-
-	    gy = MAX(2, g);
-	    g = gy;
-	    gx = MAX(2, g);
-
-	    for (y = cy - gy; y <= cy + gy; y++) {
-		double dy = (cy - y) / (double) gy,
-		       dysq = dy * dy;
-
-		for (x = cx - gx; x <= cx + gx; x++) {
-		    double dx = ((cx - x) / (double) gx),
-			   cd = (dx * dx) + dysq,
-			   cd2 = cd * 2.25,
-			   tcz = DepthBias - sqrt(fabs(1 - cd2)),
-			   cz = MAX((cd2 > 1) ? 0.0 : -10, tcz),
-			   roll, iroll;
-		    unsigned short av;
-
-		    cz *= pow(g, CdepthPower);
-		    if (dy == 0 && dx == 0 && ((int) cz) == 0) {
-		       cz = cz < 0 ? -1 : 1;
-		    }
-
-#define 	    rollmin 0.9
-		    roll = (((1 / (1 - MIN(rollmin, cd))) /
-			     (1 / (1 - rollmin))) - (1 - rollmin)) / rollmin;
-		    iroll = 1 - roll;
-
-		    av = (axelev + cz) * iroll + (*Auxadr(x,y) + cz) * roll;
-		    av = MAX(1000, MIN(64000, av));
-		    *Auxadr(x,y) = av;
-		}
-	    }
-	 }
-	if ((l % 5000) == 4999) {
-	    pm_message( "%ld craters generated of %ld (%ld%% done)",
-		l + 1, ncraters, ((l + 1) * 100) / ncraters);
-	}
-    }
-
-    i = MAX((slopemax - slopemin) + 1, 1);
-    MALLOCARRAY(slopemap, i);
-    if (slopemap == NULL)
-        pm_error("out of memory allocating slope map");
-
-    for (i = slopemin; i <= slopemax; i++) {
-
-        /* Confused?   OK,  we're using the  left-to-right slope to
-	   calculate a shade based on the sine of  the	angle  with
-	   respect  to the vertical (light incident from the left).
-	   Then, with one exponentiation, we account for  both	the
-	   inherent   gamma   of   the	 image	(ad-hoc),  and	the
-	   user-specified display gamma, using the identity:
-
-		 (x^y)^z = (x^(y*z))		     */
-
-	slopemap[i - slopemin] = i > 0 ?
-	    (128 + 127.0 *
-		pow(sin((M_PI / 2) * i / slopemax),
-		       dgamma * ImageGamma)) :
-	    (128 - 127.0 *
-		pow(sin((M_PI / 2) * i / slopemin),
-		       dgamma * ImageGamma));
-    }
-
-    /* Generate the screen image. */
-
-    pgm_writepgminit(stdout, SCRX, SCRY, RGBQuant, FALSE);
-    pixels = pgm_allocrow(SCRX);
-
-    for (y = 0; y < SCRY; y++) {
-	gray *pix = pixels;
-
-	for (x = 0; x < SCRX; x++) {
-	    int j = *Auxadr(x+1, y) - *Auxadr(x, y);
-	    j = MIN(MAX(slopemin, j), slopemax);
-	    *pix++ = slopemap[j - slopemin];
-	}
-	pgm_writepgmrow(stdout, pixels, SCRX, RGBQuant, FALSE);
-    }
-    pm_close(stdout);
-    pgm_freerow(pixels);
-
-#undef Auxadr
-#undef Scradr
-    free((char *) slopemap);
-    free((char *) aux);
-}
-
-/*  MAIN  --  Main program.  */
-
-int main(argc, argv)
-  int argc;
-  char *argv[];
-{
-    int i;
-    Boolean gammaspec = FALSE, numspec = FALSE,
-	    widspec = FALSE, hgtspec = FALSE;
-    const char * const usage = "[-number <n>] [-width|-xsize <w>]\n\
-                  [-height|-ysize <h>] [-gamma <f>]";
-
-    DepthBias = sqrt(0.5);	      /* Get exact value for depth bias */
-
-
-    pgm_init(&argc, argv);
-
-    i = 1;
-    while ((i < argc) && (argv[i][0] == '-') && (argv[i][1] != '\0')) {
-        if (pm_keymatch(argv[i], "-gamma", 2)) {
-	    if (gammaspec) {
-                pm_error("already specified gamma correction");
-	    }
-	    i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &dgamma)  != 1))
-		pm_usage(usage);
-	    if (dgamma <= 0.0) {
-                pm_error("gamma correction must be greater than 0");
-	    }
-	    gammaspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-number", 2)) {
-	    if (numspec) {
-                pm_error("already specified number of craters");
-	    }
-	    i++;
-            if ((i == argc) || (sscanf(argv[i], "%ld", &ncraters) != 1))
-		pm_usage(usage);
-	    if (ncraters <= 0) {
-                pm_error("number of craters must be greater than 0!");
-	    }
-	    numspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-xsize", 2) ||
-                   pm_keymatch(argv[i], "-width", 2)) {
-	    if (widspec) {
-                pm_error("already specified a width/xsize");
-	    }
-	    i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &screenxsize) != 1))
-		pm_usage(usage);
-	    if (screenxsize <= 0) {
-                pm_error("screen width must be greater than 0");
-	    }
-	    widspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-ysize", 2) ||
-                   pm_keymatch(argv[i], "-height", 2)) {
-	    if (hgtspec) {
-                pm_error("already specified a height/ysize");
-	    }
-	    i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &screenysize) != 1))
-		pm_usage(usage);
-	    if (screenxsize <= 0) {
-                pm_error("screen height must be greater than 0");
-	    }
-	    hgtspec = TRUE;
-	} else {
-	    pm_usage(usage);
-	}
-	i++;
-    }
-
-    initseed();
-    gencraters();
-
-    exit(0);
-}
diff --git a/generator/pgmkernel.c b/generator/pgmkernel.c
index b741d596..ec634c16 100644
--- a/generator/pgmkernel.c
+++ b/generator/pgmkernel.c
@@ -1,8 +1,8 @@
-/* pgmkernel.c - generate a portable graymap convolution kernel
+/* pgmkernel.c - generate a PGM convolution kernel
 **
-** Creates a Portable Graymap file containing a convolution filter
-** with max value = 255 and minimum value > 127 that can be used as a 
-** smoothing kernel for pnmconvol.
+** Creates a PGM image containing a convolution filter with max value = 255
+** and minimum value > 127 that can be used as a smoothing kernel for
+** pnmconvol.
 **
 ** Copyright (C) 1992 by Alberto Accomazzi, Smithsonian Astrophysical
 ** Observatory.
@@ -16,76 +16,228 @@
 */
 
 #include <math.h>
-#include "pgm.h"
+#include "pm_c_util.h"
+#include "shhopt.h"
 #include "mallocvar.h"
+#include "pgm.h"
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    unsigned int cols;
+    unsigned int rows;
+    float weight;
+    gray maxval;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+  Convert program invocation arguments (argc,argv) into a format the 
+  program can use easily, struct cmdlineInfo.  Validate arguments along
+  the way and exit program with message if invalid.
+
+  Note that some string information we return as *cmdlineP is in the storage 
+  argv[] points to.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to OptParseOptions2 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int weightSpec, maxvalSpec;
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "weight",  OPT_FLOAT, &cmdlineP->weight, 
+            &weightSpec,     0);
+    OPTENT3(0,   "maxval",  OPT_UINT, &cmdlineP->maxval, 
+            &maxvalSpec,     0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!weightSpec)
+        cmdlineP->weight = 6.0;
+
+    if (cmdlineP->weight < 0.0)
+        pm_error("-weight cannot be negative.  You specified %f",
+                 cmdlineP->weight);
+
+    if (!maxvalSpec)
+        cmdlineP->maxval = PGM_MAXMAXVAL;
+
+    if (cmdlineP->maxval > PGM_OVERALLMAXVAL)
+        pm_error("-maxval is too large: %u.  Maximum is %u",
+                 cmdlineP->maxval, PGM_OVERALLMAXVAL);
+
+    if (cmdlineP->maxval == 0)
+        pm_error("-maxval cannot be zero");
+
+    if (argc-1 < 1)
+        pm_error("Need at least one argument: size of (square) kernel");
+    else if (argc-1 == 1) {
+        if (atoi(argv[1]) <= 0)
+            pm_error("Dimension must be a positive number.  "
+                     "You specified '%s'", argv[1]);
+        cmdlineP->cols = atoi(argv[1]);
+        cmdlineP->rows = atoi(argv[1]);
+    } else if (argc-1 == 2) {
+        if (atoi(argv[1]) <= 0)
+            pm_error("Width must be a positive number.  "
+                     "You specified '%s'", argv[1]);
+        if (atoi(argv[2]) <= 0)
+            pm_error("Height must be a positive number.  "
+                     "You specified '%s'", argv[2]);
+        cmdlineP->cols = atoi(argv[1]);
+        cmdlineP->rows = atoi(argv[2]);
+    } else
+        pm_error("At most two arguments allowed.  "
+                 "You specified %u", argc-1);
+}
+
+
+
+static double
+t(double const dx2,
+  double const dy2,
+  double const weight) {
+/*----------------------------------------------------------------------------
+  The t value for a pixel that is (dx, dy) pixels away from the center of
+  the kernel, where 'dx2' is SQR(dx) and 'dy2' is SQR(dy), if the distance is
+  weighted by 'weight'.
+-----------------------------------------------------------------------------*/
+
+    return 1.0 / (1.0 + weight * sqrt(dx2 + dy2));
+}
+
+
+
+static double
+tMaxAllKernel(unsigned int const cols,
+              unsigned int const rows,
+              double       const weight) {
+/*----------------------------------------------------------------------------
+   The maximum t value over all pixels in the kernel, if the kernel is
+   'cols' by 'rows' pixels and distance is weighted by 'weight'.
+-----------------------------------------------------------------------------*/
+
+    /* It depends upon whether there is an even or odd number of rows
+       and columns.  If both dimensions are odd, there is a pixel right
+       at the center, and it has the greatest t value.  If both dimensions
+       are even, the center of the image is in the center of a 4-pixel
+       square and each of those 4 pixels has the greatest t value.  If
+       one dimension is even and the other odd, the center of the kernel
+       is midway between two pixels, horizontally or vertically, and one
+       of those two pixels has the greatest t value.
+    */
+
+    double dxMax, dyMax;
+
+    switch (cols % 2 + rows % 2) {
+    case 0:
+        dxMax = 0.5;
+        dyMax = 0.5;
+        break;
+    case 1:
+        dxMax = 0.5;
+        dyMax = 0.0;
+        break;
+    case 2:
+        dxMax = 0.0;
+        dyMax = 0.0;
+    }
+
+    return t(SQR(dxMax), SQR(dyMax), weight);
+}
+
+
+
+static void
+writeKernel(FILE *       const ofP,
+            unsigned int const cols,
+            unsigned int const rows,
+            gray         const maxval,
+            gray **      const halfKernel,
+            unsigned int const halfRows) {
+
+    unsigned int row;
+
+    pgm_writepgminit(stdout, cols, rows, maxval, 0);
+
+    for (row = 0; row < halfRows; ++row)
+        pgm_writepgmrow(stdout, halfKernel[row], cols, maxval, 0);
+
+    /* Now write out the same rows in reverse order. */
+
+    for (; row < rows; ++row)
+        pgm_writepgmrow(stdout, halfKernel[rows-1-row], cols, maxval, 0);
+}
+
+
 
 int
-main ( argc, argv )
-    int argc;
-    char *argv[];
-{
-    register    int i, j;
-    int     argn = 1, ixsize, iysize, maxval = 255;
-    double  fxsize = 0.0, fysize = 0.0, w = 6.0, kxcenter, kycenter, 
-        tmax = 0, *fkernel;
-    const char  *usage = "[-weight f] width [height]";
-
-    pgm_init( &argc, argv );
-
-    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-    {
-        if ( pm_keymatch( argv[argn], "-weight", 2 )) {
-            if (++argn >= argc)
-                pm_usage( usage );
-            else if (sscanf(argv[argn], "%lf", &w) != 1)
-                pm_usage( usage );
+main(int argc, const char * argv[]) {
+
+    struct CmdlineInfo cmdline;
+    unsigned int arows;
+    unsigned int arow;
+    double xcenter, ycenter;
+        /* row, column "number" of center of kernel */
+    double tMax;
+        /* The maximum t value over all pixels */
+    gray ** halfKernel;
+        /* The upper half of the kernel we generate.  The lower half is
+           just the mirror image of this.
+        */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    xcenter = ((double) cmdline.cols - 1) / 2.0;
+    ycenter = ((double) cmdline.rows - 1) / 2.0;
+
+    tMax = tMaxAllKernel(cmdline.cols, cmdline.rows, cmdline.weight);
+
+    /* Output matrix is symmetric vertically and horizontally. */
+
+    arows = (cmdline.rows + 1) / 2;
+        /* Half the number of rows.  Add 1 if odd. */
+    halfKernel = pgm_allocarray(cmdline.cols, arows);
+
+    for (arow = 0; arow < arows; ++arow) {
+        double const dy2 = SQR(arow - ycenter);
+
+        unsigned int col;
+        for (col = 0; col < (cmdline.cols +1) / 2; ++col) {
+            double const dx2 = SQR(col - xcenter);
+
+            double const normalized = t(dx2, dy2, cmdline.weight) / 2 / tMax;
+
+            gray const grayval = ROUNDU(cmdline.maxval * (0.5 + normalized));
+
+            halfKernel[arow][col                   ] = grayval;
+            halfKernel[arow][cmdline.cols - col - 1] = grayval;
         }
-        else
-            pm_usage( usage );
-        argn++;
     }
 
-    if (argn == argc)
-        pm_usage( usage );
-    
-    if (sscanf(argv[argn], "%lf", &fxsize) != 1) 
-        pm_error( "error reading input kernel x size, (%s)\n", argv[argn]);
+    writeKernel(stdout, cmdline.cols, cmdline.rows, cmdline.maxval,
+                halfKernel, arows);
 
-    ++argn;
-    if (argn == argc - 1) {
-        if (sscanf(argv[argn], "%lf", &fysize) != 1)
-            pm_error( "error reading input kernel y size, (%s)\n", argv[argn]);
-    }
-    else if (argn == argc)
-        fysize = fxsize;
-    else
-        pm_usage( usage );
-
-    if (fxsize <= 1 || fysize <= 1)
-        pm_usage( usage );
-
-    kxcenter = (fxsize - 1) / 2.0;
-    kycenter = (fysize - 1) / 2.0;
-    ixsize = fxsize + 0.999;
-    iysize = fysize + 0.999;
-    MALLOCARRAY(fkernel, ixsize * iysize);
-    for (i = 0; i < iysize; i++) 
-        for (j = 0; j < ixsize; j++) {
-            fkernel[i*ixsize+j] = 1.0 / (1.0 + w * sqrt((double)
-                                                        (i-kycenter)*(i-kycenter)+
-                                                        (j-kxcenter)*(j-kxcenter)));
-            if (tmax < fkernel[i*ixsize+j])
-                tmax = fkernel[i*ixsize+j];
-        }
+    pgm_freearray(halfKernel, arows);
 
-    /* output PGM header + data (ASCII format only) */
-    printf("P2\n%d %d\n%d\n", ixsize, iysize, maxval);
-    
-    for (i = 0; i < iysize; i++, printf("\n"))
-        for (j = 0; j < ixsize; j++)
-            printf(" %3d", (int)(maxval * (fkernel[i*ixsize+j] / 
-                                           (2*tmax) + 0.5)));
-    
-    exit(0);
+    return 0;
 }
-
diff --git a/generator/pgmmake.c b/generator/pgmmake.c
index bc7f025c..f8f8b09c 100644
--- a/generator/pgmmake.c
+++ b/generator/pgmmake.c
@@ -43,19 +43,21 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = false;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = false;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
+    free (option_def);
+
     if (!maxvalSpec)
         cmdlineP->maxval = PGM_MAXMAXVAL;
     else {
         if (cmdlineP->maxval > PGM_OVERALLMAXVAL)
             pm_error("The value you specified for -maxval (%u) is too big.  "
                      "Max allowed is %u", cmdlineP->maxval, PGM_OVERALLMAXVAL);
-        
+
         if (cmdlineP->maxval < 1)
             pm_error("You cannot specify 0 for -maxval");
-    }    
+    }
 
     if (argc-1 < 3)
         pm_error("Need 3 arguments: gray level, width, height.");
@@ -82,7 +84,7 @@ main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
     gray * grayrow;
-    unsigned int row;
+    unsigned int col, row;
 
     pgm_init(&argc, argv);
 
@@ -91,12 +93,12 @@ main(int argc, char *argv[]) {
     pgm_writepgminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0);
     grayrow = pgm_allocrow(cmdline.cols);
 
-    for (row = 0; row < cmdline.rows; ++row) {
-        unsigned int col;
-        for (col = 0; col < cmdline.cols; ++col)
-            grayrow[col] = cmdline.grayLevel;
+    /* All rows are identical.  Fill once. */
+    for (col = 0; col < cmdline.cols; ++col)
+        grayrow[col] = cmdline.grayLevel;
+
+    for (row = 0; row < cmdline.rows; ++row)
         pgm_writepgmrow(stdout, grayrow, cmdline.cols, cmdline.maxval, 0);
-	}
 
     pgm_freerow(grayrow);
     pm_close(stdout);
diff --git a/generator/pgmnoise.c b/generator/pgmnoise.c
index 215cbfeb..442edc59 100644
--- a/generator/pgmnoise.c
+++ b/generator/pgmnoise.c
@@ -7,6 +7,8 @@
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "pgm.h"
+#include <assert.h>
+
 
 
 struct cmdlineInfo {
@@ -15,13 +17,13 @@ struct cmdlineInfo {
     */
     unsigned int width;
     unsigned int height;
+    unsigned int maxval;
     unsigned int randomseed;
     unsigned int randomseedSpec;
 };
 
 
 
-
 static void
 parseCommandLine(int argc, const char ** const argv,
                  struct cmdlineInfo * const cmdlineP) {
@@ -34,19 +36,32 @@ parseCommandLine(int argc, const char ** const argv,
          */
     optStruct3 opt;
     unsigned int option_def_index;
+    unsigned int maxvalSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "randomseed",   OPT_INT,    &cmdlineP->randomseed,
+    OPTENT3(0,   "randomseed",   OPT_UINT,    &cmdlineP->randomseed,
             &cmdlineP->randomseedSpec,      0);
+    OPTENT3(0,   "maxval",       OPT_UINT,    &cmdlineP->maxval,
+            &maxvalSpec,                    0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    free(option_def);
+
+    if (maxvalSpec) {
+        if (cmdlineP->maxval > PGM_OVERALLMAXVAL)
+            pm_error("Maxval too large: %u.  Maximu is %u", 
+                     cmdlineP->maxval, PGM_OVERALLMAXVAL);
+        else if (cmdlineP->maxval == 0)
+            pm_error("Maxval must not be zero");
+    } else
+        cmdlineP->maxval = PGM_MAXMAXVAL;
 
     if (argc-1 != 2)
         pm_error("Wrong number of arguments: %u.  "
@@ -70,25 +85,91 @@ parseCommandLine(int argc, const char ** const argv,
 
 
 
+static unsigned int
+randPool(unsigned int const digits) {
+/*----------------------------------------------------------------------------
+  Draw 'digits' bits from pool of random bits.  If the number of random bits
+  in pool is insufficient, call rand() and add 31 bits to it.
+  
+  'digits' must be at most 16.
+
+  We assume that each call to rand() generates 31 bits, or RAND_MAX ==
+  2147483647.
+  
+  The underlying logic is flexible and endian-free.  The above conditions
+  can be relaxed.
+-----------------------------------------------------------------------------*/
+    static unsigned long int hold=0;  /* entropy pool */
+    static unsigned int len=0;        /* number of valid bits in pool */
+
+    unsigned int const mask = (1 << digits) - 1;
+
+    unsigned int retval;
+
+    assert(RAND_MAX == 2147483647 && digits <= 16);
+
+    retval = hold;  /* initial value */
+
+    if (len > digits) { /* Enough bits in hold to satisfy request */
+        hold >>= digits;
+        len   -= digits;
+    } else {              /* Load another 31 bits into hold */
+        hold    = rand(); 
+        retval |= (hold << len);
+        hold >>=  (digits - len);
+        len = 31 - digits + len;
+    }
+    return (retval & mask);
+}
+
+
 
 static void
-pgmnoise(FILE * const ofP,
+pgmnoise(FILE *       const ofP,
          unsigned int const cols,
          unsigned int const rows,
          gray         const maxval) {
 
+    bool const usingPool = !(RAND_MAX==2147483647 && (maxval & (maxval+1)));
+    unsigned int const bitLen = pm_maxvaltobits(maxval);
+
     unsigned int row;
     gray * destrow;
 
+    /* If maxval is 2^n-1, we draw exactly n bits from the pool.
+       Otherwise call rand() and determine gray value by modulo.
+
+       In the latter case, there is a miniscule skew toward 0 (=black)
+       because smaller numbers are produced more frequently by modulo.
+       Thus we employ the pool method only when it is certain that no
+       skew will ensue.
+
+       To illustrate the point, consider converting the outcome of one
+       roll of a fair, six-sided die to 5 values (0 to 4) by N % 5.  The
+       probability for values 1, 2, 3, 4 are 1/6, but 0 alone is 2/6.
+       Average is 10/6 or 1.6667, compared to 2.0 from an ideal
+       generator which produces exactly 5 values.  With two dice
+       average improves to 70/36 or 1.9444.
+
+       The more (distinct) dice we roll, or the more binary digits we
+       draw, the smaller the skew.
+    */
+
     destrow = pgm_allocrow(cols);
 
     pgm_writepgminit(ofP, cols, rows, maxval, 0);
 
     for (row = 0; row < rows; ++row) {
-        unsigned int col;
-        for (col = 0; col < cols; ++col)
-            destrow[col] = rand() % (maxval + 1);
-
+        if (usingPool) {
+            unsigned int col;
+            for (col = 0; col < cols; ++col)
+                destrow[col] = randPool(bitLen);
+        } 
+        else { 
+            unsigned int col;
+            for (col = 0; col < cols; ++col)
+                destrow[col] = rand() % (maxval + 1); 
+        }
         pgm_writepgmrow(ofP, destrow, cols, maxval, 0);
     }
 
@@ -97,8 +178,9 @@ pgmnoise(FILE * const ofP,
 
 
 
-int main(int          argc,
-         const char * argv[]) {
+int
+main(int          argc,
+     const char * argv[]) {
     
     struct cmdlineInfo cmdline;
 
@@ -108,8 +190,7 @@ int main(int          argc,
 
     srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
 
-    pgmnoise(stdout, cmdline.width, cmdline.height, PGM_MAXMAXVAL);
+    pgmnoise(stdout, cmdline.width, cmdline.height, cmdline.maxval);
 
     return 0;
 }
-
diff --git a/generator/pgmramp.c b/generator/pgmramp.c
index c0fb42b1..225542fe 100644
--- a/generator/pgmramp.c
+++ b/generator/pgmramp.c
@@ -16,7 +16,7 @@
 #include "pgm.h"
 #include "shhopt.h"
 
-enum ramptype {RT_LR, RT_TB, RT_RECT, RT_ELLIP};
+enum ramptype {RT_LR, RT_TB, RT_DIAG, RT_RECT, RT_ELLIP};
 
 
 struct cmdlineInfo {
@@ -39,7 +39,7 @@ parseCommandLine(int argc, char ** argv,
   program can use easily, struct cmdlineInfo.  Validate arguments along
   the way and exit program with message if invalid.
 
-  Note that some string information we return as *cmdlineP is in the storage 
+  Note that some string information we return as *cmdlineP is in the storage
   argv[] points to.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc(100*sizeof(optEntry));
@@ -47,13 +47,14 @@ parseCommandLine(int argc, char ** argv,
          */
     optStruct3 opt;
 
-    unsigned int lrSpec, tbSpec, rectangleSpec, ellipseSpec;
+    unsigned int lrSpec, tbSpec, diagonalSpec, rectangleSpec, ellipseSpec;
     unsigned int maxvalSpec;
     unsigned int option_def_index;
 
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0,   "lr",        OPT_FLAG, NULL,              &lrSpec,        0);
     OPTENT3(0,   "tb",        OPT_FLAG, NULL,              &tbSpec,        0);
+    OPTENT3(0,   "diagonal",  OPT_FLAG, NULL,              &diagonalSpec,  0);
     OPTENT3(0,   "rectangle", OPT_FLAG, NULL,              &rectangleSpec, 0);
     OPTENT3(0,   "ellipse",   OPT_FLAG, NULL,              &ellipseSpec,   0);
     OPTENT3(0,   "maxval",    OPT_UINT, &cmdlineP->maxval, &maxvalSpec,    0);
@@ -62,18 +63,23 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (lrSpec + tbSpec + rectangleSpec + ellipseSpec == 0)
-        pm_error("You must specify one of -lr, -tb, -rectangle, or -ellipse");
-    if (lrSpec + tbSpec + rectangleSpec + ellipseSpec > 1)
+    free (option_def);
+
+    if (lrSpec + tbSpec + diagonalSpec + rectangleSpec + ellipseSpec == 0)
+        pm_error("You must specify one of "
+                 "-lr, -tb, -diagonal, -rectangle, or -ellipse");
+    if (lrSpec + tbSpec + diagonalSpec + rectangleSpec + ellipseSpec > 1)
         pm_error("You may specify at most one of "
-                 "-lr, -tb, -rectangle, or -ellipse");
+                 "-lr, -tb, -diagonal, -rectangle, or -ellipse");
     if (lrSpec)
         cmdlineP->ramptype = RT_LR;
     else if (tbSpec)
         cmdlineP->ramptype = RT_TB;
+    else if (diagonalSpec)
+        cmdlineP->ramptype = RT_DIAG;
     else if (rectangleSpec)
         cmdlineP->ramptype = RT_RECT;
     else if (ellipseSpec)
@@ -87,10 +93,10 @@ parseCommandLine(int argc, char ** argv,
         if (cmdlineP->maxval > PGM_OVERALLMAXVAL)
             pm_error("The value you specified for -maxval (%u) is too big.  "
                      "Max allowed is %u", cmdlineP->maxval, PGM_OVERALLMAXVAL);
-        
+
         if (cmdlineP->maxval < 1)
             pm_error("You cannot specify 0 for -maxval");
-    }    
+    }
 
     if (argc-1 < 2)
         pm_error("Need two arguments: width and height.");
@@ -105,7 +111,7 @@ parseCommandLine(int argc, char ** argv,
 
 
 
-int 
+int
 main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
@@ -119,31 +125,36 @@ main(int argc, char *argv[]) {
 
     colso2 = MAX(1, cmdline.cols / 2);
     rowso2 = MAX(1, cmdline.rows / 2);
-    
+
     pgm_writepgminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0);
     grayrow = pgm_allocrow(cmdline.cols);
-    
+
     for (row = 0; row < cmdline.rows; ++row) {
         unsigned int col;
         for (col = 0; col < cmdline.cols; ++col) {
             switch (cmdline.ramptype) {
             case RT_LR:
-                grayrow[col] = 
-                    col * cmdline.maxval / MAX(cmdline.cols-1, 1);
+                /* Fill row buffer once.  All rows are identical. */
+                if (row == 0)
+                    grayrow[col] =
+                        (float) col * cmdline.maxval / MAX(cmdline.cols-1, 1);
                 break;
             case RT_TB:
-                grayrow[col] = 
-                    row * cmdline.maxval / MAX(cmdline.rows-1, 1);
+                grayrow[col] =
+                    (float) row * cmdline.maxval / MAX(cmdline.rows-1, 1);
+                break;
+            case RT_DIAG:
+                grayrow[col] =
+                    ((float) col + row) * cmdline.maxval /
+                        MAX((float) cmdline.cols + cmdline.rows-2, 1);
                 break;
-
             case RT_RECT: {
                 float const r = fabs((int)(rowso2 - row)) / rowso2;
                 float const c = fabs((int)(colso2 - col)) / colso2;
-                grayrow[col] = 
+                grayrow[col] =
                     cmdline.maxval - (r + c) / 2.0 * cmdline.maxval;
-            }
-            break;
-            
+            } break;
+
             case RT_ELLIP: {
                 float const r = fabs((int)(rowso2 - row)) / rowso2;
                 float const c = fabs((int)(colso2 - col)) / colso2;
@@ -153,12 +164,11 @@ main(int argc, char *argv[]) {
                 if ( v < 0.0 ) v = 0.0;
                 else if ( v > 1.0 ) v = 1.0;
                 grayrow[col] = cmdline.maxval - v * cmdline.maxval;
+            } break;
             }
-            break;
-            }
-	    }
+        }
         pgm_writepgmrow(stdout, grayrow, cmdline.cols, cmdline.maxval, 0);
-	}
+    }
 
     pgm_freerow(grayrow);
     pm_close(stdout);
diff --git a/generator/ppmcie.c b/generator/ppmcie.c
index fda0ab7c..717ed13b 100644
--- a/generator/ppmcie.c
+++ b/generator/ppmcie.c
@@ -26,6 +26,7 @@
   Introduced option to plot 1976 u' v' chromaticities.
 */
 
+#include <assert.h>
 #include <math.h>
 
 #include "pm_c_util.h"
@@ -34,10 +35,8 @@
 #include "nstring.h"
 
 #define CLAMP(v, l, h)  ((v) < (l) ? (l) : (v) > (h) ? (h) : (v))
-#define TRUE    1
-#define FALSE   0
 
-#define Maxval  255                   /* Maxval to use in generated pixmaps */
+pixval const cieMaxval = 255;  /* Maxval to use in generated pixmaps */
 
 /* A  color  system is defined by the CIE x and y  coordinates of its
    three primary illuminants and the x and y coordinates of the  white
@@ -462,8 +461,18 @@ gamma_correct_rgb(const struct colorSystem * const cs,
 
 
 
-#define Sz(x) (((x) * MIN(pixcols, pixrows)) / 512)
+/* Sz(X) is the displacement in pixels of a displacement of X normalized
+   distance units.  (A normalized distance unit is 1/512 of the smaller
+   dimension of the canvas)
+*/
+#define Sz(x) (((x) * (int)MIN(pixcols, pixrows)) / 512)
+
+/* B(X, Y) is a pair of function arguments (for a libppmd function) that
+   give a pixel position on the canvas.  That position is (X, Y), biased
+   horizontally 'xBias' pixels.
+*/
 #define B(x, y) ((x) + xBias), (y)
+
 #define Bixels(y, x) pixels[y][x + xBias]
 
 
@@ -511,12 +520,12 @@ makeAllBlack(pixel **     const pixels,
 
 static void
 drawTongueOutline(pixel ** const pixels,
-                  int    const pixcols,
-                  int    const pixrows,
-                  pixval const maxval,
-                  bool   const upvp,
-                  int    const xBias,
-                  int    const yBias) {
+                  int      const pixcols,
+                  int      const pixrows,
+                  pixval   const maxval,
+                  bool     const upvp,
+                  int      const xBias,
+                  int      const yBias) {
 
     int const pxcols = pixcols - xBias;
     int const pxrows = pixrows - yBias;
@@ -535,7 +544,7 @@ drawTongueOutline(pixel ** const pixels,
                                        &icx, &icy);
         
         if (wavelength > 380)
-            ppmd_line(pixels, pixcols, pixrows, Maxval,
+            ppmd_line(pixels, pixcols, pixrows, maxval,
                       B(lx, ly), B(icx, icy),
                       PPMD_NULLDRAWPROC, (char *) &rgbcolor);
         else {
@@ -574,12 +583,12 @@ findTongue(pixel ** const pixels,
          ++i);
 
     if (i >= pxcols)
-        *presentP = FALSE;
+        *presentP = false;
     else {
         int j;
         int const leftEdge = i;
 
-        *presentP = TRUE;
+        *presentP = true;
         
         for (j = pxcols - 1;
              j >= leftEdge && PPM_GETR(Bixels(row, j)) == 0;
@@ -641,16 +650,16 @@ fillInTongue(pixel **                   const pixels,
 
                 xyz_to_rgb(cs, cx, cy, cz, &jr, &jg, &jb);
 
-                mx = Maxval;
+                mx = maxval;
         
                 /* Check whether the requested color  is  within  the
                    gamut  achievable with the given color system.  If
                    not, draw it in a reduced  intensity,  interpolated
                    by desaturation to the closest within-gamut color. */
         
-                if (constrain_rgb(&jr, &jg, &jb)) {
-                    mx = highlightGamut ? Maxval : ((Maxval + 1) * 3) / 4;
-                }
+                if (constrain_rgb(&jr, &jg, &jb))
+                    mx = highlightGamut ? maxval : ((maxval + 1) * 3) / 4;
+
                 /* Scale to max(rgb) = 1. */
                 jmax = MAX(jr, MAX(jg, jb));
                 if (jmax > 0) {
@@ -672,67 +681,175 @@ fillInTongue(pixel **                   const pixels,
 
 
 static void
-drawAxes(pixel ** const pixels,
-         int    const pixcols,
-         int    const pixrows,
-         pixval const maxval,
-         bool   const upvp,
-         int    const xBias,
-         int    const yBias) {
+drawYAxis(pixel **     const pixels,
+          unsigned int const pixcols,
+          unsigned int const pixrows,
+          pixval       const maxval,
+          unsigned int const xBias,
+          unsigned int const yBias,
+          pixel        const axisColor) {
+              
+    unsigned int const pxrows = pixrows - yBias;
 
-    int const pxcols = pixcols - xBias;
-    int const pxrows = pixrows - yBias;
-
-    pixel rgbcolor;  /* Color of axes */
-    int i;
-
-    PPM_ASSIGN(rgbcolor, maxval, maxval, maxval);
     ppmd_line(pixels, pixcols, pixrows, maxval,
               B(0, 0), B(0, pxrows - 1), PPMD_NULLDRAWPROC,
-              (char *) &rgbcolor);
+              (char *) &axisColor);
+}
+
+
+
+static void
+drawXAxis(pixel **     const pixels,
+          unsigned int const pixcols,
+          unsigned int const pixrows,
+          pixval       const maxval,
+          unsigned int const xBias,
+          unsigned int const yBias,
+          pixel        const axisColor) {
+              
+    unsigned int const pxcols = pixcols - xBias;
+    unsigned int const pxrows = pixrows - yBias;
+
     ppmd_line(pixels, pixcols, pixrows, maxval,
               B(0, pxrows - 1), B(pxcols - 1, pxrows - 1),
-              PPMD_NULLDRAWPROC, (char *) &rgbcolor);
-    
-    /* Draw tick marks on X and Y axes every 0.1 units.  Also
-       label axes.
-    */
+              PPMD_NULLDRAWPROC, (char *) &axisColor);
+}
+
+
+
+static void
+tickX(pixel **     const pixels,
+      unsigned int const pixcols,
+      unsigned int const pixrows,
+      pixval       const maxval,
+      unsigned int const xBias,
+      unsigned int const yBias,
+      pixel        const axisColor,
+      unsigned int const tenth) {
+/*----------------------------------------------------------------------------
+   Put a tick mark 'tenth' tenths of the way along the X axis
+   and label it.
+
+   'pixels' is the canvas on which to draw it; its dimensions are
+   'pixcols' by 'pixrows' and has maxval 'maxval'.
+-----------------------------------------------------------------------------*/
+    unsigned int const pxcols = pixcols - xBias;
+    unsigned int const pxrows = pixrows - yBias;
+    unsigned int const tickCol = (tenth * (pxcols - 1)) / 10;
+        /* Pixel column where the left edge of the tick goes */
+    unsigned int const tickThickness = Sz(3);
+        /* Thickness of the tick in pixels */
+    unsigned int const tickBottom = pxrows - Sz(1);
+        /* Pixel row of the bottom of the tick */
+
+    char s[20];
+
+    assert(tenth < 10);
+
+    sprintf(s, "0.%u", tenth);
+    ppmd_line(pixels, pixcols, pixrows, maxval,
+              B(tickCol, tickBottom),
+              B(tickCol, tickBottom - tickThickness),
+              PPMD_NULLDRAWPROC, (char *) &axisColor);
+    ppmd_text(pixels, pixcols, pixrows, maxval,
+              B(tickCol - Sz(11), pxrows + Sz(12)),
+              Sz(10), 0, s, PPMD_NULLDRAWPROC, (char *) &axisColor);
+}
+
+
+
+static void
+tickY(pixel **     const pixels,
+      unsigned int const pixcols,
+      unsigned int const pixrows,
+      pixval       const maxval,
+      unsigned int const xBias,
+      unsigned int const yBias,
+      pixel        const axisColor,
+      unsigned int const tenth) {
+/*----------------------------------------------------------------------------
+   Put a tick mark 'tenth' tenths of the way along the Y axis and label it.
+
+   'pixels' is the canvas on which to draw it; its dimensions are
+   'pixcols' by 'pixrows' and has maxval 'maxval'.
+-----------------------------------------------------------------------------*/
+    unsigned int const pxrows = pixrows - yBias;
+    unsigned int const tickRow = (tenth * (pxrows - 1)) / 10;
+        /* Pixel row where the top of the tick goes */
+    unsigned int const tickThickness = Sz(3);
+        /* Thickness of the tick in pixels */
     
-    for (i = 1; i <= 9; i += 1) {
-        char s[20];
-
-        /* X axis tick */
-
-        sprintf(s, "0.%d", i);
-        ppmd_line(pixels, pixcols, pixrows, maxval,
-                  B((i * (pxcols - 1)) / 10, pxrows - Sz(1)),
-                  B((i * (pxcols - 1)) / 10, pxrows - Sz(4)),
-                  PPMD_NULLDRAWPROC, (char *) &rgbcolor);
-        ppmd_text(pixels, pixcols, pixrows, maxval,
-                  B((i * (pxcols - 1)) / 10 - Sz(11), pxrows + Sz(12)),
-                  Sz(10), 0, s, PPMD_NULLDRAWPROC, (char *) &rgbcolor);
-
-        /* Y axis tick */
-
-        sprintf(s, "0.%d", 10 - i);
-        ppmd_line(pixels, pixcols, pixrows, maxval,
-                  B(0, (i * (pxrows - 1)) / 10),
-                  B(Sz(3), (i * (pxrows - 1)) / 10),
-                  PPMD_NULLDRAWPROC, (char *) &rgbcolor);
-
-        ppmd_text(pixels, pixcols, pixrows, maxval,
-                  B(Sz(-30), (i * (pxrows - 1)) / 10 + Sz(5)),
-                  Sz(10), 0, s,
-                  PPMD_NULLDRAWPROC, (char *) &rgbcolor);
-    }
+    char s[20];
+
+    assert(tenth < 10);
+
+    sprintf(s, "0.%d", 10 - tenth);
+    ppmd_line(pixels, pixcols, pixrows, maxval,
+              B(0, tickRow), B(tickThickness, tickRow),
+              PPMD_NULLDRAWPROC, (char *) &axisColor);
+
+    ppmd_text(pixels, pixcols, pixrows, maxval,
+              B(Sz(-30), tickRow + Sz(5)),
+              Sz(10), 0, s,
+              PPMD_NULLDRAWPROC, (char *) &axisColor);
+}
+
+
+
+static void
+labelAxes(pixel **     const pixels,
+          unsigned int const pixcols,
+          unsigned int const pixrows,
+          pixval       const maxval,
+          unsigned int const xBias,
+          unsigned int const yBias,
+          pixel        const axisColor,
+          bool         const upvp) {
+/*----------------------------------------------------------------------------
+   Label the axes "x" and "y" or "u" and "v".
+-----------------------------------------------------------------------------*/
+    unsigned int const pxcols = pixcols - xBias;
+    unsigned int const pxrows = pixrows - yBias;
+
     ppmd_text(pixels, pixcols, pixrows, maxval,
               B((98 * (pxcols - 1)) / 100 - Sz(11), pxrows + Sz(12)),
               Sz(10), 0, (upvp ? "u'" : "x"),
-              PPMD_NULLDRAWPROC, (char *) &rgbcolor);
+              PPMD_NULLDRAWPROC, (char *) &axisColor);
     ppmd_text(pixels,  pixcols, pixrows, maxval,
               B(Sz(-22), (2 * (pxrows - 1)) / 100 + Sz(5)),
               Sz(10), 0, (upvp ? "v'" : "y"),
-              PPMD_NULLDRAWPROC, (char *) &rgbcolor);
+              PPMD_NULLDRAWPROC, (char *) &axisColor);
+}
+
+
+
+static void
+drawAxes(pixel **     const pixels,
+         unsigned int const pixcols,
+         unsigned int const pixrows,
+         pixval       const maxval,
+         bool         const upvp,
+         unsigned int const xBias,
+         unsigned int const yBias) {
+/*----------------------------------------------------------------------------
+   Draw the axes, with tick marks every .1 units and labels.
+-----------------------------------------------------------------------------*/
+    pixel axisColor;  /* Color of axes and labels */
+    unsigned int i;
+
+    PPM_ASSIGN(axisColor, maxval, maxval, maxval);
+
+    drawYAxis(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor);
+    drawXAxis(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor);
+
+    for (i = 1; i <= 9; i += 1) {
+        tickX(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor, i);
+
+        tickY(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor, i);
+    }
+
+    labelAxes(pixels, pixcols, pixrows, maxval, xBias, yBias, axisColor,
+              upvp);
 }
 
 
@@ -840,14 +957,14 @@ plotBlackBodyCurve(pixel **                   const pixels,
         }
 
         if (t > 1000) {
-            ppmd_line(pixels, pixcols, pixrows, Maxval,
+            ppmd_line(pixels, pixcols, pixrows, maxval,
                       B(lx, ly), B(xb, yb), PPMD_NULLDRAWPROC,
                       (char *) &rgbcolor);
 
             /* Draw tick mark every 1000 kelvins */
 
             if ((((int) t) % 1000) == 0) {
-                ppmd_line(pixels, pixcols, pixrows, Maxval,
+                ppmd_line(pixels, pixcols, pixrows, maxval,
                           B(lx, ly - Sz(2)), B(lx, ly + Sz(2)),
                           PPMD_NULLDRAWPROC, (char *) &rgbcolor);
 
@@ -859,7 +976,7 @@ plotBlackBodyCurve(pixel **                   const pixels,
                     char bb[20];
 
                     sprintf(bb, "%g", t);
-                    ppmd_text(pixels, pixcols, pixrows, Maxval,
+                    ppmd_text(pixels, pixcols, pixrows, maxval,
                               B(lx - Sz(12), ly - Sz(4)), Sz(6), 0, bb,
                               PPMD_NULLDRAWPROC, (char *) &rgbcolor);
                 }
@@ -938,7 +1055,7 @@ plotMonochromeWavelengths(
             PPM_ASSIGN(rgbcolor, maxval, maxval, maxval);
             tx = icx + ((x < 520) ? Sz(-2) : ((x >= 535) ? Sz(2) : 0));
             ty = icy + ((x < 520) ? 0 : ((x >= 535) ? Sz(-1) : Sz(-2))); 
-            ppmd_line(pixels, pixcols, pixrows, Maxval,
+            ppmd_line(pixels, pixcols, pixrows, maxval,
                       B(icx, icy), B(tx, ty),
                       PPMD_NULLDRAWPROC, (char *) &rgbcolor);
 
@@ -970,13 +1087,13 @@ plotMonochromeWavelengths(
             }
             /* gamma correct from linear rgb to nonlinear rgb. */
             gamma_correct_rgb(cs, &jr, &jg, &jb);
-            r = Maxval * jr;
-            g = Maxval * jg;
-            b = Maxval * jb;
+            r = maxval * jr;
+            g = maxval * jg;
+            b = maxval * jb;
             PPM_ASSIGN(rgbcolor, (pixval) r, (pixval) g, (pixval) b);
 
             sprintf(wl, "%d", x);
-            ppmd_text(pixels, pixcols, pixrows, Maxval,
+            ppmd_text(pixels, pixcols, pixrows, maxval,
                       B(icx + bx, icy + by), Sz(6), 0, wl,
                       PPMD_NULLDRAWPROC, (char *) &rgbcolor);
         }
@@ -997,18 +1114,18 @@ writeLabel(pixel **                   const pixels,
 
     PPM_ASSIGN(rgbcolor, maxval, maxval, maxval);
     
-    snprintfN(sysdesc, sizeof(sysdesc),
-              "System: %s\n"
-              "Primary illuminants (X, Y)\n"
-              "     Red:  %0.4f, %0.4f\n"
-              "     Green: %0.4f, %0.4f\n"
-              "     Blue:  %0.4f, %0.4f\n"
-              "White point (X, Y): %0.4f, %0.4f",
-              cs->name, cs->xRed, cs->yRed, cs->xGreen, cs->yGreen,
-              cs->xBlue, cs->yBlue, cs->xWhite, cs->yWhite);
+    pm_snprintf(sysdesc, sizeof(sysdesc),
+                "System: %s\n"
+                "Primary illuminants (X, Y)\n"
+                "     Red:  %0.4f, %0.4f\n"
+                "     Green: %0.4f, %0.4f\n"
+                "     Blue:  %0.4f, %0.4f\n"
+                "White point (X, Y): %0.4f, %0.4f",
+                cs->name, cs->xRed, cs->yRed, cs->xGreen, cs->yGreen,
+                cs->xBlue, cs->yBlue, cs->xWhite, cs->yWhite);
     sysdesc[sizeof(sysdesc)-1] = '\0';  /* for robustness */
 
-    ppmd_text(pixels, pixcols, pixrows, Maxval,
+    ppmd_text(pixels, pixcols, pixrows, maxval,
               pixcols / 3, Sz(24), Sz(12), 0, sysdesc,
               PPMD_NULLDRAWPROC, (char *) &rgbcolor);
 }
@@ -1016,8 +1133,8 @@ writeLabel(pixel **                   const pixels,
 
 
 int
-main(int argc,
-     char * argv[]) {
+main(int          argc,
+     const char * argv[]) {
 
     int argn;
     const char * const usage = "[-[no]black] [-[no]wpoint] [-[no]label] [-no[axes]] [-full]\n\
@@ -1027,24 +1144,24 @@ main(int argc,
 [-size <s>] [-xsize|-width <x>] [-ysize|-height <y>]";
     const struct colorSystem *cs;
 
-    int widspec = FALSE, hgtspec = FALSE;
-    int xBias, yBias;
-    int upvp = FALSE;             /* xy or u'v' color coordinates? */
-    int showWhite = TRUE;             /* Show white point ? */
-    int showBlack = TRUE;             /* Show black body curve ? */
-    int fullChart = FALSE;            /* Fill entire tongue ? */
-    int showLabel = TRUE;             /* Show labels ? */
-    int showAxes = TRUE;              /* Plot axes ? */
+    bool widspec = false, hgtspec = false;
+    unsigned int xBias, yBias;
+    bool upvp = false;             /* xy or u'v' color coordinates? */
+    bool showWhite = true;         /* Show white point ? */
+    bool showBlack = true;         /* Show black body curve ? */
+    bool fullChart = false;        /* Fill entire tongue ? */
+    bool showLabel = true;         /* Show labels ? */
+    bool showAxes = true;          /* Plot axes ? */
 
-    ppm_init(&argc, argv);
+    pm_proginit(&argc, argv);
     argn = 1;
 
     cs = &Rec709system;  /* default */
     while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') {
         if (pm_keymatch(argv[argn], "-xy", 2)) {
-            upvp = FALSE;
+            upvp = false;
         } else if (pm_keymatch(argv[argn], "-upvp", 1)) {
-            upvp = TRUE;
+            upvp = true;
         } else if (pm_keymatch(argv[argn], "-xsize", 1) ||
                    pm_keymatch(argv[argn], "-width", 2)) {
             if (widspec) {
@@ -1053,7 +1170,7 @@ main(int argc,
             argn++;
             if ((argn == argc) || (sscanf(argv[argn], "%d", &sxsize) != 1))
                 pm_usage(usage);
-            widspec = TRUE;
+            widspec = true;
         } else if (pm_keymatch(argv[argn], "-ysize", 1) ||
                    pm_keymatch(argv[argn], "-height", 2)) {
             if (hgtspec) {
@@ -1062,7 +1179,7 @@ main(int argc,
             argn++;
             if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1))
                 pm_usage(usage);
-            hgtspec = TRUE;
+            hgtspec = true;
         } else if (pm_keymatch(argv[argn], "-size", 2)) {
             if (hgtspec || widspec) {
                 pm_error("already specified a size/height/ysize");
@@ -1071,7 +1188,7 @@ main(int argc,
             if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1))
                 pm_usage(usage);
             sxsize = sysize;
-            hgtspec = widspec = TRUE;
+            hgtspec = widspec = true;
         } else if (pm_keymatch(argv[argn], "-rec709", 1)) {
             cs = &Rec709system;
         } else if (pm_keymatch(argv[argn], "-ntsc", 1)) {
@@ -1085,23 +1202,23 @@ main(int argc,
         } else if (pm_keymatch(argv[argn], "-cie", 1)) {
             cs = &CIEsystem;                 
         } else if (pm_keymatch(argv[argn], "-black", 3)) {
-            showBlack = TRUE;         /* Show black body curve */
+            showBlack = true;         /* Show black body curve */
         } else if (pm_keymatch(argv[argn], "-wpoint", 2)) {
-            showWhite = TRUE;         /* Show white point of color system */
+            showWhite = true;         /* Show white point of color system */
         } else if (pm_keymatch(argv[argn], "-noblack", 3)) {
-            showBlack = FALSE;        /* Don't show black body curve */
+            showBlack = false;        /* Don't show black body curve */
         } else if (pm_keymatch(argv[argn], "-nowpoint", 3)) {
-            showWhite = FALSE;        /* Don't show white point of system */
+            showWhite = false;        /* Don't show white point of system */
         } else if (pm_keymatch(argv[argn], "-label", 1)) {
-            showLabel = TRUE;         /* Show labels. */
+            showLabel = true;         /* Show labels. */
         } else if (pm_keymatch(argv[argn], "-nolabel", 3)) {
-            showLabel = FALSE;        /* Don't show labels */
+            showLabel = false;        /* Don't show labels */
         } else if (pm_keymatch(argv[argn], "-axes", 1)) {
-            showAxes = TRUE;          /* Show axes. */
+            showAxes = true;          /* Show axes. */
         } else if (pm_keymatch(argv[argn], "-noaxes", 3)) {
-            showAxes = FALSE;         /* Don't show axes */
+            showAxes = false;         /* Don't show axes */
         } else if (pm_keymatch(argv[argn], "-full", 1)) {
-            fullChart = TRUE;         /* Fill whole tongue full-intensity */
+            fullChart = true;         /* Fill whole tongue full-intensity */
         } else if (pm_keymatch(argv[argn], "-gamma", 2)) {
             cs = &Customsystem;
             argn++;
@@ -1170,32 +1287,32 @@ main(int argc,
 
     makeAllBlack(pixels, pixcols, pixrows);
 
-    drawTongueOutline(pixels, pixcols, pixrows, Maxval, upvp, xBias, yBias);
+    drawTongueOutline(pixels, pixcols, pixrows, cieMaxval, upvp, xBias, yBias);
 
-    fillInTongue(pixels, pixcols, pixrows, Maxval, cs, upvp, xBias, yBias,
+    fillInTongue(pixels, pixcols, pixrows, cieMaxval, cs, upvp, xBias, yBias,
                  fullChart);
 
     if (showAxes)
-        drawAxes(pixels, pixcols, pixrows, Maxval, upvp, xBias, yBias);
+        drawAxes(pixels, pixcols, pixrows, cieMaxval, upvp, xBias, yBias);
 
     if (showWhite)
-        plotWhitePoint(pixels, pixcols, pixrows, Maxval,
+        plotWhitePoint(pixels, pixcols, pixrows, cieMaxval,
                        cs, upvp, xBias, yBias);
 
     if (showBlack)
-        plotBlackBodyCurve(pixels, pixcols, pixrows, Maxval,
+        plotBlackBodyCurve(pixels, pixcols, pixrows, cieMaxval,
                            upvp, xBias, yBias);
 
     /* Plot wavelengths around periphery of the tongue. */
 
     if (showAxes)
-        plotMonochromeWavelengths(pixels, pixcols, pixrows, Maxval,
+        plotMonochromeWavelengths(pixels, pixcols, pixrows, cieMaxval,
                                   cs, upvp, xBias, yBias);
 
     if (showLabel)
-        writeLabel(pixels, pixcols, pixrows, Maxval, cs);
+        writeLabel(pixels, pixcols, pixrows, cieMaxval, cs);
 
-    ppm_writeppm(stdout, pixels, pixcols, pixrows, Maxval, FALSE);
+    ppm_writeppm(stdout, pixels, pixcols, pixrows, cieMaxval, 0);
 
     return 0;
 }
diff --git a/generator/ppmcolors.c b/generator/ppmcolors.c
index ecae2285..701812d1 100644
--- a/generator/ppmcolors.c
+++ b/generator/ppmcolors.c
@@ -45,7 +45,7 @@ parseCommandLine(int argc, char ** argv, struct cmdlineInfo *cmdlineP) {
     /* defaults */
     cmdlineP->maxval = 5;
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (cmdlineP->maxval < 1)
@@ -72,15 +72,16 @@ main(int argc, char *argv[]) {
 
     parseCommandLine(argc, argv, &cmdline);
 
-    asprintfN(&cmd, "pamseq 3 %u -tupletype=RGB | pamtopnm", cmdline.maxval);
+    pm_asprintf(&cmd, "pamseq 3 %u -tupletype=RGB | pamtopnm", cmdline.maxval);
 
     rc = system(cmd);
 
     if (rc != 0) 
         pm_error("pamseq|pamtopnm pipeline failed.  system() rc = %d", rc);
 
-    strfree(cmd);
-    exit(rc);
+    pm_strfree(cmd);
+
+    return rc;
 }
 
 
diff --git a/generator/ppmforge.c b/generator/ppmforge.c
index c77076e3..8ea86429 100644
--- a/generator/ppmforge.c
+++ b/generator/ppmforge.c
@@ -39,12 +39,9 @@
 #include "pm_c_util.h"
 #include "ppm.h"
 #include "mallocvar.h"
+#include "shhopt.h"
 
-#ifdef VMS
-static double const hugeVal = HUGE_VAL;
-#else
 static double const hugeVal = 1e50;
-#endif
 
 /* Definitions used to address real and imaginary parts in a two-dimensional
    array of complex numbers as stored by fourn(). */
@@ -78,12 +75,6 @@ static double arand, gaussadd, gaussfac; /* Gaussian random parameters */
 static double fracdim;            /* Fractal dimension */
 static double powscale;           /* Power law scaling exponent */
 static int meshsize = 256;        /* FFT mesh size */
-static unsigned int seedarg;        /* Seed specified by user */
-static bool seedspec = FALSE;      /* Did the user specify a seed ? */
-static bool clouds = FALSE;        /* Just generate clouds */
-static bool stars = FALSE;         /* Just generate stars */
-static int screenxsize = 256;         /* Screen X size */
-static int screenysize = 256;         /* Screen Y size */
 static double inclangle, hourangle;   /* Star position relative to planet */
 static bool inclspec = FALSE;      /* No inclination specified yet */
 static bool hourspec = FALSE;      /* No hour specified yet */
@@ -92,6 +83,166 @@ static double glaciers;           /* Glacier level */
 static int starfraction;          /* Star fraction */
 static int starcolor;            /* Star color saturation */
 
+
+struct CmdlineInfo {
+    unsigned int clouds;
+    unsigned int night;
+    float        dimension;
+    float        hourAngle;
+    unsigned int hourSpec;
+    float        inclAngle;
+    unsigned int inclinationSpec;
+    unsigned int meshSize;
+    unsigned int meshSpec;
+    float        power;
+    float        glaciers;
+    float        ice;
+    int          saturation;
+    unsigned int seed;
+    int          stars;
+    unsigned int starsSpec;
+    unsigned int width;
+    unsigned int height;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char **argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+  Convert program invocation arguments (argc,argv) into a format the 
+  program can use easily, struct cmdlineInfo.  Validate arguments along
+  the way and exit program with message if invalid.
+
+  Note that some string information we return as *cmdlineP is in the storage 
+  argv[] points to.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int dimensionSpec, seedSpec,
+        meshSpec, powerSpec, glaciersSpec, iceSpec, saturationSpec,
+        starsSpec, widthSpec, heightSpec;
+    float hour;
+    float inclination;
+    unsigned int mesh;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;
+    OPTENT3(0, "clouds",      OPT_FLAG, NULL, &cmdlineP->clouds, 0);
+    OPTENT3(0, "night",       OPT_FLAG, NULL, &cmdlineP->night, 0);
+    OPTENT3(0, "dimension",   OPT_FLOAT, &cmdlineP->dimension,
+            &dimensionSpec, 0);
+    OPTENT3(0, "hour",        OPT_FLOAT, &hour,
+            &cmdlineP->hourSpec, 0);
+    OPTENT3(0, "inclination", OPT_FLOAT, &inclination,
+            &cmdlineP->inclinationSpec, 0);
+    OPTENT3(0, "tilt",        OPT_FLOAT, &inclination,
+            &cmdlineP->inclinationSpec, 0);
+    OPTENT3(0, "mesh",        OPT_UINT, &mesh,
+            &meshSpec, 0);
+    OPTENT3(0, "power",       OPT_FLOAT, &cmdlineP->power,
+            &powerSpec, 0);
+    OPTENT3(0, "glaciers",    OPT_FLOAT, &cmdlineP->glaciers,
+            &glaciersSpec, 0);
+    OPTENT3(0, "ice",         OPT_FLOAT, &cmdlineP->ice,
+            &iceSpec, 0);
+    OPTENT3(0, "saturation",  OPT_INT,   &cmdlineP->saturation,
+            &saturationSpec, 0);
+    OPTENT3(0, "seed",        OPT_UINT,  &cmdlineP->seed,
+            &seedSpec, 0);
+    OPTENT3(0, "stars",       OPT_INT,   &cmdlineP->stars,
+            &starsSpec, 0);
+    OPTENT3(0, "width",       OPT_UINT,  &cmdlineP->width,
+            &widthSpec, 0);
+    OPTENT3(0, "xsize",       OPT_UINT,  &cmdlineP->width,
+            &widthSpec, 0);
+    OPTENT3(0, "height",      OPT_UINT,  &cmdlineP->height,
+            &heightSpec, 0);
+    OPTENT3(0, "ysize",       OPT_UINT,  &cmdlineP->height,
+            &heightSpec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (dimensionSpec) {
+        if (cmdlineP->dimension <= 0.0)
+            pm_error("-dimension must be greater than zero.  "
+                     "You specified %f", cmdlineP->dimension);
+    } else
+        cmdlineP->dimension = cmdlineP->clouds ? 2.15 : 2.4;
+
+    if (cmdlineP->hourSpec)
+        cmdlineP->hourAngle = (M_PI / 12.0) * (hour + 12.0);
+
+    if (cmdlineP->inclinationSpec)
+        cmdlineP->inclAngle = (M_PI / 180.0) * inclination;
+
+    if (meshSpec) {
+        unsigned int i;
+        if (mesh < 2)
+            pm_error("-mesh value must be at least 2.  "
+                     "You specified %u", mesh);
+        /* Force FFT mesh to the next larger power of 2. */
+        for (i = 2; i < mesh; i <<= 1);
+        cmdlineP->meshSize = i;
+    } else
+        cmdlineP->meshSize = 256;
+
+    if (powerSpec) {
+        if (cmdlineP->power <= 0.0)
+            pm_error("-power must be greater than zero.  "
+                     "You specified %f", cmdlineP->power);
+    } else
+        cmdlineP->power = cmdlineP->clouds ? 0.75 : 1.2;
+
+    if (iceSpec) {
+        if (cmdlineP->ice <= 0.0)
+            pm_error("-ice must be greater than zero.  "
+                     "You specified %f", cmdlineP->ice);
+    } else
+        cmdlineP->ice = 0.4;
+
+    if (glaciersSpec) {
+        if (cmdlineP->glaciers <= 0.0)
+            pm_error("-glaciers must be greater than 0.  "
+                     "You specified %f", cmdlineP->glaciers);
+    } else
+        cmdlineP->glaciers = 0.75;
+
+    if (!starsSpec)
+        cmdlineP->stars = 100;
+
+    if (!saturationSpec)
+        cmdlineP->saturation = 125;
+
+    if (!seedSpec)
+        cmdlineP->seed = pm_randseed();
+
+    if (!widthSpec)
+        cmdlineP->width = 256;
+
+    if (!heightSpec)
+        cmdlineP->height = 256;
+
+    if (argc-1 > 0)
+        pm_error("There are no non-option arguments.  "
+                 "You specified %u", argc-1);
+
+    free(option_def);
+}
+
+
 /*  FOURN  --  Multi-dimensional fast Fourier transform
 
     Called with arguments:
@@ -278,19 +429,6 @@ static void spectralsynth(x, n, h)
 }
 
 
-static unsigned int
-initseed(void) {
-    /*  Generate initial random seed.  */
-
-    unsigned int i;
-
-    srand(pm_randseed());
-    for (i = 0; i < 7; ++i)
-        rand();
-    return rand();
-}
-
-
 
 /*  TEMPRGB  --  Calculate the relative R, G, and B components  for  a
          black  body  emitting  light  at a given temperature.
@@ -421,7 +559,8 @@ makeCp(float *      const a,
 
 
 static void
-createPlanetStuff(float *          const a,
+createPlanetStuff(bool             const clouds,
+                  float *          const a,
                   unsigned int     const n,
                   double **        const uP,
                   double **        const u1P,
@@ -460,7 +599,9 @@ createPlanetStuff(float *          const a,
             "        -inclination %.0f -hour %d -ice %.2f -glaciers %.2f",
             (siang * (180.0 / M_PI)),
             (int) (((shang * (12.0 / M_PI)) + 12 +
-                    (flipped ? 12 : 0)) + 0.5) % 24, icelevel, glaciers);
+                    (flipped ? 12 : 0)) + 0.5) % 24,
+            icelevel,
+            glaciers);
         pm_message("        -stars %d -saturation %d.",
                    starfraction, starcolor);
     }
@@ -535,9 +676,9 @@ generateCloudRow(pixel *         const pixels,
 
     /* Render the FFT output as clouds. */
 
-    unsigned int j;
+    unsigned int col;
 
-    for (j = 0; j < cols; j++) {
+    for (col = 0; col < cols; ++col) {
         double r;
         pixval w;
         
@@ -546,15 +687,15 @@ generateCloudRow(pixel *         const pixels,
            referenced below does not exist.
         */
         if (t1 > 0.0)
-            r += t1 * u1[j] * cp[byf + bxf[j]] +
-                t1 * u[j]  * cp[byf + bxc[j]];
+            r += t1 * u1[col] * cp[byf + bxf[col]] +
+                t1 * u[col]  * cp[byf + bxc[col]];
         if (t > 0.0)
-            r += t * u1[j] * cp[byc + bxf[j]] +
-                t * u[j]  * cp[byc + bxc[j]];
+            r += t * u1[col] * cp[byc + bxf[col]] +
+                t * u[col]  * cp[byc + bxc[col]];
         
         w = (r > 127.0) ? (maxval * ((r - 127.0) / 128.0)) : 0;
         
-        PPM_ASSIGN(*(pixels + j), w, w, maxval);
+        PPM_ASSIGN(pixels[col], w, w, maxval);
     }
 }
 
@@ -823,7 +964,7 @@ genplanet(bool         const stars,
         pm_message("%s: -seed %d -dimension %.2f -power %.2f -mesh %d",
                    clouds ? "clouds" : "planet",
                    rseed, fracdim, powscale, meshsize);
-        createPlanetStuff(a, n, &u, &u1, &bxf, &bxc, &cp, &sunvec, 
+        createPlanetStuff(clouds, a, n, &u, &u1, &bxf, &bxc, &cp, &sunvec, 
                           cols, maxval);
     }
 
@@ -944,19 +1085,14 @@ static bool
 planet(unsigned int const cols,
        unsigned int const rows,
        bool         const stars,
-       bool         const clouds) {
+       bool         const clouds,
+       unsigned int const rseed) {
 /*----------------------------------------------------------------------------
    Make a planet.
 -----------------------------------------------------------------------------*/
     float * a;
     bool error;
-    unsigned int rseed;        /* Current random seed */
-    
-    if (seedspec)
-        rseed = seedarg;
-    else 
-        rseed = initseed();
-    
+
     initgauss(rseed);
     
     if (stars) {
@@ -985,200 +1121,39 @@ planet(unsigned int const cols,
 
 
 
-
 int 
-main(int argc, char ** argv) {
+main(int argc, const char ** argv) {
 
+    struct CmdlineInfo cmdline;
     bool success;
-    int i;
-    const char * const usage = "\n\
-[-width|-xsize <x>] [-height|-ysize <y>] [-mesh <n>]\n\
-[-clouds] [-dimension <f>] [-power <f>] [-seed <n>]\n\
-[-hour <f>] [-inclination|-tilt <f>] [-ice <f>] [-glaciers <f>]\n\
-[-night] [-stars <n>] [-saturation <n>]";
-    bool dimspec = FALSE, meshspec = FALSE, powerspec = FALSE,
-        widspec = FALSE, hgtspec = FALSE, icespec = FALSE,
-        glacspec = FALSE, starspec = FALSE, starcspec = FALSE;
-
-    int cols, rows;     /* Dimensions of our output image */
-
-    ppm_init(&argc, argv);
-    i = 1;
-    
-    while ((i < argc) && (argv[i][0] == '-') && (argv[i][1] != '\0')) {
-
-        if (pm_keymatch(argv[i], "-clouds", 2)) {
-            clouds = TRUE;
-        } else if (pm_keymatch(argv[i], "-night", 2)) {
-            stars = TRUE;
-        } else if (pm_keymatch(argv[i], "-dimension", 2)) {
-            if (dimspec) {
-                pm_error("already specified a dimension");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &fracdim)  != 1))
-                pm_usage(usage);
-            if (fracdim <= 0.0) {
-                pm_error("fractal dimension must be greater than 0");
-            }
-            dimspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-hour", 3)) {
-            if (hourspec) {
-                pm_error("already specified an hour");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &hourangle) != 1))
-                pm_usage(usage);
-            hourangle = (M_PI / 12.0) * (hourangle + 12.0);
-            hourspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-inclination", 3) ||
-                   pm_keymatch(argv[i], "-tilt", 2)) {
-            if (inclspec) {
-                pm_error("already specified an inclination/tilt");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &inclangle) != 1))
-                pm_usage(usage);
-            inclangle = (M_PI / 180.0) * inclangle;
-            inclspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-mesh", 2)) {
-            unsigned int j;
 
-            if (meshspec) {
-                pm_error("already specified a mesh size");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &meshsize) != 1))
-                pm_usage(usage);
+    unsigned int cols, rows;     /* Dimensions of our output image */
 
-            if (meshsize < 2)
-                pm_error("mesh must be at least 2");
+    pm_proginit(&argc, argv);
 
-            /* Force FFT mesh to the next larger power of 2. */
+    parseCommandLine(argc, argv, &cmdline);
 
-            for (j = meshsize; (j & 1) == 0; j >>= 1) ;
-
-            if (j != 1) {
-                for (j = 2; j < meshsize; j <<= 1) ;
-                meshsize = j;
-            }
-            meshspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-power", 2)) {
-            if (powerspec) {
-                pm_error("already specified a power factor");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &powscale) != 1))
-                pm_usage(usage);
-            if (powscale <= 0.0) {
-                pm_error("power factor must be greater than 0");
-            }
-            powerspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-ice", 3)) {
-            if (icespec) {
-                pm_error("already specified ice cap level");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &icelevel) != 1))
-                pm_usage(usage);
-            if (icelevel <= 0.0) {
-                pm_error("ice cap level must be greater than 0");
-            }
-            icespec = TRUE;
-        } else if (pm_keymatch(argv[i], "-glaciers", 2)) {
-            if (glacspec) {
-                pm_error("already specified glacier level");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%lf", &glaciers) != 1))
-                pm_usage(usage);
-            if (glaciers <= 0.0) {
-                pm_error("glacier level must be greater than 0");
-            }
-            glacspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-stars", 3)) {
-            if (starspec) {
-                pm_error("already specified a star fraction");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &starfraction) != 1))
-                pm_usage(usage);
-            starspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-saturation", 3)) {
-            if (starcspec) {
-                pm_error("already specified a star color saturation");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &starcolor) != 1))
-                pm_usage(usage);
-            starcspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-seed", 3)) {
-            if (seedspec) {
-                pm_error("already specified a random seed");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%u", &seedarg) != 1))
-                pm_usage(usage);
-            seedspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-xsize", 2) ||
-                   pm_keymatch(argv[i], "-width", 2)) {
-            if (widspec) {
-                pm_error("already specified a width/xsize");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &screenxsize) != 1))
-                pm_usage(usage);
-            widspec = TRUE;
-        } else if (pm_keymatch(argv[i], "-ysize", 2) ||
-                   pm_keymatch(argv[i], "-height", 3)) {
-            if (hgtspec) {
-                pm_error("already specified a height/ysize");
-            }
-            i++;
-            if ((i == argc) || (sscanf(argv[i], "%d", &screenysize) != 1))
-                pm_usage(usage);
-            hgtspec = TRUE;
-        } else {
-            pm_usage(usage);
-        }
-        i++;
-    }
-
-
-    /* Set defaults when explicit specifications were not given.
-
-       The  default  fractal  dimension  and  power  scale depend upon
-       whether we are generating a planet or clouds. 
-    */
-    
-    if (!dimspec) {
-        fracdim = clouds ? 2.15 : 2.4;
-    }
-    if (!powerspec) {
-        powscale = clouds ? 0.75 : 1.2;
-    }
-    if (!icespec) {
-        icelevel = 0.4;
-    }
-    if (!glacspec) {
-        glaciers = 0.75;
-    }
-    if (!starspec) {
-        starfraction = 100;
-    }
-    if (!starcspec) {
-        starcolor = 125;
-    }
+    fracdim      = cmdline.dimension;
+    hourspec     = cmdline.hourSpec;
+    hourangle    = cmdline.hourAngle;
+    inclspec     = cmdline.inclinationSpec;
+    inclangle    = cmdline.inclAngle;
+    meshsize     = cmdline.meshSize;
+    powscale     = cmdline.power;
+    icelevel     = cmdline.ice;
+    glaciers     = cmdline.glaciers;
+    starfraction = cmdline.stars;
+    starcolor    = cmdline.saturation;
 
     /* Force  screen to be at least  as wide as it is high.  Long,
        skinny screens  cause  crashes  because  picture  width  is
        calculated based on height.  
     */
 
-    cols = (MAX(screenysize, screenxsize) + 1) & (~1);
-    rows = screenysize;
+    cols = (MAX(cmdline.height, cmdline.width) + 1) & (~1);
+    rows = cmdline.height;
 
-    success = planet(cols, rows, stars, clouds);
+    success = planet(cols, rows, cmdline.night, cmdline.clouds, cmdline.seed);
 
     exit(success ? 0 : 1);
 }
diff --git a/generator/ppmmake.c b/generator/ppmmake.c
index e59b47d5..2d4bbca8 100644
--- a/generator/ppmmake.c
+++ b/generator/ppmmake.c
@@ -55,19 +55,21 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = false;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = false;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
+    free (option_def);
+
     if (!maxvalSpec)
         cmdlineP->maxval = PPM_MAXMAXVAL;
     else {
         if (cmdlineP->maxval > PPM_OVERALLMAXVAL)
             pm_error("The value you specified for -maxval (%u) is too big.  "
                      "Max allowed is %u", cmdlineP->maxval, PPM_OVERALLMAXVAL);
-        
+
         if (cmdlineP->maxval < 1)
             pm_error("You cannot specify 0 for -maxval");
-    }    
+    }
 
     if (argc-1 < 3)
         pm_error("Need 3 arguments: color, width, height.");
@@ -88,7 +90,7 @@ main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
     pixel * pixrow;
-    unsigned int row;
+    unsigned int row, col;
 
     ppm_init(&argc, argv);
 
@@ -97,12 +99,12 @@ main(int argc, char *argv[]) {
     ppm_writeppminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0);
     pixrow = ppm_allocrow(cmdline.cols);
 
-    for (row = 0; row < cmdline.rows; ++row) {
-        unsigned int col;
-        for (col = 0; col < cmdline.cols; ++col)
-            pixrow[col] = cmdline.color;
+    /* All rows are identical.  Fill once. */
+    for (col = 0; col < cmdline.cols; ++col)
+        pixrow[col] = cmdline.color;
+
+    for (row = 0; row < cmdline.rows; ++row)
         ppm_writeppmrow(stdout, pixrow, cmdline.cols, cmdline.maxval, 0);
-	}
 
     ppm_freerow(pixrow);
     pm_close(stdout);
diff --git a/generator/ppmpat.c b/generator/ppmpat.c
index 772fa51d..fe1a1d27 100644
--- a/generator/ppmpat.c
+++ b/generator/ppmpat.c
@@ -41,6 +41,8 @@ struct cmdlineInfo {
     pattern basePattern;
     unsigned int width;
     unsigned int height;
+    unsigned int randomseed;
+    unsigned int randomseedSpec;
 };
 
 
@@ -71,22 +73,34 @@ parseCommandLine(int argc, const char ** argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0, "gingham2",  OPT_FLAG,   NULL, &gingham2,   0);
-    OPTENT3(0, "g2",        OPT_FLAG,   NULL, &gingham2,   0);
-    OPTENT3(0, "gingham3",  OPT_FLAG,   NULL, &gingham3,   0);
-    OPTENT3(0, "g3",        OPT_FLAG,   NULL, &gingham3,   0);
-    OPTENT3(0, "madras",    OPT_FLAG,   NULL, &madras,     0);
-    OPTENT3(0, "tartan",    OPT_FLAG,   NULL, &tartan,     0);
-    OPTENT3(0, "poles",     OPT_FLAG,   NULL, &poles,      0);
-    OPTENT3(0, "squig",     OPT_FLAG,   NULL, &squig,      0);
-    OPTENT3(0, "camo",      OPT_FLAG,   NULL, &camo,       0);
-    OPTENT3(0, "anticamo",  OPT_FLAG,   NULL, &anticamo,   0);
+    OPTENT3(0, "gingham2",      OPT_FLAG,   NULL,
+            &gingham2,   0);
+    OPTENT3(0, "g2",            OPT_FLAG,   NULL,
+            &gingham2,   0);
+    OPTENT3(0, "gingham3",      OPT_FLAG,   NULL,
+            &gingham3,   0);
+    OPTENT3(0, "g3",            OPT_FLAG,   NULL,
+            &gingham3,   0);
+    OPTENT3(0, "madras",        OPT_FLAG,   NULL,
+            &madras,     0);
+    OPTENT3(0, "tartan",        OPT_FLAG,   NULL,
+            &tartan,     0);
+    OPTENT3(0, "poles",         OPT_FLAG,   NULL,
+            &poles,      0);
+    OPTENT3(0, "squig",         OPT_FLAG,   NULL,
+            &squig,      0);
+    OPTENT3(0, "camo",          OPT_FLAG,   NULL,
+            &camo,       0);
+    OPTENT3(0, "anticamo",      OPT_FLAG,   NULL,
+            &anticamo,   0);
+    OPTENT3(0, "randomseed",    OPT_UINT,   &cmdlineP->randomseed,
+            &cmdlineP->randomseedSpec,      0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     basePatternCount =
@@ -136,6 +150,7 @@ parseCommandLine(int argc, const char ** argv,
         if (cmdlineP->height < 1)
             pm_error("Height must be at least 1 pixel");
     }
+    free(option_def);
 }
 
 
@@ -1140,7 +1155,8 @@ main(int argc, const char ** argv) {
 
     validateComputableDimensions(cmdline.width, cmdline.height);
     
-    srand(pm_randseed());
+    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
+
     pixels = ppm_allocarray(cmdline.width, cmdline.height);
 
     switch (cmdline.basePattern) {
@@ -1188,3 +1204,5 @@ main(int argc, const char ** argv) {
     return 0;
 }
 
+
+
diff --git a/generator/ppmrainbow b/generator/ppmrainbow
index 96e304ac..c0568d9b 100755
--- a/generator/ppmrainbow
+++ b/generator/ppmrainbow
@@ -1,4 +1,28 @@
-#!/usr/bin/perl -wl
+#!/bin/sh
+
+##############################################################################
+# This is essentially a Perl program.  We exec the Perl interpreter specifying
+# this same file as the Perl program and use the -x option to cause the Perl
+# interpreter to skip down to the Perl code.  The reason we do this instead of
+# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is
+# that the user may have multiple Perl interpreters and the one he wants to
+# use is properly located in the PATH.  The user's choice of Perl interpreter
+# may be crucial, such as when the user also has a PERL5LIB environment
+# variable and it selects modules that work with only a certain main
+# interpreter program.
+#
+# An alternative some people use is to have /usr/bin/env as the script
+# interpreter.  We don't do that because we think the existence and
+# compatibility of /bin/sh is more reliable.
+#
+# Note that we aren't concerned about efficiency because the user who needs
+# high efficiency can use directly the programs that this program invokes.
+#
+##############################################################################
+
+exec perl -w -x -S -- "$0" "$@"
+
+#!/usr/bin/perl
 use strict;
 use Getopt::Long;
 
diff --git a/generator/ppmrough.c b/generator/ppmrough.c
index b21adedf..e749c9c2 100644
--- a/generator/ppmrough.c
+++ b/generator/ppmrough.c
@@ -15,6 +15,7 @@
 #include <sys/time.h>
 
 #include "pm_c_util.h"
+#include "mallocvar.h"
 #include "shhopt.h"
 #include "ppm.h"
 
@@ -22,274 +23,309 @@ static pixel** PIX;
 static pixval BG_RED, BG_GREEN, BG_BLUE;
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
   /* All the information the user supplied in the command line,
      in a form easy for the program to use.
   */
-  unsigned int left, right, top, bottom;
-  unsigned int width, height, var;
-  const char *bg_rgb;
-  const char *fg_rgb;
-  unsigned init;
-  unsigned int verbose;
+    unsigned int left, right, top, bottom;
+    unsigned int width, height, var;
+    const char * bg_rgb;
+    const char * fg_rgb;
+    unsigned int randomseed;
+    unsigned int randomseedSpec;
+    unsigned int verbose;
 };
 
 
 static void
-/*-------------------------------------------------------------------------- */
-   parseCommandLine(int argc, char ** argv, struct cmdlineInfo *cmdlineP)
-/*-------------------------------------------------------------------------- */
-{
-  optEntry *option_def = malloc(100*sizeof(optEntry));
-    /* Instructions to OptParseOptions2 on how to parse our options.    */
-  optStruct3 opt;
-
-  unsigned int option_def_index;
-
-  option_def_index = 0;   /* incremented by OPTENTRY */
-  OPTENT3(0, "width",   OPT_UINT,   &cmdlineP->width,   NULL, 0);
-  OPTENT3(0, "height",  OPT_UINT,   &cmdlineP->height,  NULL, 0);
-  OPTENT3(0, "left",    OPT_UINT,   &cmdlineP->left,    NULL, 0);
-  OPTENT3(0, "right",   OPT_UINT,   &cmdlineP->right,   NULL, 0);
-  OPTENT3(0, "top",     OPT_UINT,   &cmdlineP->top,     NULL, 0);
-  OPTENT3(0, "bottom",  OPT_UINT,   &cmdlineP->bottom,  NULL, 0);
-  OPTENT3(0, "bg",      OPT_STRING, &cmdlineP->bg_rgb,  NULL, 0);
-  OPTENT3(0, "fg",      OPT_STRING, &cmdlineP->fg_rgb,  NULL, 0);
-  OPTENT3(0, "var",     OPT_UINT,   &cmdlineP->var,     NULL, 0);
-  OPTENT3(0, "init",    OPT_UINT,   &cmdlineP->init,    NULL, 0);
-  OPTENT3(0, "verbose", OPT_FLAG,   NULL, &cmdlineP->verbose, 0);
-
-  /* Set the defaults */
-  cmdlineP->width = 100;
-  cmdlineP->height = 100;
-  cmdlineP->left = cmdlineP->right = cmdlineP->top = cmdlineP->bottom = -1;
-  cmdlineP->bg_rgb = NULL;
-  cmdlineP->fg_rgb = NULL;
-  cmdlineP->var = 10;
-
-  opt.opt_table = option_def;
-  opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-  opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
-
-  optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
-
-  if (argc-1 != 0)
-    pm_error("There are no arguments.  You specified %d.", argc-1);
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
+
+    optEntry * option_def;
+        /* Instructions to OptParseOptions2 on how to parse our options.    */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "width",       OPT_UINT,   &cmdlineP->width,   NULL, 0);
+    OPTENT3(0, "height",      OPT_UINT,   &cmdlineP->height,  NULL, 0);
+    OPTENT3(0, "left",        OPT_UINT,   &cmdlineP->left,    NULL, 0);
+    OPTENT3(0, "right",       OPT_UINT,   &cmdlineP->right,   NULL, 0);
+    OPTENT3(0, "top",         OPT_UINT,   &cmdlineP->top,     NULL, 0);
+    OPTENT3(0, "bottom",      OPT_UINT,   &cmdlineP->bottom,  NULL, 0);
+    OPTENT3(0, "bg",          OPT_STRING, &cmdlineP->bg_rgb,  NULL, 0);
+    OPTENT3(0, "fg",          OPT_STRING, &cmdlineP->fg_rgb,  NULL, 0);
+    OPTENT3(0, "var",         OPT_UINT,   &cmdlineP->var,     NULL, 0);
+    OPTENT3(0, "randomseed",  OPT_UINT,   &cmdlineP->randomseed,
+            &cmdlineP->randomseedSpec, 0);
+    OPTENT3(0, "init",        OPT_UINT,   &cmdlineP->randomseed,
+            &cmdlineP->randomseedSpec, 0);
+    OPTENT3(0, "verbose",     OPT_FLAG,   NULL, &cmdlineP->verbose, 0);
+
+    /* Set the defaults */
+    cmdlineP->width = 100;
+    cmdlineP->height = 100;
+    cmdlineP->left = cmdlineP->right = cmdlineP->top = cmdlineP->bottom = -1;
+    cmdlineP->bg_rgb = NULL;
+    cmdlineP->fg_rgb = NULL;
+    cmdlineP->var = 10;
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+
+    if (argc-1 != 0)
+        pm_error("There are no arguments.  You specified %d.", argc-1);
+
+    free(option_def);
 }
 
+
+
 static void
-/* ----------------------------------------- */
-   proc_left(int const r1, int const r2, int const c1, int const c2, 
-             unsigned int const var)
-/* ----------------------------------------- */
-{
-  int cm, rm, c;
-
-  if (r1 + 1 == r2)  return;
-  rm = (r1 + r2) >> 1;
-  cm = (c1 + c2) >> 1;
-  cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
-
-  for (c = 0; c < cm; c++)
-    PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE);
-
-  proc_left(r1, rm, c1, cm, var);
-  proc_left(rm, r2, cm, c2, var);
+procLeft(int          const r1,
+         int          const r2,
+         int          const c1,
+         int          const c2, 
+         unsigned int const var) {
+
+    int cm, rm, c;
+
+    if (r1 + 1 == r2)  return;
+    rm = (r1 + r2) >> 1;
+    cm = (c1 + c2) >> 1;
+    cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
+
+    for (c = 0; c < cm; c++)
+        PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE);
+
+    procLeft(r1, rm, c1, cm, var);
+    procLeft(rm, r2, cm, c2, var);
 }
 
+
+
 static void
-/* ------------------------------------------ */
-   proc_right(int const r1, int const r2, int const c1, int const c2,
-              unsigned int const width, unsigned int const var)
-/* ------------------------------------------ */
-{
-  int cm, rm, c;
-
-  if (r1 + 1 == r2)  return;
-  rm = (r1 + r2) >> 1;
-  cm = (c1 + c2) >> 1;
-  cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
-
-  for (c = cm; c < width; c++)
-    PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE);
-
-  proc_right(r1, rm, c1, cm, width, var);
-  proc_right(rm, r2, cm, c2, width, var);
+procRight(int          const r1,
+          int          const r2,
+          int          const c1,
+          int          const c2,
+          unsigned int const width,
+          unsigned int const var) {
+
+    int cm, rm, c;
+
+    if (r1 + 1 == r2)  return;
+    rm = (r1 + r2) >> 1;
+    cm = (c1 + c2) >> 1;
+    cm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
+
+    for (c = cm; c < width; c++)
+        PPM_ASSIGN(PIX[rm][c], BG_RED, BG_GREEN, BG_BLUE);
+
+    procRight(r1, rm, c1, cm, width, var);
+    procRight(rm, r2, cm, c2, width, var);
 }
 
+
+
 static void
-/* ---------------------------------------- */
-   proc_top(int const c1, int const c2, int const r1, int const r2,
-            unsigned int const var)
-/* ---------------------------------------- */
-{
-  int rm, cm, r;
-
-  if (c1 + 1 == c2)  return;
-  cm = (c1 + c2) >> 1;
-  rm = (r1 + r2) >> 1;
-  rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
-
-  for (r = 0; r < rm; r++)
-    PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE);
-
-  proc_top(c1, cm, r1, rm, var);
-  proc_top(cm, c2, rm, r2, var);
+procTop(int          const c1,
+        int          const c2,
+        int          const r1,
+        int          const r2,
+        unsigned int const var) {
+
+    int rm, cm, r;
+
+    if (c1 + 1 == c2)  return;
+    cm = (c1 + c2) >> 1;
+    rm = (r1 + r2) >> 1;
+    rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
+
+    for (r = 0; r < rm; r++)
+        PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE);
+
+    procTop(c1, cm, r1, rm, var);
+    procTop(cm, c2, rm, r2, var);
 }
 
+
+
 static void
-/* ------------------------------------------- */
-   proc_bottom(int const c1, int const c2, int const r1, int const r2,
-               unsigned int const height, unsigned int const var)
-/* ------------------------------------------- */
-{
-  int rm, cm, r;
-
-  if (c1 + 1 == c2)  return;
-  cm = (c1 + c2) >> 1;
-  rm = (r1 + r2) >> 1;
-  rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
-
-  for (r = rm; r < height; r++)
-    PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE);
-
-  proc_bottom(c1, cm, r1, rm, height, var);
-  proc_bottom(cm, c2, rm, r2, height, var);
+procBottom(int          const c1,
+           int          const c2,
+           int          const r1,
+           int          const r2,
+           unsigned int const height,
+           unsigned int const var) {
+
+    int rm, cm, r;
+
+    if (c1 + 1 == c2)  return;
+    cm = (c1 + c2) >> 1;
+    rm = (r1 + r2) >> 1;
+    rm += (int)floor(((float)rand() / RAND_MAX - 0.5) * var + 0.5);
+
+    for (r = rm; r < height; r++)
+        PPM_ASSIGN(PIX[r][cm], BG_RED, BG_GREEN, BG_BLUE);
+
+    procBottom(c1, cm, r1, rm, height, var);
+    procBottom(cm, c2, rm, r2, height, var);
 }
 
 
+
 int
-/* ============================ */
-   main(int argc, char* argv[])
-/* ============================ */
-{
-  struct cmdlineInfo cmdline;
-  pixel bgcolor, fgcolor;
-  pixval fg_red, fg_green, fg_blue;
-  int rows, cols, row;
-  int left, right, top, bottom;
-  int left_r1, left_r2, left_c1, left_c2;
-  int right_r1, right_r2, right_c1, right_c2;
-  int top_r1, top_r2, top_c1, top_c2;
-  int bottom_r1, bottom_r2, bottom_c1, bottom_c2;
-  unsigned init;
-  struct timeval tv;
-  int col;
-
-  ppm_init(&argc, argv);
-
-  parseCommandLine(argc, argv, &cmdline);
-
-  init = cmdline.init;
-  if (init == 0) {
-    gettimeofday(&tv, NULL);
-    srand((unsigned int)tv.tv_usec);
-  }
-  else
-    srand(init);
-
-  cols = cmdline.width;
-  rows = cmdline.height;
-  left = cmdline.left;
-  right = cmdline.right;
-  top = cmdline.top;
-  bottom = cmdline.bottom;
-
-  if (cmdline.bg_rgb)
-    bgcolor = ppm_parsecolor(cmdline.bg_rgb, PPM_MAXMAXVAL);
-  else
-    PPM_ASSIGN(bgcolor, 0, 0, 0);
-  BG_RED = PPM_GETR(bgcolor);
-  BG_GREEN = PPM_GETG(bgcolor);
-  BG_BLUE = PPM_GETB(bgcolor);
-
-  if (cmdline.fg_rgb)
-    fgcolor = ppm_parsecolor(cmdline.fg_rgb, PPM_MAXMAXVAL);
-  else
-    PPM_ASSIGN(fgcolor, PPM_MAXMAXVAL, PPM_MAXMAXVAL, PPM_MAXMAXVAL);
-  fg_red = PPM_GETR(fgcolor);
-  fg_green = PPM_GETG(fgcolor);
-  fg_blue = PPM_GETB(fgcolor);
-
-  if (cmdline.verbose) {
-    pm_message("width is %d, height is %d, variance is %d.", 
-               cols, rows, cmdline.var);
-    if (left >= 0) pm_message("ragged left border is required");
-    if (right >= 0) pm_message("ragged right border is required");
-    if (top >= 0) pm_message("ragged top border is required");
-    if (bottom >= 0) pm_message("ragged bottom border is required");
-    pm_message("background is %s", ppm_colorname(&bgcolor, PPM_MAXMAXVAL, 1));
-    pm_message("foreground is %s", ppm_colorname(&fgcolor, PPM_MAXMAXVAL, 1));
-    if (init >= 0) pm_message("srand() initialized with seed %u", init);
-  }
-
-  /* Allocate memory for the whole pixmap */
-  PIX = ppm_allocarray(cols, rows);
-
-  /* First, set all pixel to foreground color */
-  for (row = 0; row < rows; row++)
-    for (col = 0; col < cols; col++)
-      PPM_ASSIGN(PIX[row][col], fg_red, fg_green, fg_blue);
-
-  /* Make a ragged left border */
-  if (left >= 0) {
-    left_c1 = left_c2 = left;
-    left_r1 = 0;
-    left_r2 = rows - 1;
-    for (col = 0; col < left_c1; col++)
-      PPM_ASSIGN(PIX[left_r1][col], BG_RED, BG_GREEN, BG_BLUE);
-    for (col = 0; col < left_c2; col++)
-      PPM_ASSIGN(PIX[left_r2][col], BG_RED, BG_GREEN, BG_BLUE);
-
-    proc_left(left_r1, left_r2, left_c1, left_c2, cmdline.var);
-  }
-
-  /* Make a ragged right border */
-  if (right >= 0) {
-    right_c1 = right_c2 = cols - right - 1;
-    right_r1 = 0;
-    right_r2 = rows - 1;
-    for (col = right_c1; col < cols; col++)
-      PPM_ASSIGN(PIX[right_r1][col], BG_RED, BG_GREEN, BG_BLUE);
-    for (col = right_c2; col < cols; col++)
-      PPM_ASSIGN(PIX[right_r2][col], BG_RED, BG_GREEN, BG_BLUE);
-
-    proc_right(right_r1, right_r2, right_c1, right_c2, 
-               cmdline.width, cmdline.var);
-  }
-
-  /* Make a ragged top border */
-  if (top >= 0) {
-    top_r1 = top_r2 = top;
-    top_c1 = 0;
-    top_c2 = cols - 1;
-    for (row = 0; row < top_r1; row++)
-      PPM_ASSIGN(PIX[row][top_c1], BG_RED, BG_GREEN, BG_BLUE);
-    for (row = 0; row < top_r2; row++)
-      PPM_ASSIGN(PIX[row][top_c2], BG_RED, BG_GREEN, BG_BLUE);
-
-    proc_top(top_c1, top_c2, top_r1, top_r2, cmdline.var);
-  }
-
-  /* Make a ragged bottom border */
-  if (bottom >= 0) {
-    bottom_r1 = bottom_r2 = rows - bottom - 1;
-    bottom_c1 = 0;
-    bottom_c2 = cols - 1;
-    for (row = bottom_r1; row < rows; row++)
-      PPM_ASSIGN(PIX[row][bottom_c1], BG_RED, BG_GREEN, BG_BLUE);
-    for (row = bottom_r2; row < rows; row++)
-      PPM_ASSIGN(PIX[row][bottom_c2], BG_RED, BG_GREEN, BG_BLUE);
-
-    proc_bottom(bottom_c1, bottom_c2, bottom_r1, bottom_r2, 
-                cmdline.height, cmdline.var);
-  }
-
-  /* Write pixmap */
-  ppm_writeppm(stdout, PIX, cols, rows, PPM_MAXMAXVAL, 0);
-
-  ppm_freearray(PIX, rows);
-
-  pm_close(stdout);
-  exit(0);
+main(int argc, const char * argv[]) {
+
+    struct CmdlineInfo cmdline;
+    pixel bgcolor, fgcolor;
+    pixval fg_red, fg_green, fg_blue;
+    int rows, cols, row;
+    int left, right, top, bottom;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
+
+    cols = cmdline.width;
+    rows = cmdline.height;
+    left = cmdline.left;
+    right = cmdline.right;
+    top = cmdline.top;
+    bottom = cmdline.bottom;
+
+    if (cmdline.bg_rgb)
+        bgcolor = ppm_parsecolor(cmdline.bg_rgb, PPM_MAXMAXVAL);
+    else
+        PPM_ASSIGN(bgcolor, 0, 0, 0);
+    BG_RED = PPM_GETR(bgcolor);
+    BG_GREEN = PPM_GETG(bgcolor);
+    BG_BLUE = PPM_GETB(bgcolor);
+
+    if (cmdline.fg_rgb)
+        fgcolor = ppm_parsecolor(cmdline.fg_rgb, PPM_MAXMAXVAL);
+    else
+        PPM_ASSIGN(fgcolor, PPM_MAXMAXVAL, PPM_MAXMAXVAL, PPM_MAXMAXVAL);
+    fg_red = PPM_GETR(fgcolor);
+    fg_green = PPM_GETG(fgcolor);
+    fg_blue = PPM_GETB(fgcolor);
+
+    if (cmdline.verbose) {
+        pm_message("width is %d, height is %d, variance is %d.", 
+                   cols, rows, cmdline.var);
+        if (left >= 0)
+            pm_message("ragged left border is required");
+        if (right >= 0)
+            pm_message("ragged right border is required");
+        if (top >= 0)
+            pm_message("ragged top border is required");
+        if (bottom >= 0)
+            pm_message("ragged bottom border is required");
+        pm_message("background is %s",
+                   ppm_colorname(&bgcolor, PPM_MAXMAXVAL, 1));
+        pm_message("foreground is %s",
+                   ppm_colorname(&fgcolor, PPM_MAXMAXVAL, 1));
+        if (cmdline.randomseedSpec)
+            pm_message("srand() initialized with seed %u", cmdline.randomseed);
+    }
+
+    /* Allocate memory for the whole pixmap */
+    PIX = ppm_allocarray(cols, rows);
+
+    /* First, set all pixel to foreground color */
+    for (row = 0; row < rows; row++) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col)
+            PPM_ASSIGN(PIX[row][col], fg_red, fg_green, fg_blue);
+    }
+    /* Make a ragged left border */
+    if (left >= 0) {
+        int const left_c1 = left;
+        int const left_c2 = left;
+        int const left_r1 = 0;
+        int const left_r2 = rows - 1;
+
+        unsigned int col;
+
+        for (col = 0; col < left_c1; ++col)
+            PPM_ASSIGN(PIX[left_r1][col], BG_RED, BG_GREEN, BG_BLUE);
+        for (col = 0; col < left_c2; ++col)
+            PPM_ASSIGN(PIX[left_r2][col], BG_RED, BG_GREEN, BG_BLUE);
+
+        procLeft(left_r1, left_r2, left_c1, left_c2, cmdline.var);
+    }
+
+    /* Make a ragged right border */
+    if (right >= 0) {
+        int const right_c1 = cols - right - 1;
+        int const right_c2 = cols - right - 1;
+        int const right_r1 = 0;
+        int const right_r2 = rows - 1;
+
+        unsigned int col;
+
+        for (col = right_c1; col < cols; col++)
+            PPM_ASSIGN(PIX[right_r1][col], BG_RED, BG_GREEN, BG_BLUE);
+        for (col = right_c2; col < cols; col++)
+            PPM_ASSIGN(PIX[right_r2][col], BG_RED, BG_GREEN, BG_BLUE);
+
+        procRight(right_r1, right_r2, right_c1, right_c2, 
+                   cmdline.width, cmdline.var);
+    }
+
+    /* Make a ragged top border */
+    if (top >= 0) {
+        int const top_r1 = top;
+        int const top_r2 = top;
+        int const top_c1 = 0;
+        int const top_c2 = cols - 1;
+
+        unsigned int row;
+
+        for (row = 0; row < top_r1; ++row)
+            PPM_ASSIGN(PIX[row][top_c1], BG_RED, BG_GREEN, BG_BLUE);
+        for (row = 0; row < top_r2; ++row)
+            PPM_ASSIGN(PIX[row][top_c2], BG_RED, BG_GREEN, BG_BLUE);
+
+        procTop(top_c1, top_c2, top_r1, top_r2, cmdline.var);
+    }
+
+    /* Make a ragged bottom border */
+    if (bottom >= 0) {
+        int const bottom_r1 = rows - bottom - 1;
+        int const bottom_r2 = rows - bottom - 1;
+        int const bottom_c1 = 0;
+        int const bottom_c2 = cols - 1;
+
+        unsigned int row;
+
+        for (row = bottom_r1; row < rows; ++row)
+            PPM_ASSIGN(PIX[row][bottom_c1], BG_RED, BG_GREEN, BG_BLUE);
+        for (row = bottom_r2; row < rows; ++row)
+            PPM_ASSIGN(PIX[row][bottom_c2], BG_RED, BG_GREEN, BG_BLUE);
+
+        procBottom(bottom_c1, bottom_c2, bottom_r1, bottom_r2, 
+                   cmdline.height, cmdline.var);
+    }
+
+    /* Write pixmap */
+    ppm_writeppm(stdout, PIX, cols, rows, PPM_MAXMAXVAL, 0);
+
+    ppm_freearray(PIX, rows);
+
+    pm_close(stdout);
+
+    return 0;
 }
+
+
+
diff --git a/icon/Makefile b/icon/Makefile
new file mode 100644
index 00000000..95786c37
--- /dev/null
+++ b/icon/Makefile
@@ -0,0 +1,31 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = buildtools
+VPATH = .:$(SRCDIR)/$(SUBDIR)
+include $(BUILDDIR)/config.mk
+
+.PHONY: default
+default: netpbm.o
+
+# We must not attempt to build netpbm.o if the build system does not
+# have 'windres', so our all list is empty.
+.PHONY: all
+all:
+
+include $(SRCDIR)/common.mk
+
+%.ico:%.ppm
+	ppmtowinicon $< >$@
+
+%.o:%.ico
+	echo "id ICON \"$<\"" >rc
+	$(WINDRES) --input-format rc --input rc --output-format coff \
+	   --output $@
+	rm rc
+
+distclean clean: cleanlocal
+.PHONY: cleanlocal
+cleanlocal:
+	rm -f rc
diff --git a/icon/netpbm.ico b/icon/netpbm.ico
new file mode 100644
index 00000000..3bb8e29f
--- /dev/null
+++ b/icon/netpbm.ico
Binary files differdiff --git a/icon/netpbm.ppm b/icon/netpbm.ppm
new file mode 100644
index 00000000..eb5e9d71
--- /dev/null
+++ b/icon/netpbm.ppm
@@ -0,0 +1,12 @@
+P6
+48 48
+255
+****''''
PPoooooPP<7YYhwwhhYHPooˆˆˆˆ—ˆˆ„>tbdvD~ˆˆ–ˆ–ˆˆwhH'*Pˆˆˆ—¨¨¨¨¤¤V¨vŒŠŒŠhžhž$¦§§§§––ˆwh'*oˆˆ—¨¸¸¸¸¼"Ä^¨v¬”¬””ª”ªp¸p¸$¾¸¸¸¸§§ˆˆh7*oˆ—¨¸¸ÈÈÈÌÄ^ÌzÌ’Ì¢´´´´œÄœÄ|Ê\ÔÎÈȸ¸¸§–ˆh7oˆˆ¨¸¸ÈÈØØäVÌzäŠÌ¢Ò¸Ò¸¸Ê¸ÊœÔœÔxÞ\ÔÙÙÙȸ¸§–ˆhPoˆ¨¸¸ÈØØØè0ìnäŠÌ¢ä¾Ò¸ÔϸʸܸÜééxÞ4èÙÙÙÈȸ§ˆwH
oˆ—¸¸ØØØëô	äVé{÷šð©ä¾ìÊÔÏÔÞÔÞ¯æ¯æéé\êèèÙÙÈȧ§ˆh'*ˆˆ¨¸ÈØØëëè0ìnäŠð©÷·ä¾éÖ	éÖ	âçÌëÌë·÷¯æˆöxô4èèèÙÙȸ§–wHoˆ—¸ÈÈØëëëôBìn÷šð©÷·ìÊéÖ	âçâçâçÌë·÷¨ö˜üxôYùô
+èèÙÙȸ–ˆY
oˆ—¸ÈØØëëëüZù‡÷š÷·üÄ`ôØXôØXäíaäíaäíaÌøLÌøL·÷˜üxôYùô
+èèÙÙȸ§ˆh'*oˆ¨¸ÈØëëëô	üZù‡ü¢hü¶lüÄ`úØ–ôØXóè¡óè¡ÔúŽÔúŽÄþ|©þi©þi„þ4Yùô
+ô
+èèÙȸ§–h'*oˆ¨¸ÈØØëëô	üb`üŠlü¢hü¶¡üÈ úØ–úØ–óè¡èô¬ÔúŽÔúŽÔúŽÄþ|©þiŒþldþ`ô
+ô
+ô
+èÙȸ§–w'*o—¨¸ØØëëëølüb`ü†œü¢ ü¶¡üÈ úØ–ö×Ìèô¬èô¬èô¬Ðü°Ðü°¹þ¡¤þ ‡ýŸdþ`ö\ô
+èèÙÈȧ–w'*oˆ¨¸ÈØØëðhøløX˜ü†œü¢ ü¶¡ùÆÌö×Ìö×ÌêëÌêëÌèô¬ØöÌÇù̹þ¡¤þ ‡ýŸdúŒóö\èèÙȸ§ˆh'Pˆ¨¸ÈØØðhðhì”øX˜ü†œüš¼ø¸Ìø¸Ìö×Ìö×ÌêëÌêëÌØöÌØöÌÇùÌ°öÒœú¼‡ýŸLô¬óóçiÙÙȸ§ˆh'Pˆ—¨ÈÈØágì”ì”ô>´ôjÌüš¼ñ§ßø¸ÌùÆÌéÖçö×ÌäãçÌêæÇùÌ°öÒ°öÒôÐtôÄLô¬óçiçiÚ4ȸ¸–ˆY*ˆˆ¨¸ÈÐdئì”ç·ä(ÈôjÌìˆàüš¼è¸èèÄèéÖçéÖçÌêæäãçÌêæ´êé°öÒôÐtôÄ,æÌä™ä™ÔœÎhȸ§–w7oˆ—¸¸Ðdئئç·ç·ìRÔðzÌð”Øñ§ßè¸èèÄèÑÕôÑÕôÌêæ´êé´êéŒæètæàTêÔâÁâÁä™ÔœÎh¾4¸–ˆh*oˆ¨¸d¸dȤئئÏÔä(ÈÜpèÌŒøØžðÔ®øÔÆôÔÆôÑÕô´Øô´ØôŒÖôŒæètæà,æÌâÁÖÏÔœȨÂ|¾4––wHPˆˆœ`±È¤È¤ÏÔÏÔÏÔÐLìÜpèÌŒøØžðÔ®ø̼ø´Æü´Æü ØðŒÖôtÎðPÐìÖÏÖÏÉÚȨ¸¸¬T–wY
*Pˆœ`œ¤±¬±ÏÔÏÔÏÔÌìÐLì¼vøÌŒø¼žü¼žüœ¾üœ¾üŒÂüŒÂüPÐìÉÚÉÚÉÚº¼º¼º¼š¤’lwh'*P|l‰¤œ¤¬±¬±¦ä¸Ú¸Ú°ô´Rô¼vø¬†üœœüœœü„ªüt²üT¶ôºô¸Ü¸Ü®Ì®ÌšÐš¤’lzlh'*Tlt¤t¤‰¤˜ИЦä¦ä¦ä°ôœFü”jüŒ~ütütüLžü¢î¢î¢î¢î¢îšÐŠ¤Š¤r¤Vl/q4 t¤t¤|̇ì‡ì‡ì‡ì‡ì‡ì|*ülVüTnü,zü†ì†ì†ì†ì†ì~Ì~Ìr¤^¤6 q/Rq°4 DüT´XèdèdèXèXèDü!ï&üBüBüdèdèdèdèXèJÄ6 ¤R/R°°Ð!ïÐ!ï!ïííííííèèèèаÐR/q°°ÐÐÐíííííííííííÐа°q//R°°ÐÐíííííííííÐÐа°q//q°°ÐÐÐÐííííííÐÐÐа°q//Rq°°°ÐÐÐÐÐÐÐÐÐÐа°qq//qq°°°ÐÐÐÐÐÐÐа°q//qq°°°°°°Ð°°°°R//qqq°°°°°°qq/RqqqqqR//RqqqqqqRR//////
\ No newline at end of file
diff --git a/lib/Makefile b/lib/Makefile
index 0738e5cb..1e607ee5 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -13,7 +13,7 @@ else
 LIBNETPBM = $(NETPBMSHLIBPREFIX)netpbm$(DLLVER).$(NETPBMLIBSUFFIX)
 endif
 
-ifeq ($(STATICLIB_TOO),y)
+ifeq ($(STATICLIB_TOO),Y)
 EXTRA_STATICLIB = libnetpbm.$(STATICLIBSUFFIX)
 else
 EXTRA_STATICLIB =
@@ -25,7 +25,8 @@ else
   LIBSYSTEM = libsystem.o
 endif
 
-LIBOBJECTS = libpm.o pmfileio.o fileio.o bitio.o colorname.o \
+LIBOBJECTS = libpm.o pmfileio.o fileio.o colorname.o \
+	libpamd.o \
 	libpbm1.o libpbm2.o libpbm3.o libpbmfont.o \
 	libpgm1.o libpgm2.o \
 	libppm1.o libppm2.o libppmcmap.o libppmcolor.o libppmfuzzy.o \
@@ -36,24 +37,28 @@ LIBOBJECTS = libpm.o pmfileio.o fileio.o bitio.o colorname.o \
 	libpamn.o libpammap.o libpamcolor.o \
 	$(LIBSYSTEM) \
 
-ifneq (${VMS}x,x)
-LIBOBJECTS += libpbmvms.o
-endif
 # Library objects to be linked but not built by common.mk:
 LIBOBJECTS_X = \
-  util/shhopt.o \
-  util/nstring.o \
-  util/vasprintf.o \
+  util/bitio.o \
   util/filename.o \
+  util/io.o \
+  util/mallocvar.o \
+  util/matrix.o \
   util/nsleep.o \
+  util/nstring.o \
+  util/runlength.o \
+  util/shhopt.o \
+  util/token.o \
+  util/vasprintf.o \
 
 MANUALS3 = libnetpbm
 MANUALS5 = pbm pgm ppm pnm pam
 
-INTERFACE_HEADERS =  pm.h pbm.h bitio.h pbmfont.h \
-	pgm.h ppm.h ppmcmap.h ppmfloyd.h colorname.h \
-	pnm.h pam.h pammap.h util/shhopt.h util/mallocvar.h \
-	pm_system.h pm_gamma.h ppmdraw.h ppmdfont.h \
+INTERFACE_HEADERS = colorname.h \
+	pam.h pamdraw.h pammap.h pbm.h pbmfont.h \
+	pgm.h pm.h pm_gamma.h pm_system.h pnm.h \
+	ppm.h ppmcmap.h ppmdfont.h ppmdraw.h ppmfloyd.h \
+	util/mallocvar.h util/runlength.h util/shhopt.h \
 
 DATAFILES = rgb.txt
 
@@ -65,10 +70,9 @@ SCRIPTS =
 BINARIES = 
 
 OMIT_LIBRARY_RULE = 1
+ALL_INTERNAL_HEADER_FILES_ARE_QUALIFIED = Y
 include $(SRCDIR)/common.mk
 
-INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc
-
 # The following must go after common.mk because $(LIBNETPBM) may 
 # contain a reference to $(NETPBM_MAJOR_RELEASE).
 .PHONY: libnetpbm
@@ -83,12 +87,14 @@ extra_staticlib: $(EXTRA_STATICLIB)
 # type, but request a static library in addition.
 #----------------------------------------------------------------------------
 
+$(LIBOBJECTS): CFLAGS_TARGET=$(CFLAGS_SHLIB)
+
+libpbm3.o: CFLAGS_TARGET+=$(CFLAGS_SSE)
+
 $(LIBOBJECTS): %.o: %.c importinc
-# Note that the user may have configured -I options into CPPFLAGS/CFLAGS.
-	$(CC) -c $(INCLUDES) -DNDEBUG $(CPPFLAGS) $(CFLAGS) $(CFLAGS_SHLIB) \
-	  $(CFLAGS_PERSONAL) $(CADD) -o $@ $<
+	$(CC) -c $(INCLUDES) $(CFLAGS_ALL) -o $@ $<
 
-MAJ = $(NETPBM_MAJOR_RELEASE)
+MAJ = 11
 MIN = $(NETPBM_MINOR_RELEASE)
 
 SONAME = libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ)
@@ -127,7 +133,7 @@ libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ).$(MIN): $(LIBOBJECTS) $(LIBOBJECTS_X)
 endif
 
 ifeq ($(NETPBMLIBTYPE),dll)
-ifeq ($(STATICLIB_TOO),y)
+ifeq ($(STATICLIB_TOO),Y)
 $(NETPBMSHLIBPREFIX)netpbm$(DLLVER).dll: $(LIBOBJECTS) $(LIBOBJECTS_X) libnetpbm.$(STATICLIBSUFFIX)
 else
 $(NETPBMSHLIBPREFIX)netpbm$(DLLVER).dll: $(LIBOBJECTS) $(LIBOBJECTS_X)
@@ -167,22 +173,23 @@ endif
 # STATICLIB_SUFFIX may just be arbitrary.
 #-----------------------------------------------------------------------------
 ifeq ($(NETPBMLIBTYPE),unixstatic)
-  BUILD_STATICLIB = y
+  BUILD_STATICLIB = Y
 else
-  ifeq ($(STATICLIB_TOO),y)
-    BUILD_STATICLIB = y
+  ifeq ($(STATICLIB_TOO),Y)
+    BUILD_STATICLIB = Y
   else
-    BUILD_STATICLIB = n
+    BUILD_STATICLIB = N
   endif
 endif
 
-ifeq ($(BUILD_STATICLIB),y)
+ifeq ($(BUILD_STATICLIB),Y)
 libnetpbm.$(STATICLIBSUFFIX): $(LIBOBJECTS) $(LIBOBJECTS_X)
 	-rm -f $@
 	$(AR) rc $@ $(LIBOBJECTS) $(LIBOBJECTS_X)
 	-$(RANLIB) $@
 endif
 
+
 # To avoid major hassles with having ppmdcfont available here, we just ship a
 # pre-made standardppmfont.c, so this rule will not normally be used.  Though
 # standardppmdfont.c depends upon standard.ppmdfont, we don't declare that
diff --git a/lib/colorname.c b/lib/colorname.c
index ce53bdcd..83cf5d1a 100644
--- a/lib/colorname.c
+++ b/lib/colorname.c
@@ -15,28 +15,32 @@
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
-#include "pm_c_util.h"
+#include "netpbm/pm_c_util.h"
 #include <ctype.h>
 #include <string.h>
 #include <stdlib.h>
 #include <errno.h>
 
-#include "nstring.h"
+#include "netpbm/nstring.h"
+
 #include "colorname.h"
 
 static int lineNo;
 
+
+
 void 
-pm_canonstr(char * const str) {
-
-    char * p;
-    for (p = str; *p; ) {
-        if (ISSPACE(*p)) {
-            strcpy(p, &(p[1]));
-        } else {
-            if (ISUPPER(*p))
-                *p = tolower(*p);
-            ++p;
+pm_canonstr(char * const arg) {
+/*----------------------------------------------------------------------------
+   Modify string 'arg' to canonical form: lower case, no white space.
+-----------------------------------------------------------------------------*/
+    const char * srcCursor;
+    char * dstCursor;
+
+    for (srcCursor = arg, dstCursor = arg; *srcCursor; ++srcCursor) {
+        if (!ISSPACE(*srcCursor)) {
+            *dstCursor++ =
+                ISUPPER(*srcCursor) ? tolower(*srcCursor) : *srcCursor;
         }
     }
 }
@@ -65,7 +69,7 @@ openColornameFileSearch(const char * const searchPath,
         *filePP = NULL;  /* initial value */
         while (!eol && !*filePP) {
             const char * token;
-            token = strsepN(&cursor, ":");
+            token = pm_strsep(&cursor, ":");
             if (token) {
                 *filePP = fopen(token, "r");
             } else
@@ -84,8 +88,8 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
    Open the colorname dictionary file.  Its file name is 'fileName', unless
    'fileName' is NULL.  In that case, its file name is the value of the
    environment variable whose name is RGB_ENV (e.g. "RGBDEF").  Except
-   if that environment variable is not set, it is RGB_DB1, RGB_DB2,
-   or RGB_DB3 (e.g. "/usr/lib/X11/rgb.txt"), whichever exists.
+   if that environment variable is not set, it is the first file found,
+   if any, in the search path RGB_DB_PATH.
    
    'must_open' is a logical: we must get the file open or die.  If
    'must_open' is true and we can't open the file (e.g. it doesn't
@@ -195,7 +199,7 @@ pm_parse_dictionary_name(char    const colorname[],
     pixval r,g,b;
 
     f = pm_openColornameFile(NULL, TRUE);  /* exits if error */
-    canoncolor = strdup(colorname);
+    canoncolor = pm_strdup(colorname);
 
     if (!canoncolor)
         pm_error("Failed to allocate memory for %u-byte color name",
diff --git a/lib/libpam.c b/lib/libpam.c
index ab75fab6..cc6368e1 100644
--- a/lib/libpam.c
+++ b/lib/libpam.c
@@ -1,9 +1,12 @@
-/*----------------------------------------------------------------------------
+/*=============================================================================
                                   libpam.c
-------------------------------------------------------------------------------
+===============================================================================
    These are the library functions, which belong in the libnetpbm library,
    that deal with the PAM (Portable Arbitrary Format) image format.
------------------------------------------------------------------------------*/
+
+   This file was originally written by Bryan Henderson and is contributed
+   to the public domain by him and subsequent authors.
+=============================================================================*/
 
 /* See pmfileio.c for the complicated explanation of this 32/64 bit file
    offset stuff.
@@ -20,9 +23,10 @@
 
 #include <math.h>
 
-#include "pm_c_util.h"
-#include "mallocvar.h"
-#include "nstring.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+
 #include "pam.h"
 #include "ppm.h"
 #include "libpbm.h"
@@ -70,6 +74,7 @@ pamCommentP(const struct pam * const pamP) {
 }
 
 
+
 static void
 validateComputableSize(struct pam * const pamP) {
 /*----------------------------------------------------------------------------
@@ -205,21 +210,6 @@ pnm_createBlackTuple(const struct pam * const pamP,
 
 
 
-void
-createBlackTuple(const struct pam * const pamP, 
-                 tuple *            const blackTupleP) {
-
-/* This is poorly named, because it lacks the "pnm" prefix.  But for some
-   reason, this is how we originally named this.  So to maintain backward
-   compatibility with binaries that refer to "createBlackTuple", we define
-   this.  The preferred name, pnm_createBlackTuple() was new in Netpbm 10.20,
-   January 2004.  We should eventually retire createBlackTuple().
-*/
-    pnm_createBlackTuple(pamP, blackTupleP);
-}
-
-
-
 static tuple *
 allocPamRow(const struct pam * const pamP) {
 /*----------------------------------------------------------------------------
@@ -227,12 +217,11 @@ allocPamRow(const struct pam * const pamP) {
    overflow will not occur in our calculations.  NOTE: pnm_readpaminit()
    ensures this assumption is valid.
 -----------------------------------------------------------------------------*/
-    /* The tuple row data structure starts with 'width' pointers to
-       the tuples, immediately followed by the 'width' tuples
-       themselves.  Each tuple consists of 'depth' samples.  
+    /* The tuple row data structure starts with pointers to the tuples,
+       immediately followed by the tuples themselves.
     */
 
-    int const bytesPerTuple = allocationDepth(pamP) * sizeof(sample);
+    unsigned int const bytesPerTuple = allocationDepth(pamP) * sizeof(sample);
     tuple * tuplerow;
 
     tuplerow = malloc(pamP->width * (sizeof(tuple *) + bytesPerTuple));
@@ -262,9 +251,9 @@ pnm_allocpamrow(const struct pam * const pamP) {
     tuple * const tuplerow = allocPamRow(pamP);
 
     if (tuplerow == NULL)
-        pm_error("Out of memory allocating space for a tuple row of\n"
-                 "%d tuples by %d samples per tuple by %d bytes per sample.",
-                 pamP->width, allocationDepth(pamP), sizeof(sample));
+        pm_error("Out of memory allocating space for a tuple row of "
+                 "%d tuples by %d samples per tuple by %u bytes per sample.",
+                 pamP->width, allocationDepth(pamP), (unsigned)sizeof(sample));
 
     return tuplerow;
 }
@@ -401,7 +390,7 @@ pnm_setminallocationdepth(struct pam * const pamP,
         pm_error("Can't set minimum allocation depth in pam structure, "
                  "because the structure is only %u bytes long, and to "
                  "have an allocation_depth field, it must bea at least %u",
-                 pamP->len, PAM_STRUCT_SIZE(allocation_depth));
+                 pamP->len, (unsigned)PAM_STRUCT_SIZE(allocation_depth));
 
     pamP->allocation_depth = MAX(allocationDepth, pamP->depth);
         
@@ -430,8 +419,9 @@ pnm_setpamrow(const struct pam * const pamP,
 #define MAX_VALUE_LENGTH 255
 
 static void
-parse_header_line(const char buffer[], char label[MAX_LABEL_LENGTH+1], 
-                  char value[MAX_VALUE_LENGTH+1]) {
+parseHeaderLine(const char buffer[],
+                char label[MAX_LABEL_LENGTH+1], 
+                char value[MAX_VALUE_LENGTH+1]) {
     
     int buffer_curs;
 
@@ -483,9 +473,69 @@ struct headerSeen {
 
 
 static void
-process_header_line(char                const buffer[],
-                    struct pam *        const pamP,
-                    struct headerSeen * const headerSeenP) {
+parseHeaderUint(const char *   const valueString,
+                unsigned int * const valueNumP,
+                const char *   const name) {
+/*----------------------------------------------------------------------------
+   Interpret 'valueString' as the number in a header such as
+   "WIDTH 200".
+
+   'name' is the header name ("WIDTH" in the example).
+-----------------------------------------------------------------------------*/
+
+    if (strlen(valueString) == 0)
+        pm_error("Missing value for %s in PAM file header.", name);
+    else {
+        char * endptr;
+        long int valueNum;
+        errno = 0;  /* Clear errno so we can detect strtol() failure */
+        valueNum = strtol(valueString, &endptr, 10);
+        if (errno != 0)
+            pm_error("Too-large value for %s in "
+                     "PAM file header: '%s'", name, valueString);
+        else if (*endptr != '\0') 
+            pm_error("Non-numeric value for %s in "
+                     "PAM file header: '%s'", name, valueString);
+        else if (valueNum < 0) 
+            pm_error("Negative value for %s in "
+                     "PAM file header: '%s'", name, valueString);
+        else if ((unsigned int)valueNum != valueNum)
+            pm_error("Ridiculously large value for %s in "
+                     "PAM file header: %lu", name, valueNum);
+        else
+            *valueNumP = (unsigned int)valueNum;
+    }
+}
+
+
+
+static void
+parseHeaderInt(const char * const valueString,
+               int *        const valueNumP,
+               const char * const name) {
+/*----------------------------------------------------------------------------
+  This is not what it seems.  It is the same thing as
+  parseHeaderUint, except that the type of the value it returns is
+  "int" instead of "unsigned int".  But that doesn't mean the value can
+  be negative.  We throw an error is it is not positive.
+-----------------------------------------------------------------------------*/
+    unsigned int valueNum;
+
+    parseHeaderUint(valueString, &valueNum, name);
+
+    if ((int)valueNum != valueNum)
+        pm_error("Ridiculously large value for %s in "
+                 "PAM file header: %u", name, valueNum);
+    else
+        *valueNumP = (int)valueNum;
+}
+
+
+
+static void
+processHeaderLine(char                const buffer[],
+                  struct pam *        const pamP,
+                  struct headerSeen * const headerSeenP) {
 /*----------------------------------------------------------------------------
    Process a line from the PAM header.  The line is buffer[], and it is not
    a comment or blank.
@@ -497,67 +547,44 @@ process_header_line(char                const buffer[],
     char label[MAX_LABEL_LENGTH+1];
     char value[MAX_VALUE_LENGTH+1];
 
-    parse_header_line(buffer, label, value);
+    parseHeaderLine(buffer, label, value);
 
-    if (strcmp(label, "ENDHDR") == 0)
+    if (streq(label, "ENDHDR"))
         headerSeenP->endhdr = TRUE;
-    else {
-        if (strcmp(label, "WIDTH") == 0 ||
-            strcmp(label, "HEIGHT") == 0 ||
-            strcmp(label, "DEPTH") == 0 ||
-            strcmp(label, "MAXVAL") == 0) {
-
-            if (strlen(value) == 0)
-                pm_error("Missing value for %s in PAM file header.",
-                         label);
+    else if (streq(label, "WIDTH")) {
+        parseHeaderInt(value, &pamP->width, label);
+        headerSeenP->width = TRUE;
+    } else if (streq(label, "HEIGHT")) {
+        parseHeaderInt(value, &pamP->height, label);
+        headerSeenP->height = TRUE;
+    } else if (streq(label, "DEPTH")) {
+        parseHeaderUint(value, &pamP->depth, label);
+        headerSeenP->depth = TRUE;
+    } else if (streq(label, "MAXVAL")) {
+        unsigned int maxval;
+        parseHeaderUint(value, &maxval, label);
+        if (maxval >= (1<<16))
+            pm_error("Maxval too large: %u.  Max is 65535", maxval);
+        pamP->maxval = maxval;
+        headerSeenP->maxval = TRUE;
+    } else if (streq(label, "TUPLTYPE")) {
+        if (strlen(value) == 0)
+            pm_error("TUPLTYPE header does not have any tuple type text");
+        else {
+            size_t const oldLen = strlen(pamP->tuple_type);
+            if (oldLen + strlen(value) + 1 > sizeof(pamP->tuple_type)-1)
+                pm_error("TUPLTYPE value too long in PAM header");
+            if (oldLen == 0)
+                strcpy(pamP->tuple_type, value);
             else {
-                char *endptr;
-                long int numeric_value;
-                errno = 0;  /* Clear errno so we can detect strtol() failure */
-                numeric_value = strtol(value, &endptr, 10);
-                if (errno != 0)
-                    pm_error("Too-large value for %s in "
-                             "PAM file header: '%s'", label, value);
-                if (*endptr != '\0') 
-                    pm_error("Non-numeric value for %s in "
-                             "PAM file header: '%s'", label, value);
-                else if (numeric_value < 0) 
-                    pm_error("Negative value for %s in "
-                             "PAM file header: '%s'", label, value);
+                strcat(pamP->tuple_type, " ");
+                strcat(pamP->tuple_type, value);
             }
+            pamP->tuple_type[sizeof(pamP->tuple_type)-1] = '\0';
         }
-    
-        if (strcmp(label, "WIDTH") == 0) {
-            pamP->width = atoi(value);
-            headerSeenP->width = TRUE;
-        } else if (strcmp(label, "HEIGHT") == 0) {
-            pamP->height = atoi(value);
-            headerSeenP->height = TRUE;
-        } else if (strcmp(label, "DEPTH") == 0) {
-            pamP->depth = atoi(value);
-            headerSeenP->depth = TRUE;
-        } else if (strcmp(label, "MAXVAL") == 0) {
-            pamP->maxval = atoi(value);
-            headerSeenP->maxval = TRUE;
-        } else if (strcmp(label, "TUPLTYPE") == 0) {
-            if (strlen(value) == 0)
-                pm_error("TUPLTYPE header does not have any tuple type text");
-            else {
-                size_t const oldLen = strlen(pamP->tuple_type);
-                if (oldLen + strlen(value) + 1 > sizeof(pamP->tuple_type)-1)
-                    pm_error("TUPLTYPE value too long in PAM header");
-                if (oldLen == 0)
-                    strcpy(pamP->tuple_type, value);
-                else {
-                    strcat(pamP->tuple_type, " ");
-                    strcat(pamP->tuple_type, value);
-                }
-                pamP->tuple_type[sizeof(pamP->tuple_type)-1] = '\0';
-            }
-        } else 
-            pm_error("Unrecognized header line: '%s'.  "
-                     "Possible missing ENDHDR line?", label);
-    }
+    } else 
+        pm_error("Unrecognized header line type: '%s'.  "
+                 "Possible missing ENDHDR line?", label);
 }
 
 
@@ -576,7 +603,7 @@ appendComment(char **      const commentsP,
 
     if (*commentsP == NULL)
         pm_error("Couldn't get storage for %u characters of comments from "
-                 "the PAM header", commentLen);
+                 "the PAM header", (unsigned)commentLen);
 
     strcat(*commentsP, commentLine);
 }
@@ -592,7 +619,7 @@ disposeOfComments(const struct pam * const pamP,
     if (retP)
         *retP = comments;
     else
-        strfree(comments);
+        pm_strfree(comments);
 }
 
 
@@ -635,10 +662,10 @@ readpaminitrest(struct pam * const pamP) {
             buffer[256-1-1] = '\n';  /* In case fgets() truncated */
             if (buffer[0] == '#')
                 appendComment(&comments, buffer);
-            else if (stripeq(buffer, ""));
+            else if (pm_stripeq(buffer, ""));
                 /* Ignore it; it's a blank line */
             else 
-                process_header_line(buffer, pamP, &headerSeen);
+                processHeaderLine(buffer, pamP, &headerSeen);
         }
     }
 
@@ -718,13 +745,127 @@ pnm_readpaminitrestaspnm(FILE * const fileP,
                 
 unsigned int
 pnm_bytespersample(sample const maxval) {
+/*----------------------------------------------------------------------------
+   Return the number of bytes per sample in the PAM raster of a PAM image
+   with maxval 'maxval'.  It's defined to be the minimum number of bytes
+   needed for that maxval, i.e. 1 for maxval < 256, 2 otherwise.
+-----------------------------------------------------------------------------*/
+
+    /* The PAM format requires maxval to be greater than zero and less than
+       1<<16, but since that is a largely arbitrary restriction, we don't want
+       to rely on it.
+    */
 
-    assert(sizeof(maxval) * 8 <= 32);
+    unsigned int i;
+    sample a;
+
+    for (i = 0, a = maxval; i <= sizeof(maxval); ++i) {
+        if (a == 0)
+            return i;
+        a >>= 8;
+    }
+    return 0;  /* silence compiler warning */
+}
+
+
+
+static void
+validateMinDepth(const struct pam * const pamP,
+                 unsigned int       const minDepth) {
 
-    if      (maxval >>  8 == 0) return 1;
-    else if (maxval >> 16 == 0) return 2;
-    else if (maxval >> 24 == 0) return 3;
-    else                        return 4;
+    if (pamP->depth < minDepth)
+        pm_error("Depth %u is insufficient for tuple type '%s'.  "
+                 "Minimum depth is %u",
+                 pamP->depth, pamP->tuple_type, minDepth);
+}
+
+
+
+static void
+interpretTupleType(struct pam * const pamP) {
+/*----------------------------------------------------------------------------
+   Fill in redundant convenience fields in *pamP with information the
+   pamP->tuple_type value implies:
+
+     visual
+     colorDepth
+     haveOpacity
+     opacityPlane
+
+   Validate the tuple type against the depth and maxval as well.
+-----------------------------------------------------------------------------*/
+    const char * const tupleType =
+        pamP->len >= PAM_STRUCT_SIZE(tuple_type) ? pamP->tuple_type : "";
+
+    bool         visual;
+    unsigned int colorDepth;
+    bool         haveOpacity;
+    unsigned int opacityPlane;
+
+    assert(pamP->depth > 0);
+
+    switch (PAM_FORMAT_TYPE(pamP->format)) {
+    case PAM_TYPE: {
+        if (streq(tupleType, "BLACKANDWHITE")) {
+            visual = true;
+            colorDepth = 1;
+            haveOpacity = false;
+            if (pamP->maxval != 1)
+                pm_error("maxval %u is not consistent with tuple type "
+                         "BLACKANDWHITE (should be 1)",
+                         (unsigned)pamP->maxval);
+        } else if (streq(tupleType, "GRAYSCALE")) {
+            visual = true;
+            colorDepth = 1;
+            haveOpacity = false;
+        } else if (streq(tupleType, "GRAYSCALE_ALPHA")) {
+            visual = true;
+            colorDepth = 1;
+            haveOpacity = true;
+            opacityPlane = PAM_GRAY_TRN_PLANE;
+            validateMinDepth(pamP, 2);
+        } else if (streq(tupleType, "RGB")) {
+            visual = true;
+            colorDepth = 3;
+            haveOpacity = false;
+            validateMinDepth(pamP, 3);
+        } else if (streq(tupleType, "RGB_ALPHA")) {
+            visual = true;
+            colorDepth = 3;
+            haveOpacity = true;
+            opacityPlane = PAM_TRN_PLANE;
+            validateMinDepth(pamP, 4);
+        } else {
+            visual = false;
+        }
+    } break;
+    case PPM_TYPE:
+        visual = true;
+        colorDepth = 3;
+        haveOpacity = false;
+        assert(pamP->depth == 3);
+        break;
+    case PGM_TYPE:
+        visual = true;
+        colorDepth = 1;
+        haveOpacity = false;
+        break;
+    case PBM_TYPE:
+        visual = true;
+        colorDepth = 1;
+        haveOpacity = false;
+        break;
+    default:
+        assert(false);
+    }
+    if (pamP->size >= PAM_STRUCT_SIZE(visual))
+        pamP->visual = visual;
+    if (pamP->size >= PAM_STRUCT_SIZE(color_depth))
+        pamP->color_depth = colorDepth;
+    if (pamP->size >= PAM_STRUCT_SIZE(have_opacity))
+        pamP->have_opacity = haveOpacity;
+    if (pamP->size >= PAM_STRUCT_SIZE(opacity_plane))
+        pamP->opacity_plane = opacityPlane;
 }
 
 
@@ -736,9 +877,9 @@ pnm_readpaminit(FILE *       const file,
 
     if (size < PAM_STRUCT_SIZE(tuple_type)) 
         pm_error("pam object passed to pnm_readpaminit() is too small.  "
-                 "It must be large\n"
+                 "It must be large "
                  "enough to hold at least up to the "
-                 "'tuple_type' member, but according\n"
+                 "'tuple_type' member, but according "
                  "to the 'size' argument, it is only %d bytes long.", 
                  size);
 
@@ -794,6 +935,8 @@ pnm_readpaminit(FILE *       const file,
     pamP->plainformat = FALSE;
         /* See below for complex explanation of why this is FALSE. */
 
+    interpretTupleType(pamP);
+
     validateComputableSize(pamP);
 }
 
@@ -867,9 +1010,9 @@ pnm_writepaminit(struct pam * const pamP) {
 
     if (pamP->size < PAM_STRUCT_SIZE(bytes_per_sample))
         pm_error("pam object passed to pnm_writepaminit() is too small.  "
-                 "It must be large\n"
+                 "It must be large "
                  "enough to hold at least up through the "
-                 "'bytes_per_sample' member, but according\n"
+                 "'bytes_per_sample' member, but according "
                  "to its 'size' member, it is only %u bytes long.", 
                  pamP->size);
     if (pamP->len < PAM_STRUCT_SIZE(maxval))
@@ -881,27 +1024,37 @@ pnm_writepaminit(struct pam * const pamP) {
         pm_error("maxval (%lu) passed to pnm_writepaminit() "
                  "is greater than %u", pamP->maxval, PAM_OVERALL_MAXVAL);
 
-    if (pamP->len < PAM_STRUCT_SIZE(tuple_type))
+    if (pamP->len < PAM_STRUCT_SIZE(tuple_type)) {
         tupleType = "";
-    else
+        if (pamP->size >= PAM_STRUCT_SIZE(tuple_type))
+            pamP->tuple_type[0] = '\0';
+    } else
         tupleType = pamP->tuple_type;
 
-    if (pamP->len < PAM_STRUCT_SIZE(bytes_per_sample))
-        pamP->len = PAM_STRUCT_SIZE(bytes_per_sample);
     pamP->bytes_per_sample = pnm_bytespersample(pamP->maxval);
+
+    if (pamP->size >= PAM_STRUCT_SIZE(comment_p) &&
+        pamP->len < PAM_STRUCT_SIZE(comment_p))
+        pamP->comment_p = NULL;
+
+    if (pamP->size >= PAM_STRUCT_SIZE(allocation_depth) &&
+        pamP->len < PAM_STRUCT_SIZE(allocation_depth))
+        pamP->allocation_depth = 0;
+
+    interpretTupleType(pamP);
+
+    pamP->len = MIN(pamP->size, PAM_STRUCT_SIZE(opacity_plane));
     
     switch (PAM_FORMAT_TYPE(pamP->format)) {
     case PAM_TYPE:
-        if (pm_plain_output)
-            pm_error("There is no plain version of PAM.  -plain option "
-                     "is not allowed");
+        /* See explanation below of why we ignore 'pm_plain_output' here. */
         fprintf(pamP->file, "P7\n");
         writeComments(pamP);
         fprintf(pamP->file, "WIDTH %u\n",   (unsigned)pamP->width);
         fprintf(pamP->file, "HEIGHT %u\n",  (unsigned)pamP->height);
         fprintf(pamP->file, "DEPTH %u\n",   pamP->depth);
         fprintf(pamP->file, "MAXVAL %lu\n", pamP->maxval);
-        if (!stripeq(tupleType, ""))
+        if (!pm_stripeq(tupleType, ""))
             fprintf(pamP->file, "TUPLTYPE %s\n", pamP->tuple_type);
         fprintf(pamP->file, "ENDHDR\n");
         break;
@@ -954,6 +1107,23 @@ pnm_writepaminit(struct pam * const pamP) {
 
 
 
+/* EFFECT OF -plain WHEN WRITING PAM FORMAT:
+
+   Before Netpbm 10.63 (June 2013), pnm_writepaminit() did a pm_error() here
+   if 'pm_plain_output' was set (i.e. the user said -plain).  But this isn't
+   really logical, because -plain is a global option for the program and here
+   we are just writing one image.  As a global option, -plain must be defined
+   to have effect where it makes sense and have no effect where it doesn't.
+   Note that a program that generates GIF just ignores -plain.  Note also that
+   a program could conceivably generate both a PPM image and a PAM image.
+
+   Note also how we handle the other a user can request plain format: the
+   'plainformat' member of the PAM struct.  In the case of PAM, we ignore that
+   member.
+*/
+
+
+
 void
 pnm_checkpam(const struct pam *   const pamP, 
              enum pm_check_type   const checkType, 
@@ -1046,19 +1216,107 @@ pnm_makearrayrgb(const struct pam * const pamP,
 
 
 
+void 
+pnm_makerowrgba(const struct pam * const pamP,
+                tuple *            const tuplerow) {
+/*----------------------------------------------------------------------------
+   Make the tuples 'tuplerow' the RGBA equivalent of what they are now,
+   which is described by *pamP.
+
+   This means afterward, *pamP no longer correctly describes these tuples;
+   Caller must be sure to update *pamP it or not use it anymore.
+
+   We fail if Caller did not supply enough allocated space in 'tuplerow' for
+   the extra planes (tuple allocation depth).
+-----------------------------------------------------------------------------*/
+    if (pamP->len < PAM_STRUCT_SIZE(opacity_plane)) {
+        pm_message("struct pam length %u is too small for pnm_makerowrgba().  "
+                   "This function requires struct pam fields through "
+                   "'opacity_plane'", pamP->len);
+        abort();
+    } else {
+        if (!pamP->visual)
+            pm_error("Non-visual tuples given to pnm_addopacityrow()");
+        
+        if (pamP->color_depth >= 3 && pamP->have_opacity) {
+            /* It's already in RGBA format.  Leave it alone. */
+        } else {
+            unsigned int col;
+
+            if (allocationDepth(pamP) < 4)
+                pm_error("allocation depth %u passed to pnm_makerowrgba().  "
+                         "Must be at least 4.", allocationDepth(pamP));
+        
+            for (col = 0; col < pamP->width; ++col) {
+                tuple const thisTuple = tuplerow[col];
+                thisTuple[PAM_TRN_PLANE] = 
+                    pamP->have_opacity ? thisTuple[pamP->opacity_plane] :
+                    pamP->maxval;
+
+                assert(PAM_RED_PLANE == 0);
+                thisTuple[PAM_BLU_PLANE] = thisTuple[0];
+                thisTuple[PAM_GRN_PLANE] = thisTuple[0];
+            }
+        }
+    }
+}
+
+
+
+void 
+pnm_addopacityrow(const struct pam * const pamP,
+                  tuple *            const tuplerow) {
+/*----------------------------------------------------------------------------
+   Add an opacity plane to the tuples in 'tuplerow', if one isn't already
+   there.
+
+   This means afterward, *pamP no longer correctly describes these tuples;
+   Caller must be sure to update *pamP it or not use it anymore.
+
+   We fail if Caller did not supply enough allocated space in 'tuplerow' for
+   the extra plane (tuple allocation depth).
+-----------------------------------------------------------------------------*/
+    if (pamP->len < PAM_STRUCT_SIZE(opacity_plane)) {
+        pm_message("struct pam length %u is too small for pnm_makerowrgba().  "
+                   "This function requires struct pam fields through "
+                   "'opacity_plane'", pamP->len);
+        abort();
+    } else {
+        if (!pamP->visual)
+            pm_error("Non-visual tuples given to pnm_addopacityrow()");
+        
+        if (pamP->have_opacity) {
+            /* It already has opacity.  Leave it alone. */
+        } else {
+            unsigned int const opacityPlane = pamP->color_depth;
+
+            unsigned int col;
+
+            if (allocationDepth(pamP) < opacityPlane + 1)
+                pm_error("allocation depth %u passed to pnm_addopacityrow().  "
+                         "Must be at least %u.",
+                         allocationDepth(pamP), opacityPlane + 1);
+        
+            for (col = 0; col < pamP->width; ++col)
+                tuplerow[col][opacityPlane] = pamP->maxval;
+        }
+    }
+}
+
+
+
 void
 pnm_getopacity(const struct pam * const pamP,
-               bool *             const haveOpacityP,
+               int *              const haveOpacityP,
                unsigned int *     const opacityPlaneP) {
 
-    /* Design note; If use of this information proliferates, we should
-       probably add it to struct pam as convenience values analogous to
-       bytes_per_sample.
+    /* Usage note: this is obsolete since we added 'have_opacity', etc.
+       to struct pam.
     */
-    if (strcmp(pamP->tuple_type, "RGB_ALPHA") == 0) {
+    if (streq(pamP->tuple_type, "RGB_ALPHA")) {
         *haveOpacityP = TRUE;
         *opacityPlaneP = PAM_TRN_PLANE;
-    } else if (strcmp(pamP->tuple_type, "GRAYSCALE_ALPHA") == 0) {
+    } else if (streq(pamP->tuple_type, "GRAYSCALE_ALPHA")) {
         *haveOpacityP = TRUE;
         *opacityPlaneP = PAM_GRAY_TRN_PLANE;
     } else
@@ -1067,6 +1325,67 @@ pnm_getopacity(const struct pam * const pamP,
 
 
 
+tuple
+pnm_backgroundtuple(struct pam *  const pamP,
+                    tuple      ** const tuples) {
+/*--------------------------------------------------------------------
+  This function was copied from libpnm3.c's pnm_backgroundxel() and
+  modified to use tuples instead of xels.
+----------------------------------------------------------------------*/
+    tuple tuplePtr, bgtuple, ul, ur, ll, lr;
+
+    /* Guess a good background value. */
+    ul = tuples[0][0];
+    ur = tuples[0][pamP->width-1];
+    ll = tuples[pamP->height-1][0];
+    lr = tuples[pamP->height-1][pamP->width-1];
+    bgtuple = NULL;
+
+    /* We first recognize three corners equal.  If not, we look for any
+       two.  If not, we just average all four.
+    */
+    if (pnm_tupleequal(pamP, ul, ur) && pnm_tupleequal(pamP, ur, ll))
+        tuplePtr = ul;
+    else if (pnm_tupleequal(pamP, ul, ur) &&
+             pnm_tupleequal(pamP, ur, lr))
+        tuplePtr = ul;
+    else if (pnm_tupleequal(pamP, ul, ll) &&
+             pnm_tupleequal(pamP, ll, lr))
+        tuplePtr = ul;
+    else if (pnm_tupleequal(pamP, ur, ll) &&
+             pnm_tupleequal(pamP, ll, lr))
+        tuplePtr = ur;
+    else if (pnm_tupleequal(pamP, ul, ur))
+        tuplePtr = ul;
+    else if (pnm_tupleequal(pamP, ul, ll))
+        tuplePtr = ul;
+    else if (pnm_tupleequal(pamP, ul, lr))
+        tuplePtr = ul;
+    else if (pnm_tupleequal(pamP, ur, ll))
+        tuplePtr = ur;
+    else if (pnm_tupleequal(pamP, ur, lr))
+        tuplePtr = ur;
+    else if (pnm_tupleequal(pamP, ll, lr))
+        tuplePtr = ll;
+    else {
+        /* Reimplement libpnm3.c's mean4() but for tuples. */
+        unsigned int plane;
+        bgtuple = pnm_allocpamtuple(pamP);
+        for (plane = 0; plane < pamP->depth; ++plane)
+          bgtuple[plane] = (ul[plane] + ur[plane] + ll[plane] + lr[plane]) / 4;
+    }
+    if (!bgtuple) {
+        unsigned int plane;
+        bgtuple = pnm_allocpamtuple(pamP);
+        for (plane = 0; plane < pamP->depth; ++plane)
+          bgtuple[plane] = tuplePtr[plane];
+    }
+
+    return bgtuple;
+}
+
+
+
 /*=============================================================================
    pm_system() Standard Input feeder and Standard Output accepter functions.   
 =============================================================================*/
diff --git a/lib/libpamcolor.c b/lib/libpamcolor.c
index b64f8963..f3ca9a86 100644
--- a/lib/libpamcolor.c
+++ b/lib/libpamcolor.c
@@ -1,9 +1,12 @@
-/*----------------------------------------------------------------------------
+/*============================================================================
                                   libpamcolor.c
-------------------------------------------------------------------------------
-   These are the library functions, which belong in the libnetpbm library,
-   that deal with colors in the PAM image format.
------------------------------------------------------------------------------*/
+==============================================================================
+  These are the library functions, which belong in the libnetpbm library,
+  that deal with colors in the PAM image format.
+
+  This file was originally written by Bryan Henderson and is contributed
+  to the public domain by him and subsequent authors.
+=============================================================================*/
 
 /* See pmfileio.c for the complicated explanation of this 32/64 bit file
    offset stuff.
@@ -17,11 +20,13 @@
 #include <string.h>
 #include <limits.h>
 
-#include "pm_c_util.h"
+#include "netpbm/pm_c_util.h"
+
 #include "pam.h"
 #include "ppm.h"
 
 
+
 tuple
 pnm_parsecolor(const char * const colorname,
                sample       const maxval) {
diff --git a/lib/libpamd.c b/lib/libpamd.c
new file mode 100644
index 00000000..952150b4
--- /dev/null
+++ b/lib/libpamd.c
@@ -0,0 +1,1524 @@
+/* 
+**
+** This library module contains the pamdraw routines.
+**
+** Copyright (C) 1989, 1991 by Jef Poskanzer.
+**
+** Modified from ppm to pam by Willem van Schaik, Feb 2011
+**
+** 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 character drawing routines are by John Walker
+** Copyright (C) 1994 by John Walker, kelvin@fourmilab.ch
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "netpbm/pm_config.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+
+#include "pam.h"
+#include "ppmdfont.h"
+
+#include "pamdraw.h"
+
+
+struct penpos {
+    int x;
+    int y;
+};
+
+struct rectangle {
+    /* ((0,0),(0,0)) means empty. */
+    /* 'lr' is guaranteed not to be left of or above 'ul' */
+    struct penpos ul;
+    struct penpos lr;
+};
+
+static struct rectangle const emptyRectangle = {
+    {0, 0},
+    {0, 0},
+};
+
+
+
+static pamd_point
+makePoint(int const x,
+          int const y) {
+
+    return pamd_makePoint(x, y);
+}
+
+
+
+static pamd_point
+middlePoint(pamd_point const a,
+            pamd_point const b) {
+
+    pamd_point retval;
+
+    retval.x = (a.x + b.x) / 2;
+    retval.y = (a.y + b.y) / 2;
+
+    return retval;
+}
+
+
+
+static bool
+pointsEqual(pamd_point const a,
+            pamd_point const b) {
+
+    return a.x == b.x && a.y == b.y;
+}
+
+
+
+static bool
+pointIsWithinBounds(pamd_point   const p,
+                    unsigned int const cols,
+                    unsigned int const rows) {
+
+    return (p.x >= 0 && p.x < cols && p.y >= 0 && p.y < rows);
+}
+
+        
+
+static pamd_point
+vectorSum(pamd_point const a,
+          pamd_point const b) {
+
+    return makePoint(a.x + b.x, a.y + b.y);
+}
+
+
+
+static long int const DDA_SCALE = 8192;
+
+#define PAMD_MAXCOORD 32767
+/*
+  Several factors govern the limit of x, y coordination values.
+
+  The limit must be representable as (signed) int for coordinates to 
+  be carried in struct penpos (immediately above).
+
+  The following calculation, done with long ints, must not overflow:
+  cy0 = cy0 + (y1 - cy0) * (cols - 1 - cx0) / (x1 - cx0);
+
+  The following must not overflow when DDA_SCALE is set to 8092:
+  dy = (y1 - y0) * DDA_SCALE / abs(x1 - x0);
+
+  Overflow conditions for pamd_text are rather complicated, for commands
+  come from an external PPMD font file.  See comments below.  
+*/
+
+
+
+void
+pamd_validateCoord(int const c) {
+
+    if (c < -PAMD_MAXCOORD || c > PAMD_MAXCOORD)
+        pm_error("Coordinate out of bounds: %d", c);
+}
+
+
+
+void
+pamd_validatePoint(pamd_point const p) {
+
+    if (p.x < -PAMD_MAXCOORD || p.x > PAMD_MAXCOORD)
+        pm_error("x coordinate of (%d, %d) out of bounds", p.x, p.y);
+
+    if (p.y < -PAMD_MAXCOORD || p.y > PAMD_MAXCOORD)
+        pm_error("y coordinate of (%d, %d) out of bounds", p.x, p.y);
+}
+
+
+
+static void
+drawPoint(pamd_drawproc       drawproc,
+          const void *  const clientdata,
+          tuple **      const tuples, 
+          int           const cols, 
+          int           const rows, 
+          int           const depth, 
+          sample        const maxval, 
+          pamd_point    const p) {
+/*----------------------------------------------------------------------------
+   Draw a single point, assuming that it is within the bounds of the
+   image.
+-----------------------------------------------------------------------------*/
+    int i;
+
+    if (drawproc == PAMD_NULLDRAWPROC) {
+        assert(p.x >= 0); assert(p.x < cols);
+        assert(p.y >= 0); assert(p.y < rows);
+
+        for (i = 0; i < depth; i++)
+            tuples[p.y][p.x][i] = (sample) *((tuple *) clientdata + i);
+    } else {
+        drawproc(tuples, cols, rows, depth, maxval, p, clientdata);
+    }
+}
+
+
+
+void
+pamd_point_drawproc(tuple **     const tuples, 
+                    unsigned int const cols, 
+                    unsigned int const rows, 
+                    unsigned int const depth, 
+                    sample       const maxval, 
+                    pamd_point   const p,
+                    const void * const clientdata) {
+
+    unsigned int i;
+    
+    if ((p.x >= 0) && (p.x < cols) && (p.y >= 0) && (p.y < rows))
+        for (i = 0; i < depth; ++i)
+            tuples[p.y][p.x][i] = (sample) *((tuple *) clientdata + i);
+}
+
+
+
+static void
+findRectangleIntersection(struct rectangle   const rect1,
+                          struct rectangle   const rect2,
+                          struct rectangle * const intersectionP) {
+/*----------------------------------------------------------------------------
+   Find the intersection between rectangles 'rect1' and 'rect2'.
+   Return it as *intersectionP.
+-----------------------------------------------------------------------------*/
+    struct penpos tentativeUl, tentativeLr;
+
+    tentativeUl.x = MAX(rect1.ul.x, rect2.ul.x);
+    tentativeUl.y = MAX(rect1.ul.y, rect2.ul.y);
+    tentativeLr.x = MIN(rect1.lr.x, rect2.lr.x);
+    tentativeLr.y = MIN(rect1.lr.y, rect2.lr.y);
+
+    if (tentativeLr.x <= tentativeUl.x ||
+        tentativeLr.y <= tentativeUl.y) {
+        /* No intersection */
+        *intersectionP = emptyRectangle;
+    } else {
+        intersectionP->ul = tentativeUl;
+        intersectionP->lr = tentativeLr;
+    }
+}
+
+
+
+void
+pamd_filledrectangle(tuple **      const tuples, 
+                     int           const cols, 
+                     int           const rows, 
+                     int           const depth, 
+                     sample        const maxval, 
+                     int           const left, 
+                     int           const top, 
+                     int           const width, 
+                     int           const height, 
+                     pamd_drawproc       drawProc,
+                     const void *  const clientdata) {
+
+    struct rectangle image, request, intersection;
+    unsigned int row;
+
+    if (width < 0)
+        pm_error("negative width %d passed to pamd_filledrectanglep", width);
+    if (height < 0)
+        pm_error("negative height %d passed to pamd_filledrectanglep", height);
+    if (cols < 0)
+        pm_error("negative image width %d passed to pamd_filledrectanglep",
+                 cols);
+    if (rows < 0)
+        pm_error("negative image height %d passed to pamd_filledrectanglep",
+                 rows);
+
+    request.ul.x = left;
+    request.ul.y = top;
+    request.lr.x = left + width;
+    request.lr.y = top + height;
+
+    image.ul.x = 0;
+    image.ul.y = 0;
+    image.lr.x = cols;
+    image.lr.y = rows;
+
+    findRectangleIntersection(image, request, &intersection);
+
+    /* Draw. */
+    for (row = intersection.ul.y; row < intersection.lr.y; ++row) {
+        unsigned int col;
+        for (col = intersection.ul.x; col < intersection.lr.x; ++col)
+            drawPoint(drawProc, clientdata,
+                      tuples, cols, rows, depth, maxval, makePoint(col, row));
+    }
+}
+
+
+
+/* Outline drawing stuff. */
+
+static int linetype = PAMD_LINETYPE_NORMAL;
+
+
+
+int
+pamd_setlinetype(int const type) {
+
+    int old;
+
+    old = linetype;
+    linetype = type;
+    return old;
+}
+
+
+static bool lineclip = TRUE;
+
+
+
+int
+pamd_setlineclip(int const newSetting) {
+
+    bool previousSetting;
+
+    previousSetting = lineclip;
+
+    lineclip = newSetting;
+
+    return previousSetting;
+}
+
+
+
+static void
+clipEnd0(pamd_point   const p0,
+         pamd_point   const p1,
+         int          const cols,
+         int          const rows,
+         pamd_point * const c0P,
+         bool *       const noLineP) {
+/*----------------------------------------------------------------------------
+   Given a line that goes from p0 to p1, where any of these coordinates may be
+   anywhere in space -- not just in the frame, clip the p0 end to bring it
+   into the frame.  Return the clipped-to location as *c0P.
+
+   Iff this is not possible because the entire line described is
+   outside the frame, return *nolineP == true.
+
+   The frame is 'cols' columns starting at 0, by 'rows' rows starting
+   at 0.
+-----------------------------------------------------------------------------*/
+    pamd_point c0;
+    bool noLine;
+
+    c0 = p0;         /* initial value */
+    noLine = FALSE;  /* initial value */
+
+    /* Clip End 0 of the line horizontally */
+    if (c0.x < 0) {
+        if (p1.x < 0)
+            noLine = TRUE;
+        else {
+            c0.y = c0.y + (p1.y - c0.y) * (-c0.x) / (p1.x - c0.x);
+            c0.x = 0;
+        }
+    } else if (c0.x >= cols) {
+        if (p1.x >= cols)
+            noLine = TRUE;
+        else {
+            c0.y = c0.y + (p1.y - c0.y) * (cols - 1 - c0.x) / (p1.x - c0.x);
+            c0.x = cols - 1;
+        }
+    }
+
+    /* Clip End 0 of the line vertically */
+    if (c0.y < 0) {
+        if (p1.y < 0)
+            noLine = TRUE;
+        else {
+            c0.x = c0.x + (p1.x - c0.x) * (-c0.y) / (p1.y - c0.y);
+            c0.y = 0;
+        }
+    } else if (c0.y >= rows) {
+        if (p1.y >= rows)
+            noLine = TRUE;
+        else {
+            c0.x = c0.x + (p1.x - c0.x) * (rows - 1 - c0.y) / (p1.y - c0.y);
+            c0.y = rows - 1;
+        }
+    }
+
+    /* Clipping vertically may have moved the endpoint out of frame
+       horizontally.  If so, we know the other endpoint is also out of
+       frame horizontally and the line misses the frame entirely.
+    */
+    if (c0.x < 0 || c0.x >= cols) {
+        assert(p1.x < 0 || p1.x >= cols);
+        noLine = TRUE;
+    }
+    *c0P = c0;
+    *noLineP = noLine;
+}
+
+
+
+static void
+clipEnd1(pamd_point   const p0,
+         pamd_point   const p1,
+         int          const cols,
+         int          const rows,
+         pamd_point * const c1P) {
+/*----------------------------------------------------------------------------
+   Given a line that goes from p0 to p1, where p0 is within the frame, but p1
+   can be anywhere in space, clip the p1 end to bring it into the frame.
+   Return the clipped-to location as *c1P.
+
+   This is guaranteed to be possible, since we already know at least one point
+   (i.e. p0) is in the frame.
+
+   The frame is 'cols' columns starting at 0, by 'rows' rows starting
+   at 0.
+-----------------------------------------------------------------------------*/
+    pamd_point c1;
+        /* The current clipped location of p1; we clip it multile times
+           to get the final location.
+        */
+    /* p0 is in the frame: */
+    assert(p0.x >= 0 && p0.x < cols);
+    assert(p0.y >= 0 && p0.y < rows);
+    
+    /* Clip End 1 of the line horizontally */
+    c1 = p1;  /* initial value */
+    
+    if (c1.x < 0) {
+        /* We know the line isn't vertical, since End 0 is in the frame
+           and End 1 is left of frame.
+        */
+        c1.y = c1.y + (p0.y - c1.y) * (-c1.x) / (p0.x - c1.x);
+        c1.x = 0;
+    } else if (c1.x >= cols) {
+        /* We know the line isn't vertical, since End 0 is in the frame
+           and End 1 is right of frame.
+        */
+        c1.y = c1.y + (p0.y - c1.y) * (cols - 1 - c1.x) / (p0.x - c1.x);
+        c1.x = cols - 1;
+    }
+    
+    /* Clip End 1 of the line vertically */
+    if (c1.y < 0) {
+        /* We know the line isn't horizontal, since End 0 is in the frame
+           and End 1 is above frame.
+        */
+        c1.x = c1.x + (p0.x - c1.x) * (-c1.y) / (p0.y - c1.y);
+        c1.y = 0;
+    } else if (c1.y >= rows) {
+        /* We know the line isn't horizontal, since End 0 is in the frame
+           and End 1 is below frame.
+        */
+        c1.x = c1.x + (p0.x - c1.x) * (rows - 1 - c1.y) / (p0.y - c1.y);
+        c1.y = rows - 1;
+    }
+
+    *c1P = c1;
+}
+
+
+
+static void
+clipLine(pamd_point   const p0,
+         pamd_point   const p1,
+         int          const cols,
+         int          const rows,
+         pamd_point * const c0P,
+         pamd_point * const c1P,
+         bool *       const noLineP) {
+/*----------------------------------------------------------------------------
+   Clip the line that goes from p0 to p1 so that none of it is outside the
+   boundaries of the raster with width 'cols' and height 'rows'
+
+   The clipped line goes from *c0P to *c1P.
+
+   But if the entire line is outside the boundaries (i.e. we clip the
+   entire line), return *noLineP true and the other values undefined.
+-----------------------------------------------------------------------------*/
+    pamd_point c0, c1;
+        /* The line we successively modify.  Starts out as the input
+           line and ends up as the output line.
+        */
+    bool noLine;
+
+    clipEnd0(p0, p1, cols, rows, &c0, &noLine);
+
+    if (!noLine) {
+        /* p0 is in the frame: */
+        assert(c0.x >= 0 && c0.x < cols);
+        assert(c0.y >= 0 && c0.y < rows);
+
+        clipEnd1(c0, p1, cols, rows, &c1);
+    }
+
+    *c0P = c0;
+    *c1P = c1;
+    *noLineP = noLine;
+}
+
+
+
+static void
+drawShallowLine(pamd_drawproc       drawProc,
+                const void *  const clientdata,
+                tuple **      const tuples, 
+                int           const cols, 
+                int           const rows, 
+                int           const depth, 
+                sample        const maxval, 
+                pamd_point    const p0,
+                pamd_point    const p1) {
+/*----------------------------------------------------------------------------
+   Draw a line that is more horizontal than vertical.
+
+   Don't clip.
+
+   Assume the line has distinct start and end points (i.e. it's at least
+   two points).
+-----------------------------------------------------------------------------*/
+    /* Loop over X domain. */
+    long dy, srow;
+    int dx, col, row, prevrow;
+
+    if (p1.x > p0.x)
+        dx = 1;
+    else
+        dx = -1;
+    dy = (p1.y - p0.y) * DDA_SCALE / abs(p1.x - p0.x);
+    prevrow = row = p0.y;
+    srow = row * DDA_SCALE + DDA_SCALE / 2;
+    col = p0.x;
+    for ( ; ; ) {
+        if (linetype == PAMD_LINETYPE_NODIAGS && row != prevrow) {
+            drawPoint(drawProc, clientdata,
+                      tuples, cols, rows, depth, maxval,
+                      makePoint(col, prevrow));
+            prevrow = row;
+        }
+        drawPoint(drawProc, clientdata, tuples, cols, rows, depth, maxval,
+                  makePoint(col, row));
+        if (col == p1.x)
+            break;
+        srow += dy;
+        row = srow / DDA_SCALE;
+        col += dx;
+    }
+}
+
+
+
+static void
+drawSteepLine(pamd_drawproc       drawProc,
+              const void *  const clientdata,
+              tuple **      const tuples, 
+              int           const cols, 
+              int           const rows, 
+              int           const depth,
+              sample        const maxval, 
+              pamd_point    const p0,
+              pamd_point    const p1) {
+/*----------------------------------------------------------------------------
+   Draw a line that is more vertical than horizontal.
+
+   Don't clip.
+
+   Assume the line has distinct start and end points (i.e. it's at least
+   two points).
+-----------------------------------------------------------------------------*/
+    /* Loop over Y domain. */
+
+    long dx, scol;
+    int dy, col, row, prevcol;
+
+    if (p1.y > p0.y)
+        dy = 1;
+    else
+        dy = -1;
+    dx = (p1.x - p0.x) * DDA_SCALE / abs(p1.y - p0.y);
+    row = p0.y;
+    prevcol = col = p0.x;
+    scol = col * DDA_SCALE + DDA_SCALE / 2;
+    for ( ; ; ) {
+        if (linetype == PAMD_LINETYPE_NODIAGS && col != prevcol) {
+            drawPoint(drawProc, clientdata,
+                      tuples, cols, rows, depth, maxval,
+                      makePoint(prevcol, row));
+            prevcol = col;
+        }
+        drawPoint(drawProc, clientdata, tuples, cols, rows, depth, maxval,
+                  makePoint(col, row));
+        if (row == p1.y)
+            break;
+        row += dy;
+        scol += dx;
+        col = scol / DDA_SCALE;
+    }
+}
+
+
+
+void
+pamd_line(tuple **      const tuples, 
+          int           const cols, 
+          int           const rows, 
+          int           const depth, 
+          sample        const maxval, 
+          pamd_point    const p0,
+          pamd_point    const p1,
+          pamd_drawproc       drawProc,
+          const void *  const clientdata) {
+
+    pamd_point c0, c1;
+    bool noLine;  /* There's no line left after clipping */
+
+    pamd_validateCoord(cols);
+    pamd_validateCoord(rows);
+    pamd_validatePoint(p0);
+    pamd_validatePoint(p1);
+
+    if (lineclip) {
+        clipLine(p0, p1, cols, rows, &c0, &c1, &noLine);
+    } else {
+        c0 = p0;
+        c1 = p1;
+        noLine = FALSE;
+    }
+
+    if (noLine) {
+        /* Nothing to draw */
+    } else if (pointsEqual(c0, c1)) {
+        /* This line is just a point.  Because there aren't two
+           distinct endpoints, we have a special case.
+        */
+        drawPoint(drawProc, clientdata, tuples, cols, rows, depth, maxval, c0);
+    } else {
+        /* Draw, using a simple DDA. */
+        if (abs(c1.x - c0.x) > abs(c1.y - c0.y))
+            drawShallowLine(drawProc, clientdata, tuples, cols, rows,
+                            depth, maxval, c0, c1);
+        else
+            drawSteepLine(drawProc, clientdata, tuples, cols, rows,
+                          depth, maxval, c0, c1);
+    }
+}
+
+
+
+static unsigned int
+distanceFromLine(pamd_point const p,
+                 pamd_point const l0,
+                 pamd_point const l1) {
+/*----------------------------------------------------------------------------
+  Compute, sort of, the distance between point 'p' and the line through
+  'l0' and 'l1'.
+
+  I don't really know the signficance of this measurement.
+-----------------------------------------------------------------------------*/
+
+    pamd_point const middle = middlePoint(l0, l1);
+
+    return (abs(p.x - middle.x) + abs(p.y - middle.y));
+}
+
+
+
+void
+pamd_spline3(tuple **      const tuples, 
+             int           const cols, 
+             int           const rows, 
+             int           const depth, 
+             sample        const maxval, 
+             pamd_point    const p0,
+             pamd_point    const ctl,
+             pamd_point    const p1,
+             pamd_drawproc       drawProc,
+             const void *  const clientdata) {
+
+    static unsigned int const splineThresh = 3;
+        /* The limit of recursion */
+
+    if (distanceFromLine(ctl, p0, p1) <= splineThresh) {
+        /* The control point is pretty close to the straight line that
+           joins the endpoints, so we'll just draw a straight line.
+        */
+        pamd_line(
+            tuples, cols, rows, depth, maxval, p0, p1, drawProc, clientdata);
+    } else {
+        /* We want some curvature, so pick a point (b) sort of between the
+           two endpoints and the control point and then draw a spline
+           between each of the endpoints and (b):
+        */
+        pamd_point const a = middlePoint(p0, ctl);
+        pamd_point const c = middlePoint(ctl, p1);
+        pamd_point const b = middlePoint(a, c);
+
+        pamd_spline3(
+            tuples, cols, rows, depth, maxval, p0, a, b, drawProc, clientdata);
+
+        pamd_spline3(
+            tuples, cols, rows, depth, maxval, b, c, p1, drawProc, clientdata);
+    }
+}
+
+
+
+void
+pamd_polyspline(tuple **      const tuples, 
+                unsigned int  const cols, 
+                unsigned int  const rows, 
+                unsigned int  const depth, 
+                sample        const maxval, 
+                pamd_point    const p0,
+                unsigned int  const nc,
+                pamd_point *  const c,
+                pamd_point    const p1,
+                pamd_drawproc       drawProc,
+                const void *  const clientdata) {
+
+    pamd_point p;
+    
+    unsigned int i;
+
+    assert(nc > 0);
+
+    p = p0;
+    for (i = 0; i < nc - 1; ++i) {
+        pamd_point const n = middlePoint(c[i], c[i+1]);
+        pamd_spline3(
+            tuples, cols, rows, depth, maxval, p, c[i], n,
+            drawProc, clientdata);
+        p = n;
+    }
+    pamd_spline3(
+        tuples, cols, rows, depth, maxval, p, c[nc - 1], p1,
+        drawProc, clientdata);
+}
+
+
+
+void
+pamd_spline4(tuple **      const tuples, 
+             unsigned int  const cols, 
+             unsigned int  const rows, 
+             unsigned int  const depth, 
+             sample        const maxval, 
+             pamd_point    const endPt0,
+             pamd_point    const endPt1,
+             pamd_point    const ctlPt0,
+             pamd_point    const ctlPt1,
+             pamd_drawproc       drawproc,
+             const void *  const clientdata) {
+/*----------------------------------------------------------------------------
+   Draw a cubic spline from 'endPt0' to 'endPt1', using 'ctlPt0' and
+   'ctlPt1' as control points in the classic way: a line through
+   'endPt0' and 'ctlPt0' is tangent to the curve at 'entPt0' and the
+   length of that line controls "enthusiasm," whatever that is.
+   Same for 'endPt1' and 'ctlPt1'.
+-----------------------------------------------------------------------------*/
+
+    pm_error("pamd_spline4() has not been written yet!");
+
+}
+
+
+
+void
+pamd_circle(tuple **      const tuples, 
+            unsigned int  const cols, 
+            unsigned int  const rows, 
+            unsigned int  const depth, 
+            sample        const maxval, 
+            pamd_point    const center,
+            unsigned int  const radius, 
+            pamd_drawproc       drawProc,
+            const void *  const clientData) {
+/*----------------------------------------------------------------------------
+  If lineclip mode is on, draw only points within the image.
+  If lineclip is off, "draw" all points (by designated drawproc).  Note
+  that the drawproc can't actually draw a point outside the image, but
+  it might maintain state that is affected by imaginary points outside
+  the image.
+
+  Initial point is 3 o'clock. 
+-----------------------------------------------------------------------------*/
+    if (radius >= DDA_SCALE)
+        pm_error("Error drawing circle.  Radius %d is too large.", radius);
+
+    pamd_validateCoord(center.x + radius);
+    pamd_validateCoord(center.y + radius);
+    pamd_validateCoord(center.x - radius);
+    pamd_validateCoord(center.y - radius);
+
+    if (radius > 0) {
+        long const e = DDA_SCALE / radius;
+
+        pamd_point const p0 = makePoint(radius, 0);  /* 3 o'clock */
+            /* The starting point around the circle, assuming (0, 0) center */
+        pamd_point p;
+            /* Current drawing position in the circle, assuming (0,0) center */
+        bool onFirstPoint;
+        bool prevPointExists;
+        pamd_point prevPoint;
+            /* Previous drawing position, assuming (0, 0) center*/
+        long sx, sy;  /* 'p', scaled by DDA_SCALE */
+
+        p = p0;
+
+        sx = p.x * DDA_SCALE + DDA_SCALE / 2;
+        sy = p.y * DDA_SCALE + DDA_SCALE / 2;
+
+        onFirstPoint = TRUE;
+        prevPointExists = FALSE;
+
+        while (onFirstPoint || !pointsEqual(p, p0)) {
+            if (prevPointExists && pointsEqual(p, prevPoint)) {
+                /* We're on the same point we were on last time (we moved less
+                   than a point's worth).  Just keep moving.
+                */
+            } else {
+                pamd_point const imagePoint = vectorSum(center,p);
+                if (!lineclip || pointIsWithinBounds(imagePoint, cols, rows))
+                    drawPoint(drawProc, clientData,
+                              tuples, cols, rows, depth, maxval, imagePoint);
+
+                prevPoint = p;
+                prevPointExists = TRUE;
+            }
+
+            if (!pointsEqual(p, p0))
+                onFirstPoint = FALSE;
+
+            sx += e * sy / DDA_SCALE;
+            sy -= e * sx / DDA_SCALE;
+            p = makePoint(sx / DDA_SCALE, sy / DDA_SCALE);
+        }
+    }
+}
+
+
+
+/* Arbitrary fill stuff. */
+
+typedef struct {
+    pamd_point point;
+    int edge;
+} coord;
+
+typedef struct fillState {
+    int n;
+        /* Number of elements in 'coords' */
+    int size;
+    int curedge;
+    int segstart;
+    int ydir;
+    int startydir;
+    coord * coords;
+} fillState;
+
+typedef struct fillobj {
+
+    /* The only reason we have a struct fillState separate from
+       struct fillobj is that the drawproc interface is defined to
+       have drawing not modify the fillobj, i.e. it passed
+       const fillobj * to the drawing program.
+    */
+    struct fillState * stateP;
+} fillobj;
+
+#define SOME 1000
+
+static int oldclip;
+
+
+
+struct fillobj *
+pamd_fill_create(void) {
+
+    fillobj * fillObjP;
+    struct fillState * stateP;
+
+    MALLOCVAR(fillObjP);
+    if (fillObjP == NULL)
+        pm_error("out of memory allocating a fillhandle");
+
+    MALLOCVAR(stateP);
+    if (stateP == NULL)
+        pm_error("out of memory allocating a fillhandle");
+
+    stateP->n = 0;
+    stateP->size = SOME;
+    MALLOCARRAY(stateP->coords, stateP->size);
+    if (stateP->coords == NULL)
+        pm_error("out of memory allocating a fillhandle");
+    stateP->curedge = 0;
+
+    fillObjP->stateP = stateP;
+    
+    /* Turn off line clipping. */
+    /* UGGH! We must eliminate this global variable */
+    oldclip = pamd_setlineclip(0);
+    
+    return fillObjP;
+}
+
+
+
+void
+pamd_fill_destroy(struct fillobj * const fillObjP) {
+
+    free(fillObjP->stateP->coords);
+    free(fillObjP->stateP);
+    free(fillObjP);
+}
+
+
+
+static void
+addCoord(struct fillState *  const stateP,
+         pamd_point const point) {
+
+    stateP->coords[stateP->n].point = point;
+    stateP->coords[stateP->n].edge = stateP->curedge;
+
+    ++stateP->n;
+}
+
+
+
+static void
+startNewSegment(struct fillState * const stateP) {
+/*----------------------------------------------------------------------------
+   Close off the segment we're currently building and start a new one.
+-----------------------------------------------------------------------------*/
+    if (stateP->startydir != 0 && stateP->ydir != 0) {
+        /* There's stuff in the current segment.  */
+        if (stateP->startydir == stateP->ydir) {
+            /* Oops, first edge and last edge of current segment are the same.
+               Change all points in the first edge to be in the last.
+            */
+            int const firstEdge = stateP->coords[stateP->segstart].edge;
+            int const lastEdge  = stateP->coords[stateP->n - 1].edge;
+            coord * const segStartCoordP = &stateP->coords[stateP->segstart];
+            coord * const segEndCoordP   = &stateP->coords[stateP->n];
+
+            coord * fcP;
+
+            for (fcP = segStartCoordP;
+                 fcP < segEndCoordP && fcP->edge == firstEdge;
+                 ++fcP)
+                fcP->edge = lastEdge;
+        }
+    }
+    /* And start new segment. */
+    ++stateP->curedge;
+    stateP->segstart  = stateP->n;
+    stateP->ydir      = 0;
+    stateP->startydir = 0;
+}
+
+
+
+static void
+continueSegment(struct fillState * const stateP,
+                int                const dy) {
+/*----------------------------------------------------------------------------
+   'dy' is how much the current point is above the previous one.
+-----------------------------------------------------------------------------*/
+    if (dy != 0) {
+        if (stateP->ydir != 0 && stateP->ydir != dy) {
+            /* Direction changed.  Insert a fake coord, old
+               position but new edge number.
+            */
+            ++stateP->curedge;
+            addCoord(stateP, stateP->coords[stateP->n - 1].point);
+        }
+        stateP->ydir = dy;
+        if (stateP->startydir == 0)
+            stateP->startydir = dy;
+    }
+}
+
+
+
+/* pamd_fill_drawproc() is a drawproc that turns an outline drawing function
+   into a filled shape function.  This is a somewhat off-label application of
+   a drawproc:  A drawproc is intended just to draw a point.  So e.g. you
+   might draw a circle with a fat brush by calling pamd_circle with a drawproc
+   that draws a point as a 10-tuple disk.
+
+   But pamd_fill_drawproc() just draws a point the trivial way: as one tuple.
+   However, it tracks every point that is drawn in a form that a subsequent
+   pamd_fill() call can use to to fill in the shape drawn, assuming it turns
+   out to be a closed shape.
+*/
+
+void
+pamd_fill_drawproc(tuple **     const tuples, 
+                   unsigned int const cols, 
+                   unsigned int const rows, 
+                   unsigned int const depth, 
+                   sample       const maxval, 
+                   pamd_point   const p,
+                   const void * const clientdata) {
+
+    const fillobj *    const fillObjP = clientdata;
+    struct fillState * const stateP   = fillObjP->stateP;
+
+    /* Make room for two more coords, the max we might add. */
+    if (stateP->n + 2 > stateP->size) {
+        stateP->size += SOME;
+        REALLOCARRAY(stateP->coords, stateP->size);
+        if (stateP->coords == NULL)
+            pm_error("out of memory enlarging a fillhandle");
+    }
+
+    if (stateP->n == 0) {
+        /* Start first segment. */
+        stateP->segstart = stateP->n;
+        stateP->ydir = 0;
+        stateP->startydir = 0;
+        addCoord(stateP, p);
+    } else {
+        pamd_point const prevPoint = stateP->coords[stateP->n - 1].point;
+        int const dx = p.x - prevPoint.x;
+        int const dy = p.y - prevPoint.y;
+
+        if (dx == 0 && dy == 0) {
+            /* These are the same coords we had last time; don't bother */
+        } else {
+            if (abs(dx) > 1 || abs(dy) > 1)
+                startNewSegment(stateP);
+            else
+                continueSegment(stateP, dy);
+
+            addCoord(stateP, p);
+        }
+    }
+}
+
+
+
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn yxCompare;
+#endif
+
+static int
+yxCompare(const void * const c1Arg,
+          const void * const c2Arg) {
+
+    const coord * const c1P = c1Arg;
+    const coord * const c2P = c2Arg;
+
+    pamd_point const p1 = c1P->point;
+    pamd_point const p2 = c2P->point;
+
+    int retval;
+    
+    if (p1.y > p2.y)
+        retval = 1;
+    else if (p1.y < p2.y)
+        retval = -1;
+    else if (p1.x > p2.x)
+        retval = 1;
+    else if (p1.x < p2.x)
+        retval = -1;
+    else
+        retval = 0;
+
+    return retval;
+}
+
+
+
+void
+pamd_fill(tuple **         const tuples, 
+          int              const cols, 
+          int              const rows, 
+          int              const depth, 
+          sample           const maxval, 
+          struct fillobj * const fillObjP,
+          pamd_drawproc          drawProc,
+          const void *     const clientdata) {
+    
+    struct fillState * const fh = fillObjP->stateP;
+
+    int pedge;
+    int i, edge, lx, rx, py;
+    coord * cp;
+    bool eq;
+    bool leftside;
+
+    /* Close off final segment. */
+    if (fh->n > 0 && fh->startydir != 0 && fh->ydir != 0) {
+        if (fh->startydir == fh->ydir) {
+            /* Oops, first edge and last edge are the same. */
+            coord * fcp;
+            const coord * const fcpLast = & (fh->coords[fh->n - 1]);
+            int lastedge, oldedge;
+
+            lastedge = fh->coords[fh->n - 1].edge;
+            fcp = &(fh->coords[fh->segstart]);
+            oldedge = fcp->edge;
+            for ( ; fcp<=fcpLast && fcp->edge == oldedge; ++fcp )
+                fcp->edge = lastedge;
+        }
+    }
+    /* Restore clipping now. */
+    pamd_setlineclip(oldclip);
+
+    /* Sort the coords by Y, secondarily by X. */
+    qsort((char*) fh->coords, fh->n, sizeof(coord), yxCompare);
+
+    /* Find equal coords with different edge numbers, and swap if necessary. */
+    edge = -1;
+    for (i = 0; i < fh->n; ++i) {
+        cp = &fh->coords[i];
+        if (i > 1 && eq && cp->edge != edge && cp->edge == pedge) {
+            /* Swap .-1 and .-2. */
+            coord t;
+
+            t = fh->coords[i-1];
+            fh->coords[i-1] = fh->coords[i-2];
+            fh->coords[i-2] = t;
+        }
+        if (i > 0) {
+            if (cp->point.x == lx && cp->point.y == py) {
+                eq = TRUE;
+                if (cp->edge != edge && cp->edge == pedge) {
+                    /* Swap . and .-1. */
+                    coord t;
+
+                    t = *cp;
+                    *cp = fh->coords[i-1];
+                    fh->coords[i-1] = t;
+                }
+            } else
+                eq = FALSE;
+        }
+        lx    = cp->point.x;
+        py    = cp->point.y;
+        pedge = edge;
+        edge  = cp->edge;
+    }
+
+    /* Ok, now run through the coords filling spans. */
+    for (i = 0; i < fh->n; ++i) {
+        cp = &fh->coords[i];
+        if (i == 0) {
+            lx       = rx = cp->point.x;
+            py       = cp->point.y;
+            edge     = cp->edge;
+            leftside = TRUE;
+        } else {
+            if (cp->point.y != py) {
+                /* Row changed.  Emit old span and start a new one. */
+                pamd_filledrectangle(
+                    tuples, cols, rows, depth, maxval, lx, py, rx - lx + 1, 1,
+                    drawProc, clientdata);
+                lx       = rx = cp->point.x;
+                py       = cp->point.y;
+                edge     = cp->edge;
+                leftside = TRUE;
+            } else {
+                if (cp->edge == edge) {
+                    /* Continuation of side. */
+                    rx = cp->point.x;
+                } else {
+                    /* Edge changed.  Is it a span? */
+                    if (leftside) {
+                        rx       = cp->point.x;
+                        leftside = FALSE;
+                    } else {
+                        /* Got a span to fill. */
+                        pamd_filledrectangle(
+                            tuples, cols, rows, depth, maxval,
+                            lx, py, rx - lx + 1, 1, drawProc, clientdata);
+                        lx       = rx = cp->point.x;
+                        leftside = TRUE;
+                    }
+                    edge = cp->edge;
+                }
+            }
+        }
+    }
+}
+
+
+
+/* Table used to look up sine of angles from 0 through 90 degrees.
+   The value returned is the sine * 65536.  Symmetry is used to
+   obtain sine and cosine for arbitrary angles using this table. */
+
+static long sintab[] = {
+    0, 1143, 2287, 3429, 4571, 5711, 6850, 7986, 9120, 10252, 11380,
+    12504, 13625, 14742, 15854, 16961, 18064, 19160, 20251, 21336,
+    22414, 23486, 24550, 25606, 26655, 27696, 28729, 29752, 30767,
+    31772, 32768, 33753, 34728, 35693, 36647, 37589, 38521, 39440,
+    40347, 41243, 42125, 42995, 43852, 44695, 45525, 46340, 47142,
+    47929, 48702, 49460, 50203, 50931, 51643, 52339, 53019, 53683,
+    54331, 54963, 55577, 56175, 56755, 57319, 57864, 58393, 58903,
+    59395, 59870, 60326, 60763, 61183, 61583, 61965, 62328, 62672,
+    62997, 63302, 63589, 63856, 64103, 64331, 64540, 64729, 64898,
+    65047, 65176, 65286, 65376, 65446, 65496, 65526, 65536
+};
+
+static int extleft, exttop, extright, extbottom;  /* To accumulate extents */
+
+
+
+/* LINTLIBRARY */
+
+static long
+isin(int const argDeg) {
+/*----------------------------------------------------------------------------
+    Return sine of an angle in integral degrees.  The value returned is 65536
+    times the sine.
+-----------------------------------------------------------------------------*/
+
+    int deg360;
+        /* The argument reduced to the range 0-360 degrees */
+
+    if (argDeg < 0) {
+        deg360 = (360 - ((- argDeg) % 360)) % 360;
+    } else if (argDeg >= 360) {
+        deg360 = argDeg % 360;
+    } else
+        deg360 = argDeg;
+
+    /* Now look up from table according to quadrant. */
+
+    if (deg360 <= 90) {
+        return sintab[deg360];
+    } else if (deg360 <= 180) {
+        return sintab[180 - deg360];
+    } else if (deg360 <= 270) {
+        return -sintab[deg360 - 180];
+    }
+    return -sintab[360 - deg360];
+}
+
+
+
+static long
+icos(int const deg) {
+/*----------------------------------------------------------------------------
+  Return cosine of an angle in integral degrees.  The value returned is 65536
+  times the cosine
+-----------------------------------------------------------------------------*/
+    return isin(deg + 90);
+}  
+
+
+
+static int
+twosCompByteValue(unsigned char const c) {
+/*----------------------------------------------------------------------------
+   E.g. if 'c' is 0x5, return 5.  If 'c' is 0xF0, return -16.
+-----------------------------------------------------------------------------*/
+    return (char)c;
+}
+
+
+
+static int
+glyphSkipBefore(const struct ppmd_glyph * const glyphP) {
+
+    return twosCompByteValue(glyphP->header.skipBefore);
+}
+
+
+
+static int
+glyphWidth(const struct ppmd_glyph * const glyphP) {
+
+    return twosCompByteValue(glyphP->header.skipAfter) -
+        twosCompByteValue(glyphP->header.skipBefore);
+}
+
+
+static pamd_point
+commandPoint(const struct ppmd_glyphCommand * const commandP) {
+/*----------------------------------------------------------------------------
+   Return the point which is the argument of glyph drawing command
+   *commandP.  The origin of the coordinate system for this point
+   is the center of the glyph cell and the scale is the scale of the
+   font, so (-10, -10) means the upper left corner of the glyph cell.
+-----------------------------------------------------------------------------*/
+    return makePoint(twosCompByteValue(commandP->x),
+                     twosCompByteValue(commandP->y));
+}
+
+#define Scalef 21       /* Font design size */
+#define Descend 9       /* Descender offset */
+
+
+static pamd_point
+textPosFromFontPos(pamd_point   const fontPos,
+                   pamd_point   const textBoxOrigin,
+                   pamd_point   const center,
+                   pamd_point   const glyphOrigin,
+                   unsigned int const height,
+                   long         const rotcos,
+                   long         const rotsin) {
+/*----------------------------------------------------------------------------
+   'fontPos' is a position within a glyph as told by the font definition.
+   It is relative to the center of the glyph, in units of font tuples
+   (1/21 of a glyph cell).
+
+   We return the position on the canvas of that point.
+
+   That takes into account where in the text box we are, where the text box
+   is on the canvas, the size of the characters, and the rotation of the
+   text box.
+-----------------------------------------------------------------------------*/
+    pamd_point const ptl = vectorSum(center, fontPos);
+        /* Position relative to the top left of the standard glyph cell */
+
+    pamd_point const pl = vectorSum(glyphOrigin, ptl);
+        /* Position relative to the top left of the whole text box,
+           assuming the text box is horizontal and has font scale.
+        */
+    
+    pamd_point const ps = makePoint((pl.x * (int)height) / Scalef,
+                                    (pl.y * (int)height) / Scalef);
+         /* Same as above, but with the text box its actual size */
+
+    pamd_point const retval =
+        makePoint(textBoxOrigin.x +
+                  (ps.x * rotcos - (ps.y-(int)height) * rotsin) / 65536,
+                  textBoxOrigin.y +
+                  (ps.x * rotsin + (ps.y-(int)height) * rotcos) / 65536);
+
+    pamd_validatePoint(retval);
+
+    return retval;
+}
+
+
+
+static void
+drawGlyph(const struct ppmd_glyph * const glyphP,
+          pamd_point                const glyphOrigin,
+          tuple **                  const tuples,
+          unsigned int              const cols,
+          unsigned int              const rows,
+          unsigned int              const depth,
+          sample                    const maxval,
+          int                       const height,
+          pamd_point                const textBoxOrigin,
+          long                      const rotcos,
+          long                      const rotsin,
+          pamd_drawproc                   drawProc,
+          const void *              const clientdata,
+          unsigned int *            const cursorAdvanceP
+    ) {
+/*----------------------------------------------------------------------------
+  'glyphOrigin' is the position relative to the upper left corner of the text
+  box of the upper left corner of this glyph cell.  It is in units of font
+  tuples (so you have to scale it by the font size to actual distance on
+  the canvas).
+
+  We return as *cursorAdvanceP the amount to the right of this glyph cell
+  the next glyph cell on the line (if any) should be.
+
+  The actual glyph cell may be a little to the left of the nominal position
+  because of kerning.  The font says how much to shift the cell left.
+
+  'textBoxOrigin' is the left end of the baseline of the top line in the
+  text box, in the coordinate system of the canvas.  'rotcos' and 'rotsin'
+  tell how that text box is rotated with respect to the horizontal on the
+  canvas.
+  
+  'height' is the height in canvas tuples of a glyph.  This is a scale factor
+  to convert font coordinates to canvas coordinates.
+-----------------------------------------------------------------------------*/
+    pamd_point const center = makePoint(-glyphSkipBefore(glyphP), Scalef/2);
+        /* This is what you have to add to the coordinates in a glyph
+           command, which are relative to the center of the glyph, to get
+           coordinates relative to the upper left corner of the glyph
+        */
+    pamd_point p;
+        /* Current drawing position within the glyph.  Origin is the top
+           left of the glyph cell.  Units are font tuples.
+        */
+    unsigned int commandNum;
+
+    p = textPosFromFontPos(makePoint(0, 0),
+                           textBoxOrigin,
+                           center,
+                           glyphOrigin,
+                           height,
+                           rotcos, rotsin);   /* initial value */
+
+    for (commandNum = 0;
+         commandNum < glyphP->header.commandCount;
+         ++commandNum) {
+
+        const struct ppmd_glyphCommand * const commandP =
+            &glyphP->commandList[commandNum];
+
+        switch (commandP->verb) {
+        case CMD_NOOP:
+            break;
+        case CMD_DRAWLINE:
+        {
+            pamd_point const n = textPosFromFontPos(commandPoint(commandP),
+                                                    textBoxOrigin,
+                                                    center,
+                                                    glyphOrigin,
+                                                    height,
+                                                    rotcos, rotsin);
+                                                    
+            pamd_line(tuples, cols, rows, depth, maxval, p, n,
+                      drawProc, clientdata);
+
+            p = n;
+        }
+        break;
+        case CMD_MOVEPEN:
+            p = textPosFromFontPos(commandPoint(commandP),
+                                   textBoxOrigin,
+                                   center,
+                                   glyphOrigin,
+                                   height,
+                                   rotcos, rotsin);
+            break;
+        }
+    }
+    *cursorAdvanceP = glyphWidth(glyphP);
+}
+
+
+
+void
+pamd_text(tuple**       const tuples, 
+          int           const cols, 
+          int           const rows, 
+          int           const depth, 
+          sample        const maxval, 
+          pamd_point    const pos,
+          int           const height, 
+          int           const angle, 
+          const char *  const sArg, 
+          pamd_drawproc       drawProc,
+          const void *  const clientdata) {
+/*----------------------------------------------------------------------------
+   Draw the zero-terminated string 'sArg', with its baseline starting at point
+   'pos', inclined by 'angle' degrees to the X axis, with letters 'height'
+   tuples high (descenders will extend below the baseline).  We pass the
+   supplied drawproc and clientdata to pamd_linep, which performs the actual
+   drawing.
+
+   There may be multiple lines of text.  The baseline of the topmost line
+   starts at 'pos'.
+-----------------------------------------------------------------------------*/
+    const struct ppmd_font * const fontP = ppmd_get_font();
+
+    long rotsin, rotcos;
+    pamd_point p;
+    const char * s;
+
+    pamd_validatePoint(pos);
+
+    p = makePoint(0, 0);
+    rotsin = isin(-angle);
+    rotcos = icos(-angle);
+
+    for (s = &sArg[0]; *s; ) {
+        unsigned char const ch = *s++;
+
+        if (ch >= fontP->header.firstCodePoint &&
+            ch < fontP->header.firstCodePoint + fontP->header.characterCount) {
+
+            const struct ppmd_glyph * const glyphP =
+                &fontP->glyphTable[ch - fontP->header.firstCodePoint];
+
+            unsigned int cursorAdvance;
+
+            pamd_validatePoint(p); 
+
+            drawGlyph(glyphP, p, tuples, cols, rows, depth, maxval,
+                      height, pos, rotcos, rotsin,
+                      drawProc, clientdata, &cursorAdvance);
+            p.x += cursorAdvance;
+        } else if (ch == '\n') {
+            /* Move to the left edge of the next line down */
+            p.y += Scalef + Descend;
+            p.x = 0;
+        }
+    }
+}
+
+
+
+#ifndef LITERAL_FN_DEF_MATCH
+static pamd_drawproc extetnsDrawproc;
+#endif
+
+static void 
+extentsDrawproc(tuple**      const tuples, 
+                unsigned int const cols, 
+                unsigned int const rows,
+                unsigned int const depth,
+                sample       const maxval, 
+                pamd_point   const p,
+                const void * const clientdata) {
+/*----------------------------------------------------------------------------
+    Drawproc which just accumulates the extents rectangle bounding the
+    text.
+-----------------------------------------------------------------------------*/
+    extleft   = MIN(extleft,   p.x);
+    exttop    = MIN(exttop,    p.y);
+    extright  = MAX(extright,  p.x);
+    extbottom = MAX(extbottom, p.y);
+}
+
+
+
+void
+pamd_text_box(int          const height, 
+              int          const angle, 
+              const char * const s, 
+              int *        const leftP, 
+              int *        const topP, 
+              int *        const rightP, 
+              int *        const bottomP) {
+/*----------------------------------------------------------------------------
+  Calculate extents rectangle for a given piece of text.  For most
+  applications where extents are needed, angle should be zero to obtain the
+  unrotated extents.  If you need the extents box for post-rotation text,
+  however, you can set angle nonzero and it will be calculated correctly.
+-----------------------------------------------------------------------------*/
+    extleft   =  32767;
+    exttop    =  32767;
+    extright  = -32767;
+    extbottom = -32767;
+
+    pamd_text(NULL, 32767, 32767, 255, 255, makePoint(1000, 1000),
+              height, angle, s, 
+              extentsDrawproc, NULL);
+
+    *leftP   = extleft   - 1000; 
+    *topP    = exttop    - 1000;
+    *rightP  = extright  - 1000;
+    *bottomP = extbottom - 1000;
+}
+
+
+
diff --git a/lib/libpammap.c b/lib/libpammap.c
index 6fea0eb9..22224913 100644
--- a/lib/libpammap.c
+++ b/lib/libpammap.c
@@ -1,6 +1,6 @@
-/******************************************************************************
+/*=============================================================================
                                   libpammap.c
-*******************************************************************************
+===============================================================================
 
   These are functions that deal with tuple hashes and tuple tables.
 
@@ -11,13 +11,16 @@
   A tuple table lets you scan all the values, being a table of elements
   that consist of an ordered pair of a tuple value and integer.
 
-******************************************************************************/
+  This file was originally written by Bryan Henderson and is contributed
+  to the public domain by him and subsequent authors.
+=============================================================================*/
 
 #include <assert.h>
 
-#include "pm_c_util.h"
-#include "mallocvar.h"
-#include "nstring.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+
 #include "pam.h"
 #include "pammap.h"
 
@@ -31,13 +34,14 @@ pnm_hashtuple(struct pam * const pamP,
    Return the hash value of the tuple 'tuple' -- i.e. an index into a hash
    table.
 -----------------------------------------------------------------------------*/
+    unsigned int const hash_factor[] = {1, 33, 33*33};
+
     unsigned int i;
     unsigned int hash;
-    const unsigned int hash_factor[] = {33023, 30013, 27011};
 
     hash = 0;  /* initial value */
     for (i = 0; i < MIN(pamP->depth, 3); ++i) {
-        hash += tuple[i] * hash_factor[i];  /* May overflow */
+        hash += tuple[i] * hash_factor[i];
     }
     hash %= HASH_SIZE;
     return hash;
@@ -71,7 +75,7 @@ pnm_createtuplehash(void) {
 void
 pnm_destroytuplehash(tuplehash const tuplehash) {
 
-    int i;
+    unsigned int i;
 
     /* Free the chains */
 
@@ -152,7 +156,13 @@ pnm_lookuptuple(struct pam *    const pamP,
                 const tuple           searchval, 
                 int *           const foundP, 
                 int *           const retvalP) {
-    
+/*----------------------------------------------------------------------------
+   Return as *revtvalP the index of the tuple value 'searchval' in the
+   tuple hash 'tuplehash'.
+
+   But iff the tuple value isn't in the hash, return *foundP == false
+   and nothing as *retvalP.
+-----------------------------------------------------------------------------*/
     unsigned int const hashvalue = pnm_hashtuple(pamP, searchval);
     struct tupleint_list_item * p;
     struct tupleint_list_item * found;
@@ -189,7 +199,7 @@ addColorOccurrenceToHash(tuple          const color,
          p = p->next);
 
     if (p) {
-        /* It's in the hash; just tally one more occurence */
+        /* It's in the hash; just tally one more occurrence */
         ++p->tupleint.value;
         *fullP = FALSE;
     } else {
@@ -217,7 +227,16 @@ pnm_addtuplefreqoccurrence(struct pam *   const pamP,
                            tuple          const value,
                            tuplehash      const tuplefreqhash,
                            int *          const firstOccurrenceP) {
+/*----------------------------------------------------------------------------
+  Tally one more occurence of the tuple value 'value' to the tuple frequencey
+  hash 'tuplefreqhash', adding the tuple to the hash if it isn't there
+  already.
+
+  Allocate new space for the tuple value and the hash chain element.
 
+  If we can't allocate space for the new hash chain element, abort the
+  program.
+-----------------------------------------------------------------------------*/
     unsigned int const hashvalue = pnm_hashtuple(pamP, value);
             
     struct tupleint_list_item * p;
@@ -227,7 +246,7 @@ pnm_addtuplefreqoccurrence(struct pam *   const pamP,
          p = p->next);
 
     if (p) {
-        /* It's in the hash; just tally one more occurence */
+        /* It's in the hash; just tally one more occurrence */
         ++p->tupleint.value;
         *firstOccurrenceP = FALSE;
     } else {
@@ -407,7 +426,7 @@ alloctupletable(const struct pam * const pamP,
                 const char **      const errorP) {
     
     if (UINT_MAX / sizeof(struct tupleint) < size)
-        asprintfN(errorP, "size %u is too big for arithmetic", size);
+        pm_asprintf(errorP, "size %u is too big for arithmetic", size);
     else {
         unsigned int const mainTableSize = size * sizeof(struct tupleint *);
         unsigned int const tupleIntSize = 
@@ -419,7 +438,7 @@ alloctupletable(const struct pam * const pamP,
            as a single malloc block and suballocate internally.
         */
         if ((UINT_MAX - mainTableSize) / tupleIntSize < size)
-            asprintfN(errorP, "size %u is too big for arithmetic", size);
+            pm_asprintf(errorP, "size %u is too big for arithmetic", size);
         else {
             unsigned int const allocSize = mainTableSize + size * tupleIntSize;
             void * pool;
@@ -427,8 +446,9 @@ alloctupletable(const struct pam * const pamP,
             pool = malloc(allocSize);
 
             if (!pool)
-                asprintfN(errorP, "Unable to allocate %u bytes for a %u-entry "
-                          "tuple table", allocSize, size);
+                pm_asprintf(errorP,
+                            "Unable to allocate %u bytes for a %u-entry "
+                            "tuple table", allocSize, size);
             else {
                 tupletable const tbl = (tupletable) pool;
 
@@ -459,7 +479,7 @@ pnm_alloctupletable(const struct pam * const pamP,
 
     if (error) {
         pm_errormsg("%s", error);
-        strfree(error);
+        pm_strfree(error);
         pm_longjmp();
     }
     return retval;
@@ -514,7 +534,7 @@ tuplehashtotable(const struct pam * const pamP,
 
     if (error) {
         pm_errormsg("%s", error);
-        strfree(error);
+        pm_strfree(error);
         pm_longjmp();
     } else {
         unsigned int i, j;
@@ -570,7 +590,7 @@ pnm_computetupletablehash(struct pam * const pamP,
 -----------------------------------------------------------------------------*/
     tuplehash tupletablehash;
     unsigned int i;
-    bool fits;
+    int fits;
     
     tupletablehash = pnm_createtuplehash();
 
@@ -730,3 +750,5 @@ pam_colorname(struct pam *         const pamP,
     sprintf(colorname, "#%02x%02x%02x", r, g, b);
     return colorname;
 }
+
+
diff --git a/lib/libpamn.c b/lib/libpamn.c
index fe004e91..26dbbfe3 100644
--- a/lib/libpamn.c
+++ b/lib/libpamn.c
@@ -1,16 +1,20 @@
-/*----------------------------------------------------------------------------
+/*=============================================================================
                                   libpamn.c
-------------------------------------------------------------------------------
+===============================================================================
    These are the library functions, which belong in the libnetpbm library,
    that deal with the PAM image format via maxval-normalized, floating point
    sample values.
------------------------------------------------------------------------------*/
+
+   This file was originally written by Bryan Henderson and is contributed
+   to the public domain by him and subsequent authors.
+=============================================================================*/
 
 #include <assert.h>
 
-#include "pm_c_util.h"
-#include "mallocvar.h"
-#include "nstring.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+
 #include "pam.h"
 #include "fileio.h"
 #include "pm_gamma.h"
@@ -40,9 +44,10 @@ allocpamrown(const struct pam * const pamP,
 
     tuplerown = malloc(pamP->width * (sizeof(tuplen *) + bytes_per_tuple));
     if (tuplerown == NULL)
-        asprintfN(&error, "Out of memory allocating space for a tuple row of"
-                  "%u tuples by %u samples per tuple by %u bytes per sample.",
-                  pamP->width, pamP->depth, sizeof(samplen));
+        pm_asprintf(&error, "Out of memory allocating space for a tuple row of"
+                    "%u tuples by %u samples per tuple "
+                    "by %u bytes per sample.",
+                    pamP->width, pamP->depth, (unsigned)sizeof(samplen));
     else {
         /* Now we initialize the pointers to the individual tuples to make this
            a regulation C two dimensional array.
@@ -78,7 +83,7 @@ pnm_allocpamrown(const struct pam * const pamP) {
 
     if (error) {
         pm_errormsg("pnm_allocpamrown() failed.  %s", error);
-        strfree(error);
+        pm_strfree(error);
         pm_longjmp();
     }
 
@@ -279,9 +284,9 @@ pnm_allocpamarrayn(const struct pam * const pamP) {
     
     MALLOCARRAY(tuplenarray, pamP->height);
     if (tuplenarray == NULL) 
-        asprintfN(&error,
-                  "Out of memory allocating the row pointer section of "
-                  "a %u row array", pamP->height);
+        pm_asprintf(&error,
+                    "Out of memory allocating the row pointer section of "
+                    "a %u row array", pamP->height);
     else {
         unsigned int rowsDone;
 
@@ -302,7 +307,7 @@ pnm_allocpamarrayn(const struct pam * const pamP) {
     }
     if (error) {
         pm_errormsg("pnm_allocpamarrayn() failed.  %s", error);
-        strfree(error);
+        pm_strfree(error);
         pm_longjmp();
     }
 
@@ -494,7 +499,7 @@ gammaCommon(struct pam *  const pamP,
 
     unsigned int plane;
     unsigned int opacityPlane;
-    bool haveOpacity;
+    int haveOpacity;
     
     pnm_getopacity(pamP, &haveOpacity, &opacityPlane);
 
@@ -537,9 +542,14 @@ static void
 applyopacityCommon(enum applyUnapply const applyUnapply,
                    struct pam *      const pamP,
                    tuplen *          const tuplenrow) {
-
+/*----------------------------------------------------------------------------
+   Either apply or unapply opacity to the row tuplenrow[], per
+   'applyUnapply'.  Apply means to multiply each foreground sample by
+   the opacity value for that pixel; Unapply means to do the inverse, as
+   if the foreground values had already been so multiplied.
+-----------------------------------------------------------------------------*/
     unsigned int opacityPlane;
-    bool haveOpacity;
+    int haveOpacity;
     
     pnm_getopacity(pamP, &haveOpacity, &opacityPlane);
 
@@ -636,7 +646,7 @@ createUngammaMapOffset(const struct pam * const pamP,
         MALLOCARRAY(ungammaTransformMap, pamP->maxval+1);
 
         if (ungammaTransformMap != NULL) {
-            bool haveOpacity;
+            int haveOpacity;
             unsigned int opacityPlane;
             unsigned int plane;
 
diff --git a/lib/libpamread.c b/lib/libpamread.c
index 0506d020..74b1ab83 100644
--- a/lib/libpamread.c
+++ b/lib/libpamread.c
@@ -1,10 +1,13 @@
-/*----------------------------------------------------------------------------
+/*=============================================================================
                                   libpamread.c
-------------------------------------------------------------------------------
+===============================================================================
    These are the library functions, which belong in the libnetpbm library,
    that deal with reading the PAM (Portable Arbitrary Format) image format
    raster (not the header).
------------------------------------------------------------------------------*/
+
+   This file was originally written by Bryan Henderson and is contributed
+   to the public domain by him and subsequent authors.
+=============================================================================*/
 
 /* See pmfileio.c for the complicated explanation of this 32/64 bit file
    offset stuff.
@@ -16,8 +19,10 @@
 #include <limits.h>
 #include <assert.h>
 
+#include "netpbm/pm_config.h"
+#include "netpbm/nstring.h"
+
 #include "fileio.h"
-#include "nstring.h"
 #include "pam.h"
 
 
@@ -198,6 +203,51 @@ parse4BpsRow(const struct pam *    const pamP,
 
 
 static void
+validatePamRow(const struct pam * const pamP,
+               tuple *            const tuplerow,
+               const char **      const errorP) {
+/*----------------------------------------------------------------------------
+  Check for sample values above maxval in input.  
+
+  Note: a program that wants to deal with invalid sample values itself can
+  simply make sure it sets pamP->maxval sufficiently high, so this validation
+  never fails.
+-----------------------------------------------------------------------------*/
+    /* To save time, skip the test for if the maxval is a saturated value
+       (255, 65535) or format is PBM.
+
+       This is an expensive test, but is skipped in most cases: in practice
+       maxvals other than 255 or 65535 are uncommon.  Thus we do this in a
+       separate pass through the row rather than while reading in the row.
+    */
+
+    if (pamP->maxval == (((sample) 0x1) << pamP->bytes_per_sample*8) - 1 ||
+        PAM_FORMAT_TYPE(pamP->format) == PBM_FORMAT) {
+        /* There's no way a sample can be invalid, so we don't need to
+           look at the samples individually.
+        */
+        *errorP = NULL;
+    } else {
+        unsigned int col;
+        for (col = 0; col < pamP->width; ++col) {
+            unsigned int plane;
+            for (plane = 0; plane < pamP->depth; ++plane) {
+                if (tuplerow[col][plane] > pamP->maxval) {
+                    pm_asprintf(errorP,
+                                "Plane %u sample value %lu exceeds the "
+                                "image maxval of %lu",
+                                plane, tuplerow[col][plane], pamP->maxval);
+                    return;
+                }
+            }
+        }
+        *errorP = NULL;
+    }
+}
+
+
+
+static void
 readRawNonPbmRow(const struct pam * const pamP,
                  tuple *            const tuplerow) {
 
@@ -214,13 +264,12 @@ readRawNonPbmRow(const struct pam * const pamP,
 
     if (bytesRead != rowImageSize) {
         if (feof(pamP->file))
-            asprintfN(&error,
-                      "End of file encountered when trying to read a row from "
-                      "input file.");
+            pm_asprintf(&error, "End of file encountered "
+                        "when trying to read a row from input file.");
         else 
-            asprintfN(&error, "Error reading a row from input file.  "
-                      "fread() fails with errno=%d (%s)",
-                      errno, strerror(errno));
+            pm_asprintf(&error, "Error reading a row from input file.  "
+                        "fread() fails with errno=%d (%s)",
+                        errno, strerror(errno));
     } else {
         error = NULL;  /* initial assumption */
         if (tuplerow) {
@@ -230,16 +279,18 @@ readRawNonPbmRow(const struct pam * const pamP,
             case 3: parse3BpsRow(pamP, tuplerow, inbuf); break;
             case 4: parse4BpsRow(pamP, tuplerow, inbuf); break;
             default:
-                asprintfN(&error, "invalid bytes per sample passed to "
-                          "pnm_formatpamrow(): %u",  pamP->bytes_per_sample);
+                pm_asprintf(&error, "invalid bytes per sample passed to "
+                            "pnm_formatpamrow(): %u", pamP->bytes_per_sample);
             }
+            if (error == NULL)
+                validatePamRow(pamP, tuplerow, &error);
         }
     }
     pnm_freerowimage(inbuf);
 
     if (error) {
         pm_errormsg("%s", error);
-        strfree(error);
+        pm_strfree(error);
         pm_longjmp();
     }
 }
@@ -261,7 +312,7 @@ pnm_readpamrow(const struct pam * const pamP,
     /* For speed, we don't check any of the inputs for consistency 
        here (unless it's necessary to avoid crashing).  Any consistency
        checking should have been done by a prior call to 
-       pnm_writepaminit().
+       pnm_readpaminit().
     */  
 
     /* Need a special case for raw PBM because it has multiple tuples (8)
diff --git a/lib/libpamwrite.c b/lib/libpamwrite.c
index dd319d4a..29ddeaa2 100644
--- a/lib/libpamwrite.c
+++ b/lib/libpamwrite.c
@@ -1,10 +1,13 @@
-/*----------------------------------------------------------------------------
+/*============================================================================
                                   libpamwrite.c
-------------------------------------------------------------------------------
+==============================================================================
    These are the library functions, which belong in the libnetpbm library,
    that deal with writing the PAM (Portable Arbitrary Format) image format
    raster (not the header).
------------------------------------------------------------------------------*/
+
+   This file was originally written by Bryan Henderson and is contributed
+   to the public domain by him and subsequent authors.
+=============================================================================*/
 
 /* See pmfileio.c for the complicated explanation of this 32/64 bit file
    offset stuff.
@@ -16,9 +19,11 @@
 #include <stdio.h>
 #include <limits.h>
 #include <assert.h>
-
 #include <math.h>
 
+#include "netpbm/pm_config.h"
+#include "netpbm/pm_c_util.h"
+
 #include "pam.h"
 
 
@@ -351,7 +356,9 @@ pnm_writepamrow(const struct pam * const pamP,
        pnm_writepaminit().
     */
     
-    if (pm_plain_output || pamP->plainformat) {
+    if (pamP->format == PAM_FORMAT || !(pm_plain_output || pamP->plainformat))
+        writePamRawRow(pamP, tuplerow, 1);
+    else {
         switch (PAM_FORMAT_TYPE(pamP->format)) {
         case PBM_TYPE:
             writePamPlainPbmRow(pamP, tuplerow);
@@ -361,18 +368,13 @@ pnm_writepamrow(const struct pam * const pamP,
             writePamPlainRow(pamP, tuplerow);
             break;
         case PAM_TYPE:
-            /* pm_plain_output is impossible here due to assumption stated
-               above about pnm_writepaminit() having checked it.  The
-               pamP->plainformat is meaningless for PAM.
-            */
-            writePamRawRow(pamP, tuplerow, 1);
+            assert(false);
             break;
         default:
             pm_error("Invalid 'format' value %u in pam structure", 
                      pamP->format);
         }
-    } else
-        writePamRawRow(pamP, tuplerow, 1);
+    }
 }
 
 
diff --git a/lib/libpbm1.c b/lib/libpbm1.c
index fc20071c..49ab7fdf 100644
--- a/lib/libpbm1.c
+++ b/lib/libpbm1.c
@@ -18,9 +18,10 @@
 
 #include <stdio.h>
 
-#include "pm_c_util.h"
-#include "mallocvar.h"
-#include "shhopt.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/shhopt.h"
+
 #include "pbm.h"
 
 
@@ -57,27 +58,28 @@ pbm_nextimage(FILE *file, int * const eofP) {
 
 
 void
-pbm_check(FILE * file, const enum pm_check_type check_type, 
-          const int format, const int cols, const int rows,
-          enum pm_check_code * const retval_p) {
+pbm_check(FILE *               const fileP,
+          enum pm_check_type   const checkType, 
+          int                  const format,
+          int                  const cols,
+          int                  const rows,
+          enum pm_check_code * const retvalP) {
 
     if (rows < 0)
         pm_error("Invalid number of rows passed to pbm_check(): %d", rows);
     if (cols < 0)
         pm_error("Invalid number of columns passed to pbm_check(): %d", cols);
     
-    if (check_type != PM_CHECK_BASIC) {
-        if (retval_p) *retval_p = PM_CHECK_UNKNOWN_TYPE;
+    if (checkType != PM_CHECK_BASIC) {
+        if (retvalP)
+            *retvalP = PM_CHECK_UNKNOWN_TYPE;
     } else if (format != RPBM_FORMAT) {
-        if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE;
+        if (retvalP)
+            *retvalP = PM_CHECK_UNCHECKABLE;
     } else {        
-        pm_filepos const bytes_per_row = (cols+7)/8;
-        pm_filepos const need_raster_size = rows * bytes_per_row;
-#ifdef LARGEFILEDEBUG
-        pm_message("pm_filepos passed to pm_check() is %u bytes",
-                   sizeof(pm_filepos));
-#endif
-        pm_check(file, check_type, need_raster_size, retval_p);
+        pm_filepos const bytesPerRow    = (cols+7)/8;
+        pm_filepos const needRasterSize = rows * bytesPerRow;
+        pm_check(fileP, checkType, needRasterSize, retvalP);
     }
 }
 
@@ -113,23 +115,36 @@ static unsigned int const p[256] = {
 
 static int
 bitpop(const unsigned char * const packedRow,
-       unsigned int          const cols) {
+       unsigned int          const cols,
+       unsigned int          const offset) {
 /*----------------------------------------------------------------------------
-  Return the number of 1 bits in 'packedRow'.
+  Return the number of 1 bits in 'packedRow', ignoring 0 to 7 bits
+  at the row start (= on the left edge), indicated by offset.
 -----------------------------------------------------------------------------*/
-    unsigned int const colByteCnt  = pbm_packed_bytes(cols);
-    unsigned int const fullByteCnt = cols/8;
+    unsigned int const fullLength = cols + offset;
 
-    unsigned int i;
     unsigned int sum;
 
-    sum = 0;  /* initial value */
+    if (fullLength <= 8) {
+        /* All bits are in a single byte */
+        sum = bitpop8((packedRow[0] << offset ) & (0xff << (8 - cols)));
+    } else {
+        unsigned int const colByteCnt  = pbm_packed_bytes(fullLength);
+        unsigned int const fullByteCnt = fullLength/8;
+
+        unsigned int i;
+
+        /* First byte, whether it is full or not */
+        sum = bitpop8(packedRow[0] << offset );
 
-    for (i = 0; i < fullByteCnt; ++i)
-        sum += bitpop8(packedRow[i]);
+        /* Second byte to last full byte */
+        for (i = 1; i < fullByteCnt; ++i)
+            sum += bitpop8(packedRow[i]);
 
-    if (colByteCnt > fullByteCnt)
-        sum += bitpop8(packedRow[i] >> (8-cols%8));
+        /* Partial byte at the right end */
+        if (colByteCnt > fullByteCnt)
+            sum += bitpop8(packedRow[i] >> (8 - fullLength%8));
+    }
 
     return sum;
 }
@@ -151,19 +166,13 @@ pbm_backgroundbitrow(unsigned const char * const packedBits,
 
     unsigned int retval;
 
-    unsigned int firstbit, lastbit;
-    unsigned int totalBitpop, headBitpop;
-
-    firstbit = (row[0] >> (7-rs)) & 0x01;
-    lastbit  = (row[last] >> (7- (cols+rs-1)%8)) & 0x01;
+    bool const firstbit = (row[0] >> (7-rs)) & 0x01;
+    bool const lastbit  = (row[last] >> (7- (cols+rs-1)%8)) & 0x01;
 
     if (firstbit == lastbit)
         retval = firstbit;
     else {
-        totalBitpop = bitpop(row, cols + rs);
-        headBitpop  = (rs == 0) ? 0 : bitpop(row, rs);
-
-        if (totalBitpop - headBitpop >= cols/2)
+        if (bitpop(row, cols, rs) >= cols/2)
             retval = PBM_BLACK;
         else
             retval = PBM_WHITE;
diff --git a/lib/libpbm2.c b/lib/libpbm2.c
index a8e4b0f6..f199c51a 100644
--- a/lib/libpbm2.c
+++ b/lib/libpbm2.c
@@ -10,6 +10,7 @@
 ** implied warranty.
 */
 
+#include <assert.h>
 #include <limits.h>
 
 #include "pbm.h"
@@ -34,11 +35,9 @@ getbit (FILE * const file) {
 
 
 void
-pbm_readpbminitrest( file, colsP, rowsP )
-    FILE* file;
-    int* colsP;
-    int* rowsP;
-    {
+pbm_readpbminitrest( FILE * const file,
+                     int  * const colsP,
+                     int  * const rowsP ) {
     /* Read size. */
     *colsP = (int)pm_getuint( file );
     *rowsP = (int)pm_getuint( file );
@@ -55,7 +54,7 @@ pbm_readpbminitrest( file, colsP, rowsP )
         pm_error("Number of columns in header is too large.");
     if (*rowsP < 0)
         pm_error("Number of columns in header is too large.");
-    }
+}
 
 
 
@@ -87,10 +86,13 @@ pbm_readpbminit(FILE * const ifP,
                 int *  const rowsP,
                 int *  const formatP) {
 
-    *formatP = pm_readmagicnumber(ifP);
+    int realFormat;
 
-    switch (PAM_FORMAT_TYPE(*formatP)) {
+    realFormat = pm_readmagicnumber(ifP);
+
+    switch (PAM_FORMAT_TYPE(realFormat)) {
     case PBM_TYPE:
+        *formatP = realFormat;
         pbm_readpbminitrest(ifP, colsP, rowsP);
         break;
 
@@ -110,7 +112,8 @@ pbm_readpbminit(FILE * const ifP,
                  "to PBM with 'pamtopnm'");
         break;
     default:
-        pm_error("bad magic number - not a Netpbm file");
+        pm_error("bad magic number 0x%x - not a PPM, PGM, PBM, or PAM file",
+                 realFormat);
     }
     validateComputableSize(*colsP, *rowsP);
 }
@@ -118,32 +121,29 @@ pbm_readpbminit(FILE * const ifP,
 
 
 void
-pbm_readpbmrow( file, bitrow, cols, format )
-    FILE* file;
-    bit* bitrow;
-    int cols, format;
-    {
-    register int col, bitshift;
-    register bit* bP;
+pbm_readpbmrow( FILE * const file,
+                bit * const bitrow,
+                int const cols,
+                int const format) {
+
+    int col, bitshift;
 
     switch ( format )
     {
     case PBM_FORMAT:
-    for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-        *bP = getbit( file );
+    for ( col = 0; col < cols; ++col )
+        bitrow[col] = getbit( file );
     break;
 
     case RPBM_FORMAT: {
-        register unsigned char item;
+        unsigned char item;
         bitshift = -1;  item = 0;  /* item's value is meaningless here */
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-          {
-              if ( bitshift == -1 )
-                {
+        for ( col = 0; col < cols; ++col ) {
+              if ( bitshift == -1 ) {
                     item = pm_getrawbyte( file );
                     bitshift = 7;
                 }
-              *bP = ( item >> bitshift ) & 1;
+              bitrow[col] = ( item >> bitshift ) & 1;
               --bitshift;
           }
     }
@@ -152,7 +152,7 @@ pbm_readpbmrow( file, bitrow, cols, format )
     default:
     pm_error( "can't happen" );
     }
-    }
+}
 
 
 
@@ -180,12 +180,12 @@ pbm_readpbmrow_packed(FILE *          const fileP,
     break;
 
     case RPBM_FORMAT: {
-        int bytes_read;
-        bytes_read = fread(packedBits, 1, pbm_packed_bytes(cols), fileP);
+        unsigned int bytesReadCt;
+        bytesReadCt = fread(packedBits, 1, pbm_packed_bytes(cols), fileP);
              
-        if (bytes_read < pbm_packed_bytes(cols)) {
+        if (bytesReadCt < pbm_packed_bytes(cols)) {
             if (feof(fileP)) 
-                if (bytes_read == 0) 
+                if (bytesReadCt == 0) 
                     pm_error("Attempt to read a raw PBM image row, but "
                              "no more rows left in file.");
                 else
@@ -197,7 +197,7 @@ pbm_readpbmrow_packed(FILE *          const fileP,
     break;
     
     default:
-        pm_error( "Internal error in pbm_readpbmrow_packed." );
+        pm_error("Internal error in pbm_readpbmrow_packed.");
     }
 }
 
@@ -258,17 +258,36 @@ pbm_readpbmrow_bitoffset(FILE *          const ifP,
 
         window[last] =  leftBits | rightBits;
     }
-} 
+}
+
+
+
+void
+pbm_cleanrowend_packed(unsigned char * const packedBits,
+                       unsigned int    const cols) {
+/*----------------------------------------------------------------------------
+  Set fractional "don't care" bits at end of row to zero.
+----------------------------------------------------------------------------*/
+    unsigned int const bitsPerChar = 8;
+
+    if (cols % bitsPerChar > 0) {
+        unsigned int const last = pbm_packed_bytes(cols) - 1;
+
+        assert(pbm_packed_bytes(cols) > 0);
+
+        packedBits[last] >>= bitsPerChar - cols % bitsPerChar;
+        packedBits[last] <<= bitsPerChar - cols % bitsPerChar;
+    }
+}
 
 
 
 bit**
-pbm_readpbm( file, colsP, rowsP )
-    FILE* file;
-    int* colsP;
-    int* rowsP;
-    {
-    register bit** bits;
+pbm_readpbm( FILE * const file,
+             int  * const colsP,
+             int  * const rowsP) {
+
+    bit ** bits;
     int format, row;
 
     pbm_readpbminit( file, colsP, rowsP, &format );
@@ -279,4 +298,4 @@ pbm_readpbm( file, colsP, rowsP )
         pbm_readpbmrow( file, bits[row], *colsP, format );
 
     return bits;
-    }
+}
diff --git a/lib/libpbm3.c b/lib/libpbm3.c
index 9200d30e..c8389824 100644
--- a/lib/libpbm3.c
+++ b/lib/libpbm3.c
@@ -12,21 +12,33 @@
 
 #include <assert.h>
 
-#include "pm_c_util.h"
+#include "netpbm/pm_c_util.h"
+
 #include "pbm.h"
 
-#if HAVE_GCC_MMXSSE
-#include "bitreverse.h"
+#ifndef PACKBITS_SSE
+#if WANT_SSE && defined(__SSE2__) && HAVE_GCC_BSWAP
+  #define PACKBITS_SSE 2
+#else
+  #define PACKBITS_SSE 0
+#endif
 #endif
 
-/* HAVE_GCC_MMXSSE means we have the means to use MMX and SSE CPU facilities
-   to make PBM raster processing faster.  GCC only.
+/* WANT_SSE means we want to use SSE CPU facilities to make PBM raster
+   processing faster.  This implies it's actually possible - i.e. the
+   build environment has <emmintrin.h>.
 
-   The GNU Compiler -msse option makes SSE available.
-   For x86-32 with MMX/SSE, "-msse" must be explicitly given.
-   For x86-64 and AMD64, "-msse" is on by default.
+   The GNU Compiler -msse2 option makes SSE/SSE2 available, and is
+   evidenced by __SSE2__.
+   For x86-32 with SSE, "-msse2" must be explicitly given.
+   For x86-64 and AMD64, "-msse2" is the default (from Gcc v.4.)
 */
 
+#if PACKBITS_SSE == 2
+  #include <emmintrin.h>
+#endif
+
+
 void
 pbm_writepbminit(FILE * const fileP, 
                  int    const cols, 
@@ -35,9 +47,6 @@ pbm_writepbminit(FILE * const fileP,
 
     if (!forceplain && !pm_plain_output) {
         fprintf(fileP, "%c%c\n%d %d\n", PBM_MAGIC1, RPBM_MAGIC2, cols, rows);
-#ifdef VMS
-        set_outfile_binary();
-#endif
     } else
         fprintf(fileP, "%c%c\n%d %d\n", PBM_MAGIC1, PBM_MAGIC2, cols, rows);
 }
@@ -56,80 +65,107 @@ writePackedRawRow(FILE *                const fileP,
 } 
 
 
-#if HAVE_GCC_MMXSSE
+
+#if PACKBITS_SSE == 2
 static void
-packBitsWithMmxSse(FILE *          const fileP,
+packBitsWithSse2(  FILE *          const fileP,
                    const bit *     const bitrow,
                    unsigned char * const packedBits,
-                   unsigned int    const cols,
-                   unsigned int *  const nextColP) {
+                   unsigned int    const cols) {
 /*----------------------------------------------------------------------------
-   Pack the bits of bitrow[] into bytes at 'packedBits'.  Going left to right,
-   stop when there aren't enough bits left to fill a whole byte.  Return
-   as *nextColP the number of the next column after the rightmost one we
-   packed.
+    Pack the bits of bitrow[] into bytes at 'packedBits'.
+
+    Use the SSE2 facilities to pack the bits quickly, but
+    perform the exact same function as the simpler
+    packBitsGeneric() + packPartialBytes()
 
-   Use the Pentium MMX and SSE facilities to pack the bits quickly, but
-   perform the exact same function as the simpler packBitsGeneric().
+    Unlike packBitsGeneric(), the whole row is converted.
 -----------------------------------------------------------------------------*/
     /*
-      We use MMX/SSE facilities that operate on 8 bytes at once to pack
-      the bits quickly.
-    
-      We use 2 MMX registers (no SSE registers).
+      We use 2 SSE registers.
     
       The key machine instructions are:
-    
-    
-      PCMPGTB  Packed CoMPare Greater Than Byte
-    
-        Compares 8 bytes in parallel
-        Result is x00 if greater than, xFF if not for each byte       
-    
-      PMOVMSKB Packed MOVe MaSK Byte 
-    
-        Result is a byte of the MSBs of 8 bytes
-        x00 xFF x00 xFF xFF xFF x00 x00 --> 01011100B = 0x5C
         
-        The result is actually a 32 bit int, but the higher bits are
-        always 0.  (0x0000005C in the above case)
+      PCMPGTB128  Packed CoMPare Greater Than Byte
     
-      EMMS     Empty MMx State
+        Compares 16 bytes in parallel
+        Result is x00 if greater than, xFF if not for each byte
+
     
-        Free MMX registers  
+      PMOVMSKB128 Packed MOVe MaSK Byte 
     
-    */
-
+        Result is 16 bits, the MSBs of 16 bytes
+        x00 xFF x00 xFF xFF xFF x00 x00 xFF xFF xFF xFF x00 x00 x00 x00 
+        --> 0101110011110000B = 0x5CF0
+        
+        The result is actually a 64 bit int, but the higher bits are
+        always 0.
 
-    typedef char v8qi __attribute__ ((vector_size(8)));
-    typedef int di __attribute__ ((mode(DI)));
+      We use SSE instructions in "_mm_" form in favor of "__builtin_".
+      In GCC the "__builtin_" form is documented but "_mm_" is not.
+      Former versions of this source file used "__builtin_".  This was
+      changed to make possible compilation with clang, which does not
+      implement some "__builtin_" forms.
 
-    unsigned int col;
-    v8qi const zero64 =(v8qi)((di)0);  /* clear to zero */
+      __builtin_ia32_pcmpgtb128 :  _mm_cmpgt_epi8
+      __builtin_ia32_pmovmskb128 : _mm_movemask_epi8
 
-    for (col = 0; col + 7 < cols; col += 8) {
+      The conversion requires <emmintrin.h> .
+    */
 
-        v8qi const compare =
-            __builtin_ia32_pcmpgtb(*(v8qi*) (&bitrow[col]), (v8qi) zero64);
-        uint32_t const backwardBlackMask =  __builtin_ia32_pmovmskb(compare);
-        unsigned char const blackMask = bitreverse[backwardBlackMask];
+    typedef char v16qi __attribute__ ((vector_size(16)));
 
-        packedBits[col/8] = blackMask;
+    unsigned int col;
+    union {
+        v16qi    v16;
+        uint64_t i64[2];
+        unsigned char byte[16];
+    } bit128;
+
+    v16qi zero128;
+    zero128 = zero128 ^ zero128;   /* clear to zero */
+
+    for (col = 0; col + 15 < cols; col += 16) {
+        bit128.i64[0]=__builtin_bswap64( *(uint64_t*) &bitrow[col]);
+        bit128.i64[1]=__builtin_bswap64( *(uint64_t*) &bitrow[col+8]);
+
+        {
+            v16qi const compare = (v16qi)
+                _mm_cmpgt_epi8((__m128i)bit128.v16, (__m128i) zero128);
+            uint16_t const blackMask = _mm_movemask_epi8 ((__m128i)compare);
+            
+            *(uint16_t *) & packedBits[col/8] = blackMask;
+        }
     }
-    *nextColP = col;
 
-    __builtin_ia32_emms();
+    if (cols % 16 > 0) {
+        unsigned int i, j;
 
+        bit128.v16 = bit128.v16 ^ bit128.v16;
+    
+        for (i = 0, j = col ; j < cols; ++i, ++j) 
+            bit128.byte[ (i&8) + 7-(i&7) ] = bitrow[j];
+      
+        {
+            v16qi const compare = (v16qi)
+                _mm_cmpgt_epi8((__m128i)bit128.v16, (__m128i) zero128);
+            uint16_t const blackMask = _mm_movemask_epi8 ((__m128i)compare);
+
+            if ( cols%16 >8 )  /* Two partial bytes */
+                *(uint16_t *) & packedBits[col/8] = blackMask;
+            else              /* One partial byte */
+                packedBits[col/8] = (unsigned char) blackMask ;
+        }
+    }
 }
 #else
 /* Avoid undefined function warning; never actually called */
 
-#define packBitsWithMmxSse(a,b,c,d,e) packBitsGeneric(a,b,c,d,e)
+#define packBitsWithSse2(a,b,c,d) packBitsGeneric((a),(b),(c),(d),NULL)
 #endif
 
 
 
-
 static unsigned int
 bitValue(unsigned char const byteValue) {
 
@@ -212,18 +248,20 @@ writePbmRowRaw(FILE *      const fileP,
         pm_setjmpbuf(origJmpbufP);
         pm_longjmp();
     } else {
-        unsigned int nextCol;
 
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
-        if (HAVE_GCC_MMXSSE)
-            packBitsWithMmxSse(fileP, bitrow, packedBits, cols, &nextCol);
-        else 
+        switch (PACKBITS_SSE) {
+        case 2: 
+            packBitsWithSse2(fileP, bitrow, packedBits, cols);
+            break;
+        default: {
+            unsigned int nextCol;
             packBitsGeneric(fileP, bitrow, packedBits, cols, &nextCol);
-
-        if (cols % 8 > 0)
-            packPartialBytes(bitrow, cols, nextCol, packedBits);
-        
+            if (cols % 8 > 0)
+                packPartialBytes(bitrow, cols, nextCol, packedBits);
+        }
+        }
         writePackedRawRow(fileP, packedBits, cols);
 
         pm_setjmpbuf(origJmpbufP);
diff --git a/lib/libpbmfont.c b/lib/libpbmfont.c
index 4ba812cd..d3551e78 100644
--- a/lib/libpbmfont.c
+++ b/lib/libpbmfont.c
@@ -16,12 +16,14 @@
 
 #include <assert.h>
 #include <string.h>
+#include <ctype.h>
+
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
 
-#include "pm_c_util.h"
-#include "nstring.h"
-#include "pbm.h"
 #include "pbmfont.h"
-#include "mallocvar.h"
+#include "pbm.h"
 
 static unsigned int const firstCodePoint = 32;
     /* This is the code point of the first character in a pbmfont font.
@@ -1072,12 +1074,48 @@ pbm_dumpfont( fn )
 /* Routines for loading a BDF font file */
 
 
-static unsigned int
-mk_argvn(char *        const s,
-         const char ** const vec,
-         unsigned int  const mk_max) {
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is an object for reading lines of a font file.  It reads and tokenizes
+   them into words.
+-----------------------------------------------------------------------------*/
+    FILE * ifP;
 
-    int n;
+    char line[1024];
+        /* This is the storage space for the words of the line.  The
+           words go in here, one after another, separated by NULs.
+
+           It also functions as a work area for readline_read().
+        */
+    const char * arg[32];
+        /* These are the words; each entry is a pointer into line[] (above) */
+} readline;
+
+
+
+static void
+readline_init(readline * const readlineP,
+              FILE *     const ifP) {
+
+    readlineP->ifP = ifP;
+
+    readlineP->arg[0] = NULL;
+}
+
+
+
+static void
+tokenize(char *         const s,
+         const char **  const words,
+         unsigned int   const maxWordCt) {
+/*----------------------------------------------------------------------------
+   Chop up 's' into words by changing space characters to NUL.  Return
+   as 'words' pointer to the beginning of those words in 's'.
+
+   If there are more than 'maxWordCt' words in 's', ignore the excess on
+   the right.
+-----------------------------------------------------------------------------*/
+    unsigned int n;
     char * p;
 
     p = &s[0];
@@ -1087,30 +1125,27 @@ mk_argvn(char *        const s,
         if (ISSPACE(*p))
             *p++ = '\0';
         else {
-            vec[n++] = p;
-            if (n >= mk_max-1)
+            words[n++] = p;
+            if (n >= maxWordCt-1)
                 break;
             while (*p && !ISSPACE(*p))
                 ++p;
         }
     }
-    vec[n] = NULL;
-
-    return n;
+    words[n] = NULL;
 }
 
 
 
-static int
-readline(FILE *        const ifP,
-         char *        const line,
-         const char ** const wordList) {
+static void
+readline_read(readline * const readlineP,
+              bool *     const eofP) {
 /*----------------------------------------------------------------------------
-   Read a nonblank line from file *ifP.  Return the value of the whole line
-   in *buf (must be at least 1024 bytes long), and parse it into words
-   in *wordList (must have at least 32 entries).
+   Read a nonblank line from the file.  Make its contents available
+   as readlineP->arg[].
 
-   If there is no nonblank line before EOF, return rc == -1.
+   Return *eofP == true iff there is no nonblank line before EOF or we
+   are unable to read the file.
 -----------------------------------------------------------------------------*/
     bool gotLine;
     bool error;
@@ -1118,22 +1153,85 @@ readline(FILE *        const ifP,
     for (gotLine = false, error = false; !gotLine && !error; ) {
         char * rc;
 
-        rc = fgets(line, 1024, ifP);
+        rc = fgets(readlineP->line, 1024, readlineP->ifP);
         if (rc == NULL)
             error = true;
         else {
-            mk_argvn(line, wordList, 32);
-            if (wordList[0] != NULL)
+            tokenize(readlineP->line,
+                     readlineP->arg, ARRAY_SIZE(readlineP->arg));
+            if (readlineP->arg[0] != NULL)
                 gotLine = true;
         }
     }
-    return error ? -1 : 0;
+    *eofP = error;
+}
+
+
+
+static void
+parseBitmapRow(const char *    const hex,
+               unsigned int    const glyphWidth,
+               unsigned char * const bmap,
+               unsigned int    const origBmapIndex,
+               unsigned int *  const newBmapIndexP,
+               const char **   const errorP) {
+/*----------------------------------------------------------------------------
+   Parse one row of the bitmap for a glyph, from the hexadecimal string
+   for that row in the font file, 'hex'.  The glyph is 'glyphWidth'
+   pixels wide.
+
+   We place our result in 'bmap' at *bmapIndexP and advanced *bmapIndexP.
+-----------------------------------------------------------------------------*/
+    unsigned int bmapIndex;
+    int i;  /* dot counter */
+    const char * p;
+
+    bmapIndex = origBmapIndex;
+
+    for (i = glyphWidth, p = &hex[0], *errorP = NULL;
+         i > 0 && !*errorP;
+         i -= 4) {
+
+        if (*p == '\0')
+            pm_asprintf(errorP, "Not enough hexadecimal digits for glyph "
+                        "of width %u in '%s'",
+                        glyphWidth, hex);
+        else {
+            char const hdig = *p++;
+            unsigned int hdigValue;
+
+            if (hdig >= '0' && hdig <= '9')
+                hdigValue = hdig - '0';
+            else if (hdig >= 'a' && hdig <= 'f')
+                hdigValue = 10 + (hdig - 'a');
+            else if (hdig >= 'A' && hdig <= 'F')
+                hdigValue = 10 + (hdig - 'A');
+            else 
+                pm_asprintf(errorP,
+                            "Invalid hex digit x%02x (%c) in bitmap data '%s'",
+                            (unsigned int)(unsigned char)hdig, 
+                            isprint(hdig) ? hdig : '.',
+                            hex);
+
+            if (!*errorP) {
+                if (i > 0)
+                    bmap[bmapIndex++] = hdigValue & 0x8 ? 1 : 0;
+                if (i > 1)
+                    bmap[bmapIndex++] = hdigValue & 0x4 ? 1 : 0;
+                if (i > 2)
+                    bmap[bmapIndex++] = hdigValue & 0x2 ? 1 : 0;
+                if (i > 3)
+                    bmap[bmapIndex++] = hdigValue & 0x1 ? 1 : 0;
+            }
+        }
+    }
+    *newBmapIndexP = bmapIndex;
 }
 
 
 
 static void
-readBitmap(FILE *          const fp,
+readBitmap(readline *      const readlineP,
            unsigned int    const glyphWidth,
            unsigned int    const glyphHeight,
            const char *    const charName,
@@ -1145,48 +1243,26 @@ readBitmap(FILE *          const fp,
     bmapIndex = 0;
            
     for (n = glyphHeight; n > 0; --n) {
-        int i;  /* dot counter */
-        int rc;
-        char * hex;
-        char line[1024];
-        const char * arg[32];
+        bool eof;
+        const char * error;
 
-        rc = readline(fp, line, arg);
+        readline_read(readlineP, &eof);
 
-        if (rc < 0)
+        if (eof)
             pm_error("End of file in bitmap for character '%s' in BDF "
                      "font file.", charName);
 
-        hex = line;
-        for (i = glyphWidth; i > 0; i -= 4) {
-            if (*hex == '\0')
-                pm_error("Premature end of line in line '%s' of "
-                         "bitmap for character '%s' "
-                         "in BDF font file", line, charName);
-            else {
-                char const hdig = *hex++;
-                unsigned int hdigValue;
-
-                if (hdig >= '0' && hdig <= '9')
-                    hdigValue = hdig - '0';
-                else if (hdig >= 'a' && hdig <= 'f')
-                    hdigValue = 10 + (hdig - 'a');
-                else if (hdig >= 'A' && hdig <= 'F')
-                    hdigValue = 10 + (hdig - 'A');
-                else 
-                    pm_error("Invalid hex digit '%c' in line '%s' of "
-                             "bitmap for character '%s' "
-                             "in BDF font file", hdig, line, charName);
-                        
-                if (i > 0)
-                    bmap[bmapIndex++] = hdigValue & 0x8 ? 1 : 0;
-                if (i > 1)
-                    bmap[bmapIndex++] = hdigValue & 0x4 ? 1 : 0;
-                if (i > 2)
-                    bmap[bmapIndex++] = hdigValue & 0x2 ? 1 : 0;
-                if (i > 3)
-                    bmap[bmapIndex++] = hdigValue & 0x1 ? 1 : 0;
-            }
+        if (!readlineP->arg[0])
+            pm_error("A line that is supposed to contain bitmap data, "
+                     "in hexadecimal, for character '%s' is empty", charName);
+
+        parseBitmapRow(readlineP->arg[0], glyphWidth, bmap, bmapIndex,
+                       &bmapIndex, &error);
+
+        if (error) {
+            pm_error("Error in line %d of bitmap for character '%s': %s",
+                     n, charName, error);
+            pm_strfree(error);
         }
     }
 }
@@ -1196,15 +1272,13 @@ readBitmap(FILE *          const fp,
 static void
 createBmap(unsigned int  const glyphWidth,
            unsigned int  const glyphHeight,
-           FILE *        const fp,
+           readline *    const readlineP,
            const char *  const charName,
            const char ** const bmapP) {
 
-    char line[1024];
-    const char * arg[32];
     unsigned char * bmap;
-    int rc;
-
+    bool eof;
+    
     if (glyphWidth > 0 && UINT_MAX / glyphWidth < glyphHeight)
         pm_error("Ridiculously large glyph");
 
@@ -1213,24 +1287,26 @@ createBmap(unsigned int  const glyphWidth,
     if (!bmap)
         pm_error("no memory for font glyph byte map");
 
-    rc = readline(fp, line, arg);
-    if (rc < 0)
+    readline_read(readlineP, &eof);
+    if (eof)
         pm_error("End of file encountered reading font glyph byte map from "
                  "BDF font file.");
-
-    if (streq(arg[0], "ATTRIBUTES")) {
-        rc = readline(fp, line, arg);
-        if (rc < 0)
+    
+    if (streq(readlineP->arg[0], "ATTRIBUTES")) {
+        bool eof;
+        readline_read(readlineP, &eof);
+        if (eof)
             pm_error("End of file encountered after ATTRIBUTES in BDF "
                      "font file.");
     }                
-    if (!streq(arg[0], "BITMAP"))
+    if (!streq(readlineP->arg[0], "BITMAP"))
         pm_error("'%s' found where BITMAP expected in definition of "
-                 "character '%s' in BDF font file.", line, charName);
+                 "character '%s' in BDF font file.",
+                 readlineP->arg[0], charName);
 
-    assert(streq(arg[0], "BITMAP"));
+    assert(streq(readlineP->arg[0], "BITMAP"));
 
-    readBitmap(fp, glyphWidth, glyphHeight, charName, bmap);
+    readBitmap(readlineP, glyphWidth, glyphHeight, charName, bmap);
 
     *bmapP = (char *)bmap;
 }
@@ -1238,52 +1314,53 @@ createBmap(unsigned int  const glyphWidth,
 
 
 static void
-expect(FILE *        const fp,
-       const char *  const expected,
-       const char ** const arg,
-       char *        const line) {
-
-    int rc;
+readExpectedStatement(readline *    const readlineP,
+                      const char *  const expected) {
+/*----------------------------------------------------------------------------
+  Have the readline object *readlineP read the next line from the file, but
+  expect it to be a line of type 'expected' (i.e. the verb token at the
+  beginning of the line is that, e.g. "STARTFONT").  If it isn't, fail the
+  program.
+-----------------------------------------------------------------------------*/
+    bool eof;
 
-    rc = readline(fp, line, arg);
+    readline_read(readlineP, &eof);
 
-    if (rc < 0)
+    if (eof)
         pm_error("EOF in BDF font file where '%s' expected", expected);
-    else if (!streq(arg[0], expected))
-        pm_error("'%s' where '%s' expected in BDF font file",
-                 line, expected);
+    else if (!streq(readlineP->arg[0], expected))
+        pm_error("Statement of type '%s' where '%s' expected in BDF font file",
+                 readlineP->arg[0], expected);
 }
 
 
 
 static void
-skipCharacter(FILE * const fp) {
+skipCharacter(readline * const readlineP) {
 /*----------------------------------------------------------------------------
-  In BDF font file 'fp', skip through the end of the character we are
-  presently in.
+  In the BDF font file being read by readline object *readlineP, skip through
+  the end of the character we are presently in.
 -----------------------------------------------------------------------------*/
     bool endChar;
                         
     endChar = FALSE;
                         
     while (!endChar) {
-        char line[1024];
-        const char * arg[32];
-        int rc;
-        rc = readline(fp, line, arg);
-        if (rc < 0)
+        bool eof;
+        readline_read(readlineP, &eof);
+        if (eof)
             pm_error("End of file in the middle of a character (before "
                      "ENDCHAR) in BDF font file.");
-        endChar = streq(arg[0], "ENDCHAR");
+        endChar = streq(readlineP->arg[0], "ENDCHAR");
     }                        
 }
 
 
 
 static void
-validateEncoding(const char **  const arg,
-                 unsigned int * const codepointP,
-                 bool *         const badCodepointP) {
+interpEncoding(const char **  const arg,
+               unsigned int * const codepointP,
+               bool *         const badCodepointP) {
 /*----------------------------------------------------------------------------
    With arg[] being the ENCODING statement from the font, return as
    *codepointP the codepoint that it indicates (code point is the character
@@ -1323,44 +1400,57 @@ validateEncoding(const char **  const arg,
 
 
 
-
 static void
-processCharsLine(FILE *        const fp,
-                 const char ** const arg,
-                 struct font * const fontP) {
+readEncoding(readline *     const readlineP,
+             unsigned int * const codepointP,
+             bool *         const badCodepointP) {
+
+    readExpectedStatement(readlineP, "ENCODING");
+    
+    interpEncoding(readlineP->arg, codepointP, badCodepointP);
+}
+
 
+
+static void
+processChars(readline *    const readlineP,
+             struct font * const fontP) {
+/*----------------------------------------------------------------------------
+   Process the CHARS block in a BDF font file, assuming the file is positioned
+   just after the CHARS line.  Read the rest of the block and apply its
+   contents to *fontP.
+-----------------------------------------------------------------------------*/
     unsigned int nCharacters;
     unsigned int nCharsDone;
 
-    if (!arg[1])
+    if (!readlineP->arg[1])
         pm_error("Invalid CHARS line - no arguments");
 
-    nCharacters = atoi(arg[1]);
+    nCharacters = atoi(readlineP->arg[1]);
 
     nCharsDone = 0;
 
     while (nCharsDone < nCharacters) {
-        char line[1024];
-        const char * arg[32];
-        int rc;
+        bool eof;
 
-        rc = readline(fp, line, arg);
-        if (rc < 0)
+        readline_read(readlineP, &eof);
+        if (eof)
             pm_error("End of file after CHARS reading BDF font file");
 
-        if (streq(arg[0], "COMMENT")) {
+        if (streq(readlineP->arg[0], "COMMENT")) {
             /* ignore */
-        } else if (!streq(arg[0], "STARTCHAR"))
+        } else if (!streq(readlineP->arg[0], "STARTCHAR"))
             pm_error("no STARTCHAR after CHARS in BDF font file");
-        else if (!arg[1])
+        else if (!readlineP->arg[1])
             pm_error("Invalid STARTCHAR - no arguments");
         else {
-            const char * const charName = arg[1];
+            const char * const charName = readlineP->arg[1];
+
             struct glyph * glyphP;
             unsigned int codepoint;
             bool badCodepoint;
 
-            assert(streq(arg[0], "STARTCHAR"));
+            assert(streq(readlineP->arg[0], "STARTCHAR"));
 
             MALLOCVAR(glyphP);
 
@@ -1368,52 +1458,39 @@ processCharsLine(FILE *        const fp,
                 pm_error("no memory for font glyph for '%s' character",
                          charName);
 
-            {
-                const char * arg[32];
-                expect(fp, "ENCODING", arg, line);
+            readEncoding(readlineP, &codepoint, &badCodepoint);
 
-                validateEncoding(arg, &codepoint, &badCodepoint);
-            }
             if (badCodepoint)
-                skipCharacter(fp);
+                skipCharacter(readlineP);
             else {
-                {
-                    const char * arg[32];
-                    expect(fp, "SWIDTH", arg, line);
-                }
-                {
-                    const char * arg[32];
-                    
-                    expect(fp, "DWIDTH", arg, line);
-                    if (!arg[1])
-                        pm_error("Invalid DWIDTH statement - no arguments");
-                    glyphP->xadd = atoi(arg[1]);
-                }
-                {
-                    const char * arg[32];
+                readExpectedStatement(readlineP, "SWIDTH");
                     
-                    expect(fp, "BBX", arg, line);
-                    if (!arg[1])
-                        pm_error("Invalid BBX statement - no arguments");
-                    glyphP->width  = atoi(arg[1]);
-                    if (!arg[2])
-                        pm_error("Invalid BBX statement - only 1 argument");
-                    glyphP->height = atoi(arg[2]);
-                    if (!arg[3])
-                        pm_error("Invalid BBX statement - only 2 arguments");
-                    glyphP->x      = atoi(arg[3]);
-                    if (!arg[4])
-                        pm_error("Invalid BBX statement - only 3 arguments");
-                    glyphP->y      = atoi(arg[4]);
-                }
-                createBmap(glyphP->width, glyphP->height, fp, charName,
+                readExpectedStatement(readlineP, "DWIDTH");
+                if (!readlineP->arg[1])
+                    pm_error("Invalid DWIDTH statement - no arguments");
+                glyphP->xadd = atoi(readlineP->arg[1]);
+
+                readExpectedStatement(readlineP, "BBX");
+                if (!readlineP->arg[1])
+                    pm_error("Invalid BBX statement - no arguments");
+                glyphP->width  = atoi(readlineP->arg[1]);
+                if (!readlineP->arg[2])
+                    pm_error("Invalid BBX statement - only 1 argument");
+                glyphP->height = atoi(readlineP->arg[2]);
+                if (!readlineP->arg[3])
+                    pm_error("Invalid BBX statement - only 2 arguments");
+                glyphP->x      = atoi(readlineP->arg[3]);
+                if (!readlineP->arg[4])
+                    pm_error("Invalid BBX statement - only 3 arguments");
+                glyphP->y      = atoi(readlineP->arg[4]);
+
+                createBmap(glyphP->width, glyphP->height, readlineP, charName,
                            &glyphP->bmap);
                 
-                {
-                    const char * arg[32];
-                    expect(fp, "ENDCHAR", arg, line);
-                }
-                assert(codepoint < 256); /* Ensured by validateEncoding() */
+
+                readExpectedStatement(readlineP, "ENDCHAR");
+
+                assert(codepoint < 256); /* Ensured by readEncoding() */
 
                 fontP->glyph[codepoint] = glyphP;
             }
@@ -1425,51 +1502,57 @@ processCharsLine(FILE *        const fp,
 
 
 static void
-processFontLine(FILE *        const fp,
-                const char *  const line,
-                const char ** const arg,
-                struct font * const fontP,
-                bool *        const endOfFontP) {
+processBdfFontLine(readline *    const readlineP,
+                   struct font * const fontP,
+                   bool *        const endOfFontP) {
+/*----------------------------------------------------------------------------
+   Process a nonblank line just read from a BDF font file.
 
+   This processing may involve reading more lines.
+-----------------------------------------------------------------------------*/
     *endOfFontP = FALSE;  /* initial assumption */
 
-    if (streq(arg[0], "COMMENT")) {
+    assert(readlineP->arg[0] != NULL);  /* Entry condition */
+
+    if (streq(readlineP->arg[0], "COMMENT")) {
         /* ignore */
-    } else if (streq(arg[0], "SIZE")) {
+    } else if (streq(readlineP->arg[0], "SIZE")) {
         /* ignore */
-    } else if (streq(arg[0], "STARTPROPERTIES")) {
+    } else if (streq(readlineP->arg[0], "STARTPROPERTIES")) {
+        /* Read off the properties and ignore them all */
         unsigned int propCount;
         unsigned int i;
 
-        if (!arg[1])
+        if (!readlineP->arg[1])
             pm_error("Invalid STARTPROPERTIES statement - no arguments");
-        propCount = atoi(arg[1]);
+        propCount = atoi(readlineP->arg[1]);
 
         for (i = 0; i < propCount; ++i) {
-            char line[1024];
-            const char * arg[32];
-            int rc;
-            rc = readline(fp, line, arg);
-            if (rc < 0)
+            bool eof;
+            readline_read(readlineP, &eof);
+            if (eof)
                 pm_error("End of file after STARTPROPERTIES in BDF font file");
         }
-    } else if (streq(arg[0], "FONTBOUNDINGBOX")) {
-        if (!arg[1])
+    } else if (streq(readlineP->arg[0], "FONTBOUNDINGBOX")) {
+        if (!readlineP->arg[1])
             pm_error("Invalid FONTBOUNDINGBOX  statement - no arguments");
-        fontP->maxwidth  = atoi(arg[1]);
-        if (!arg[2])
+        fontP->maxwidth  = atoi(readlineP->arg[1]);
+        if (!readlineP->arg[2])
             pm_error("Invalid FONTBOUNDINGBOX  statement - only 1 argument");
-        fontP->maxheight = atoi(arg[2]);
-        if (!arg[3])
+        fontP->maxheight = atoi(readlineP->arg[2]);
+        if (!readlineP->arg[3])
             pm_error("Invalid FONTBOUNDINGBOX  statement - only 2 arguments");
-        fontP->x         = atoi(arg[3]);
-        if (!arg[4])
+        fontP->x         = atoi(readlineP->arg[3]);
+        if (!readlineP->arg[4])
             pm_error("Invalid FONTBOUNDINGBOX  statement - only 3 arguments");
-        fontP->y         = atoi(arg[4]);
-    } else if (streq(arg[0], "ENDFONT")) {
+        fontP->y         = atoi(readlineP->arg[4]);
+    } else if (streq(readlineP->arg[0], "ENDFONT")) {
         *endOfFontP = true;
-    } else if (!strcmp(arg[0], "CHARS"))
-        processCharsLine(fp, arg, fontP);
+    } else if (streq(readlineP->arg[0], "CHARS")) {
+        processChars(readlineP, fontP);
+    } else {
+        /* ignore */
+    }
 }
 
 
@@ -1477,18 +1560,17 @@ processFontLine(FILE *        const fp,
 struct font *
 pbm_loadbdffont(const char * const name) {
 
-    FILE * fp;
-    char line[1024];
-    const char * arg[32];
+    FILE * ifP;
+    readline readline;
     struct font * fontP;
     bool endOfFont;
 
-    fp = fopen(name, "rb");
-    if (!fp)
+    ifP = fopen(name, "rb");
+    if (!ifP)
         pm_error("Unable to open BDF font file name '%s'.  errno=%d (%s)",
                  name, errno, strerror(errno));
 
-    expect(fp, "STARTFONT", arg, line);
+    readline_init(&readline, ifP);
 
     MALLOCVAR(fontP);
     if (fontP == NULL)
@@ -1500,20 +1582,22 @@ pbm_loadbdffont(const char * const name) {
            find in the bdf file later.
         */
         unsigned int i;
-        for (i = 0; i < 256; i++) 
+        for (i = 0; i < 256; ++i) 
             fontP->glyph[i] = NULL;
     }
     fontP->x = fontP->y = 0;
 
+    readExpectedStatement(&readline, "STARTFONT");
+
     endOfFont = FALSE;
 
     while (!endOfFont) {
-        int rc;
-        rc = readline(fp, line, arg);
-        if (rc < 0)
+        bool eof;
+        readline_read(&readline, &eof);
+        if (eof)
             pm_error("End of file before ENDFONT statement in BDF font file");
 
-        processFontLine(fp, line, arg, fontP, &endOfFont);
+        processBdfFontLine(&readline, fontP, &endOfFont);
     }
     return fontP;
 }
diff --git a/lib/libpbmvms.c b/lib/libpbmvms.c
deleted file mode 100644
index 8a2a54f8..00000000
--- a/lib/libpbmvms.c
+++ /dev/null
@@ -1,734 +0,0 @@
-/***************************************************************************
-  This file contains library routines needed to build Netpbm for VMS.
-  However, as of 2000.05.26, when these were split out of libpbm1.c
-  (where they were all switched by #ifdef VMS), there is no evidence
-  that anyone is building Netpbm for VMS.
-****************************************************************************/
-
-/*
- * @(#)argproc.c 1.0 89/02/01		Mark Pizzolato (mark@infopiz.uucp)	
- */
-
-#ifndef lint
-char argproc_version[] = "@(#)argproc.c VMS uucp Version infopiz-1.0";
-#endif
-
-#include "includes.h"		/* System include files, system dependent */
-
-
-/*
- * getredirection() is intended to aid in porting C programs
- * to VMS (Vax-11 C) which does not have '>' and '<'
- * I/O redirection, along with a command line pipe mechanism
- * using the '|' AND background command execution '&'.
- * The piping mechanism will probably work with almost any 'filter' type
- * of program.  With suitable modification, it may useful for other
- * portability problems as well.
- *
- * Author:  Mark Pizzolato	mark@infopiz.UUCP
- */
-struct list_item
-    {
-    struct list_item *next;
-    char *value;
-    };
-
-int
-getredirection(ac, av)
-int		*ac;
-char		***av;
-/*
- * Process vms redirection arg's.  Exit if any error is seen.
- * If getredirection() processes an argument, it is erased
- * from the vector.  getredirection() returns a new argc and argv value.
- * In the event that a background command is requested (by a trailing "&"),
- * this routine creates a background subprocess, and simply exits the program.
- *
- * Warning: do not try to simplify the code for vms.  The code
- * presupposes that getredirection() is called before any data is
- * read from stdin or written to stdout.
- *
- * Normal usage is as follows:
- *
- *	main(argc, argv)
- *	int		argc;
- *    	char		*argv[];
- *	{
- *		getredirection(&argc, &argv);
- *	}
- */
-{
-    int			argc = *ac;	/* Argument Count	  */
-    char		**argv = *av;	/* Argument Vector	  */
-    char		*ap;   		/* Argument pointer	  */
-    int	       		j;		/* argv[] index		  */
-    extern int		errno;		/* Last vms i/o error 	  */
-    int			item_count = 0;	/* Count of Items in List */
-    int			new_file;	/* flag, true if '>' used */
-    struct list_item 	*list_head = 0;	/* First Item in List	    */
-    struct list_item	*list_tail;	/* Last Item in List	    */
-    char 		*in = NULL;	/* Input File Name	    */
-    char 		*out = NULL;	/* Output File Name	    */
-    char 		*outmode = "w";	/* Mode to Open Output File */
-    int			cmargc = 0;    	/* Piped Command Arg Count  */
-    char		**cmargv = NULL;/* Piped Command Arg Vector */
-    stat_t		statbuf;	/* fstat buffer		    */
-
-    /*
-     * First handle the case where the last thing on the line ends with
-     * a '&'.  This indicates the desire for the command to be run in a
-     * subprocess, so we satisfy that desire.
-     */
-    ap = argv[argc-1];
-    if (0 == strcmp("&", ap))
-	exit(background_process(--argc, argv));
-    if ('&' == ap[strlen(ap)-1])
-	{
-	ap[strlen(ap)-1] = '\0';
-	exit(background_process(argc, argv));
-	}
-    /*
-     * Now we handle the general redirection cases that involve '>', '>>',
-     * '<', and pipes '|'.
-     */
-    for (new_file = 0, j = 0; j < argc; ++j)
-	{
-	if (0 == strcmp("<", argv[j]))
-	    {
-	    if (j+1 >= argc)
-		{
-		errno = EINVAL;
-		perror("No input file");
-		exit(EXIT_ERR);
-		}
-	    in = argv[++j];
-	    continue;
-	    }
-	if ('<' == *(ap = argv[j]))
-	    {
-	    in = 1 + ap;
-	    continue;
-	    }
-	if (0 == strcmp(">", ap))
-	    {
-	    if (j+1 >= argc)
-		{
-		errno = EINVAL;
-		perror("No output file");
-		exit(EXIT_ERR);
-		}
-	    out = argv[++j];
-	    new_file = 1;
-	    continue;
-	    }
-	if ('>' == *ap)
-	    {
-	    if ('>' == ap[1])
-		{
-		outmode = "a";
-		if ('\0' == ap[2])
-		    out = argv[++j];
-		else
-		    out = 2 + ap;
-		}
-	    else
-		{ out = 1 + ap;  new_file = 1; }
-	    continue;
-	    }
-	if (0 == strcmp("|", argv[j]))
-	    {
-	    if (j+1 >= argc)
-		{
-		errno = EPIPE;
-		perror("No command to Pipe to");
-		exit(EXIT_ERR);
-		}
-	    cmargc = argc-(j+1);
-	    cmargv = &argv[j+1];
-	    argc = j;
-	    outmode = "wb";	/* pipes are binary mode devices */
-	    continue;
-	    }
-	if ('|' == *(ap = argv[j]))
-	    {
-	    ++argv[j];
-	    cmargc = argc-j;
-	    cmargv = &argv[j];
-	    argc = j;
-	    outmode = "wb";	/* pipes are binary mode devices */
-	    continue;
-	    }
-	expand_wild_cards(ap, &list_head, &list_tail, &item_count);
-	}
-    /*
-     * Allocate and fill in the new argument vector, Some Unix's terminate
-     * the list with an extra null pointer.
-     */
-    argv = *av = calloc(item_count+1, sizeof(char *));
-    for (j = 0; j < item_count; ++j, list_head = list_head->next)
-	argv[j] = list_head->value;
-    *ac = item_count;
-    if (cmargv != NULL)
-	{
-	char subcmd[1024];
-	static char *pipe_and_fork();
-
-	if (out != NULL)
-	    {
-	    errno = EINVAL;
-	    perror("Invalid '|' and '>' specified");
-	    exit(EXIT_ERR);
-	    }
-	strcpy(subcmd, cmargv[0]);
-	for (j = 1; j < cmargc; ++j)
-	    {
-	    strcat(subcmd, " \"");
-	    strcat(subcmd, cmargv[j]);
-	    strcat(subcmd, "\"");
-	    }
-	out = pipe_and_fork(subcmd);
-	outmode = "wb";
-	}
-	
-    /* Check for input from a pipe (mailbox) */
-
-    if(fstat(0, &statbuf) == 0){
-	if(strncmp(statbuf.st_dev, "MB", 2) == 0 || 
-	    strncmp(statbuf.st_dev, "_MB", 3) == 0){
-
-	    /* Input from a pipe, reopen it in binary mode to disable	*/
-	    /* carriage control processing.				*/
-
-	    if (in != NULL){
-		errno = EINVAL;
-		perror("Invalid '|' and '<' specified");
-		exit(EXIT_ERR);
-		}
-	    freopen(statbuf.st_dev, "rb", stdin);
-	    }
-	}
-    else {
-	perror("fstat failed");
-	exit(EXIT_ERR);
-	}
-
-#ifdef __ALPHA
-        /*, "mbc=32", "mbf=2"))) blows up on the ALPHA cbm 11/08/92 */
-    if ((in != NULL) && (NULL == freopen(in, "r", stdin)))
-        {
-#else
-    if ((in != NULL) && (NULL == freopen(in, "r", stdin, "mbc=32", "mbf=2")))
-	{
-#endif
-	perror(in);    	       	/* Can't find file		*/
-	exit(EXIT_ERR);		/* Is a fatal error		*/
-	}
-#ifdef __ALPHA
-    if ((out != NULL) && (NULL == freopen(out, outmode, stdout)))
-        {
-#else
-    if ((out != NULL) && (NULL == freopen(out, outmode, stdout, "mbc=32", "mbf=2")))
-	{	
-#endif
-	perror(ap);		/* Error, can't write or append	*/
-	exit(EXIT_ERR);		/* Is a fatal error		*/
-	}
-
-     if ( new_file ) {
-	/*
-	 * We are making an explicit output file, fstat the file and
-         * declare exit handler to be able change the file to fixed length
-	 * records if necessary. 
-	 */
-	char fname[256];
-	static int outfile_rundown(), check_outfile_filetype();
-	static stat_t ofile_stat;
-	static struct exit_control_block {
-    	    struct exit_control_block *flink;
-    	    int	(*exit_routine)();
-	    int arg_count;
-	    int *status_address;	/* arg 1 */
-	    stat_t *stat;		/* arg 2 */
-	    int exit_status;
-	    int skew[128];
-	} exit_block = 
-	    { 0, outfile_rundown, 2, &exit_block.exit_status, &ofile_stat, 0 };
-
-	if ( fstat ( fileno(stdout), &ofile_stat ) == 0 )
-	     sys$dclexh ( &exit_block );
-	else fprintf(stderr,"Error fstating stdout - %s\n",
-		strerror(errno,vaxc$errno) );
-
-	if ( fgetname ( stdout, fname, 0 ) ) check_outfile_filetype ( fname );
-     }
-#ifdef DEBUG
-    fprintf(stderr, "Arglist:\n");
-    for (j = 0; j < *ac;  ++j)
-	fprintf(stderr, "argv[%d] = '%s'\n", j, argv[j]);
-#endif
-}
-
-static int binary_outfile = 0;
-void set_outfile_binary() { binary_outfile = 1; return; }
-
-/*
- * Check if output file should be set to binary (fixed 512) on exit based
- * upon the filetype.
- */
-static int check_outfile_filetype ( name )
-    char *name;
-{
-    char *binary_filetypes, *ext, *p, *t;
-    binary_filetypes = (char *) getenv ( "PBM_BINARY_FILETYPES" );
-    if ( binary_filetypes == NULL ) return 0;
-
-    ext = strchr ( name, '.' );  if ( ext == NULL ) return 0;
-    ext++;
-    t = strrchr ( name, '.' );   if ( t != NULL ) *t = '\0';
-
-    for ( p = binary_filetypes; *p != '\0'; p++ ) {
-	for ( t = p;
-	      (*p != '\0' ) && (strchr ( ", 	", *p ) == NULL); 
-	     p++ ) *p = toupper(*p);
-	*p = '\0';
-
-	if ( strcmp ( t, ext ) == 0 ) {
-	    binary_outfile = 1;
-	    break;
-	}
-    }
-    return binary_outfile;
-}
-
-
-/*
- * Exit handler to set output file to binary on image exit.
- */
-static int outfile_rundown ( reason, statbuf )
-    int *reason;
-    stat_t *statbuf;
-{
-    int code, channel, status, LIB$GETDVI(), sys$assign(), sys$qiow();
-    long int fib_desc[2], devchar;
-    short int iosb[4];
-    $DESCRIPTOR(device, statbuf->st_dev);
-    struct fibdef fib;		/* File information block (XQP) */
-    struct atrdef atr[2];
-    struct fat {
-      unsigned char rtype, ratattrib;
-      unsigned short int rsize, hiblk[2], efblk[2], ffbyte, maxrec, defext, gbc;
-      unsigned short int reserved[4], versions;
-    } rat;
-
-    if ( !binary_outfile ) return 1;	/* leave file alone */
-    /*
-     * Assign channel to device listed in stat block and test if it is
-     * a directory structured device, returning if not.
-     */
-    device.dsc$w_length = strlen ( statbuf->st_dev );
-    status = sys$assign ( &device, &channel, 0, 0 );
-    if ((status & 1) == 0) { fprintf(stderr, 
-	"assign error to %s (%d)\n", device.dsc$a_pointer, status);
-		return status; }
-    code = DVI$_DEVCHAR;
-    status = LIB$GETDVI ( &code, &channel, 0, &devchar );
-    if ((status & 1) == 0) { fprintf(stderr, "getdvi error: %d\n", status);
-		return status; }
-    if ( (devchar & DEV$M_DIR) == 0 ) return 1;
-    /*
-     * Build File Information Block and Atrribute block.
-     */
-#ifdef __ALPHA
-    fib.fib$w_fid[0] = statbuf->st_ino[0];
-    fib.fib$w_fid[1] = statbuf->st_ino[1];
-    fib.fib$w_fid[2] = statbuf->st_ino[2];
-#else
-    fib.fib$r_fid_overlay.fib$w_fid[0] = statbuf->st_ino[0];
-    fib.fib$r_fid_overlay.fib$w_fid[1] = statbuf->st_ino[1];
-    fib.fib$r_fid_overlay.fib$w_fid[2] = statbuf->st_ino[2];
-#endif
-
-    atr[0].atr$w_size = sizeof(rat);
-    atr[0].atr$w_type = ATR$C_RECATTR;
-    atr[0].atr$l_addr = &rat;
-    atr[1].atr$w_size = 0; atr[1].atr$w_type = 0;
-    /*
-     * Perform access function with read_attributes sub-function.
-     */
-    freopen ( "SYS$OUTPUT:", "a", stdout );
-    fib_desc[0] = 10; fib_desc[1] = (long int) &fib;
-#ifdef __ALPHA
-    fib.fib$l_acctl = FIB$M_WRITE;
-#else
-    fib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_WRITE;
-#endif
-    status = sys$qiow ( 0, channel, IO$_ACCESS|IO$M_ACCESS,
-		 &iosb, 0, 0, &fib_desc, 0, 0, 0, &atr, 0 );
-    /*
-     * If status successful, update record byte and perform a MODIFY.
-     */
-    if ( (status&1) == 1 ) status = iosb[0];
-    if ( (status&1) == 1 ) {
-      rat.rtype = 1;		/* fixed length records */
-      rat.rsize = 512;  	/* 512 byte block recrods */
-      rat.ratattrib = 0;        /* Record attributes: none */
-
-     status = sys$qiow
-	( 0, channel, IO$_MODIFY, &iosb, 0, 0, &fib_desc, 0, 0, 0, &atr, 0 );
-       if ( (status&1) == 1 ) status = iosb[0];
-   }
-   sys$dassgn ( channel );
-   if ( (status & 1) == 0 ) fprintf ( stderr,
-	"Failed to convert output file to binary format, status: %d\n", status);
-   return status;
-}
-
-
-static add_item(head, tail, value, count)
-struct list_item **head;
-struct list_item **tail;
-char *value;
-int *count;
-{
-    if (*head == 0)
-	{
-	if (NULL == (*head = calloc(1, sizeof(**head))))
-	    {
-	    errno = ENOMEM;
-	    perror("");
-	    exit(EXIT_ERR);
-	    }
-	*tail = *head;
-	}
-    else
-	if (NULL == ((*tail)->next = calloc(1, sizeof(**head))))
-	    {
-	    errno = ENOMEM;
-	    perror("");
-	    exit(EXIT_ERR);
-	    }
-	else
-	    *tail = (*tail)->next;
-    (*tail)->value = value;
-    ++(*count);
-}
-
-static expand_wild_cards(item, head, tail, count)
-char *item;
-struct ltem_list **head;
-struct ltem_list **tail;
-int *count;
-{
-int expcount = 0;
-int context = 0;
-int status;
-int status_value;
-int had_version;
-$DESCRIPTOR(filespec, item);
-$DESCRIPTOR(defaultspec, "SYS$DISK:[]*.*;");
-$DESCRIPTOR(resultspec, "");
-
-    if (strcspn(item, "*%") == strlen(item))
-	{
-	add_item(head, tail, item, count);
-	return;
-	}
-    resultspec.dsc$b_dtype = DSC$K_DTYPE_T;
-    resultspec.dsc$b_class = DSC$K_CLASS_D;
-    resultspec.dsc$a_pointer = NULL;
-    filespec.dsc$w_length = strlen(item);
-    /*
-     * Only return version specs, if the caller specified a version
-     */
-    had_version = strchr(item, ';');
-    while (1 == (1&lib$find_file(&filespec, &resultspec, &context,
-    				 &defaultspec, 0, &status_value, &0)))
-	{
-	char *string;
-	char *c;
-
-	if (NULL == (string = calloc(1, resultspec.dsc$w_length+1)))
-	    {
-	    errno = ENOMEM;
-	    perror("");
-	    exit(EXIT_ERR);
-	    }
-	strncpy(string, resultspec.dsc$a_pointer, resultspec.dsc$w_length);
-	string[resultspec.dsc$w_length] = '\0';
-	if (NULL == had_version)
-	    *((char *)strrchr(string, ';')) = '\0';
-	/*
-	 * Be consistent with what the C RTL has already done to the rest of
-	 * the argv items and lowercase all of these names.
-	 */
-	for (c = string; *c; ++c)
-	    if (isupper(*c))
-		*c = tolower(*c);
-	add_item(head, tail, string, count);
-	++expcount;
-	}
-    if (expcount == 0)
-	add_item(head, tail, item, count);
-    lib$sfree1_dd(&resultspec);
-    lib$find_file_end(&context);
-}
-
-static int child_st[2];	/* Event Flag set when child process completes	*/
-
-static short child_chan;/* I/O Channel for Pipe Mailbox			*/
-
-static exit_handler(status)
-int *status;
-{
-short iosb[4];
-
-    if (0 == child_st[0])
-	{
-#ifdef DEBUG
-	fprintf(stderr, "Waiting for Child Process to Finnish . . .\n");
-#endif
-	fflush(stdout);	    /* Have to flush pipe for binary data to	*/
-			    /* terminate properly -- <tp@mccall.com>	*/
-#ifdef DEBUG
-	fprintf(stderr, "    stdout flushed. . .\n");
-#endif
-	sys$qio(0, child_chan, IO$_WRITEOF, iosb, 0, 0, 0, 0, 0, 0, 0, 0);
-#ifdef DEBUG
-	fprintf(stderr, "    Pipe terminated. . .\n");
-#endif
-	fclose(stdout);
-#ifdef DEBUG
-	fprintf(stderr, "    stdout closed. . .\n");
-#endif
-	sys$synch(0, child_st);
-	sys$dassgn(child_chan);
-	}
-#ifdef DEBUG
-	fprintf(stderr, "    sync done. . .\n");
-#endif
-}
-
-#include <syidef>		/* System Information Definitions	*/
-
-static sig_child(chan)
-int chan;
-{
-#ifdef DEBUG
-    fprintf(stderr, "Child Completion AST, st: %x\n", child_st[0] );
-#endif
-    if (child_st[0] == 0)
-	{ child_st[0] = 1; }
-    sys$setef ( 0 );
-}
-
-static struct exit_control_block
-    {
-    struct exit_control_block *flink;
-    int	(*exit_routine)();
-    int arg_count;
-    int *status_address;
-    int exit_status;
-    } exit_block =
-    {
-    0,
-    exit_handler,
-    1,
-    &exit_block.exit_status,
-    0
-    };
-
-static char *pipe_and_fork(cmd)
-char *cmd;
-{
-    $DESCRIPTOR(cmddsc, cmd);
-    static char mbxname[64], ef = 0;
-    $DESCRIPTOR(mbxdsc, mbxname);
-    short iosb[4];
-    int status;
-    int pid;
-    struct
-	{
-	short dna_buflen;
-	short dna_itmcod;
-	char *dna_buffer;
-	short *dna_retlen;
-	int listend;
-	} itmlst =
-	{
-	sizeof(mbxname),
-	DVI$_DEVNAM,
-	mbxname,
-	&mbxdsc.dsc$w_length,
-	0
-	};
-    int mbxsize;
-    struct
-	{
-	short mbf_buflen;
-	short mbf_itmcod;
-	int *mbf_maxbuf;
-	short *mbf_retlen;
-	int listend;
-	} syiitmlst =
-	{
-	sizeof(mbxsize),
-	SYI$_MAXBUF,
-	&mbxsize,
-	0,
-	0
-	};
-
-    cmddsc.dsc$w_length = strlen(cmd);
-    /*
-     * Get the SYSGEN parameter MAXBUF, and the smaller of it and 2048 as
-     * the size of the 'pipe' mailbox.
-     */
-    if (1 == (1&(vaxc$errno = sys$getsyiw(0, 0, 0, &syiitmlst, iosb, 0, 0, 0))))
-	vaxc$errno = iosb[0];
-    if (0 == (1&vaxc$errno))
-	{
- 	errno = EVMSERR;
-	perror("Can't get SYSGEN parameter value for MAXBUF");
-	exit(EXIT_ERR);
-	}
-    if (mbxsize > 2048)
-	mbxsize = 2048;
-    if (0 == (1&(vaxc$errno = sys$crembx(0, &child_chan, mbxsize, mbxsize, 0, 0, 0))))
-	{
-	errno = EVMSERR;
-	perror("Can't create pipe mailbox");
-	exit(EXIT_ERR);
-	}
-    if (1 == (1&(vaxc$errno = sys$getdviw(0, child_chan, 0, &itmlst, iosb,
-    					  0, 0, 0))))
-	vaxc$errno = iosb[0];
-    if (0 == (1&vaxc$errno))
-	{
- 	errno = EVMSERR;
-	perror("Can't get pipe mailbox device name");
-	exit(EXIT_ERR);
-	}
-    mbxname[mbxdsc.dsc$w_length] = '\0';
-#ifdef DEBUG
-    fprintf(stderr, "Pipe Mailbox Name = '%s'\n", mbxname);
-#endif
-    if (0 == (1&(vaxc$errno = lib$spawn(&cmddsc, &mbxdsc, 0, &1,
-    					0, &pid, child_st, &ef, sig_child,
-    					&child_chan))))
-	{
-	errno = EVMSERR;
-	perror("Can't spawn subprocess");
-	exit(EXIT_ERR);
-	}
-#ifdef DEBUG
-    fprintf(stderr, "Subprocess's Pid = %08X\n", pid);
-#endif
-    sys$dclexh(&exit_block);
-    return(mbxname);
-}
-
-background_process(argc, argv)
-int argc;
-char **argv;
-{
-char command[2048] = "$";
-$DESCRIPTOR(value, command);
-$DESCRIPTOR(cmd, "BACKGROUND$COMMAND");
-$DESCRIPTOR(null, "NLA0:");
-int pid;
-
-    strcat(command, argv[0]);
-    while (--argc)
-	{
-	strcat(command, " \"");
-	strcat(command, *(++argv));
-	strcat(command, "\"");
-	}
-    value.dsc$w_length = strlen(command);
-    if (0 == (1&(vaxc$errno = lib$set_symbol(&cmd, &value))))
-	{
-	errno = EVMSERR;
-	perror("Can't create symbol for subprocess command");
-	exit(EXIT_ERR);
-	}
-    if (0 == (1&(vaxc$errno = lib$spawn(&cmd, &null, 0, &17, 0, &pid))))
-	{
-	errno = EVMSERR;
-	perror("Can't spawn subprocess");
-	exit(EXIT_ERR);
-	}
-#ifdef DEBUG
-    fprintf(stderr, "%s\n", command);
-#endif
-    fprintf(stderr, "%08X\n", pid);
-    return(EXIT_OK);
-}
-
-/* got this off net.sources */
-
-#ifdef	VMS
-#define	index	strchr
-#endif	/*VMS*/
-
-/*
- * get option letter from argument vector
- */
-int	opterr = 1,		/* useless, never set or used */
-	optind = 1,		/* index into parent argv vector */
-	optopt;			/* character checked for validity */
-char	*optarg;		/* argument associated with option */
-
-#define BADCH	(int)'?'
-#define EMSG	""
-#define tell(s)	fputs(progname,stderr);fputs(s,stderr); \
-		fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);
-
-getopt(nargc,nargv,ostr)
-int	nargc;
-char	**nargv,
-	*ostr;
-{
-	static char	*place = EMSG;	/* option letter processing */
-	register char	*oli;		/* option letter list index */
-	char	*index();
-	char *progname;
-
-	if(!*place) {			/* update scanning pointer */
-		if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF);
-		if (*place == '-') {	/* found "--" */
-			++optind;
-			return(EOF);
-		}
-	}				/* option letter okay? */
-	if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) {
-		if(!*place) ++optind;
-#ifdef VMS
-		progname = strrchr(nargv[0],']');
-#else
-		progname = rindex(nargv[0],'/');
-#endif
-		if (!progname) progname = nargv[0]; else progname++;
-		tell(": illegal option -- ");
-	}
-	if (*++oli != ':') {		/* don't need argument */
-		optarg = NULL;
-		if (!*place) ++optind;
-	}
-	else {				/* need an argument */
-		if (*place) optarg = place;	/* no white space */
-		else if (nargc <= ++optind) {	/* no arg */
-			place = EMSG;
-#ifdef VMS
-			progname = strrchr(nargv[0],']');
-#else
-			progname = rindex(nargv[0],'/');
-#endif
-			if (!progname) progname = nargv[0]; else progname++;
-			tell(": option requires an argument -- ");
-		}
-	 	else optarg = nargv[optind];	/* white space */
-		place = EMSG;
-		++optind;
-	}
-	return(optopt);			/* dump back option letter */
-}
diff --git a/lib/libpgm1.c b/lib/libpgm1.c
index 4d93e4be..9ef5d2c1 100644
--- a/lib/libpgm1.c
+++ b/lib/libpgm1.c
@@ -20,6 +20,9 @@
 #include <stdio.h>
 #include <errno.h>
 
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+
 #include "pgm.h"
 #include "libpgm.h"
 #include "pbm.h"
@@ -27,8 +30,6 @@
 #include "pam.h"
 #include "libpam.h"
 #include "fileio.h"
-#include "mallocvar.h"
-#include "nstring.h"
 
 
 gray *
@@ -100,10 +101,13 @@ validateComputableSize(unsigned int const cols,
    you expect.  That failed expectation can be disastrous if you use
    it to allocate memory.
 
+   It is very normal to allocate space for a pixel row, so we make sure
+   the size of a pixel row, in bytes, can be represented by an 'int'.
+
    A common operation is adding 1 or 2 to the highest row or
    column number in the image, so we make sure that's possible.
 -----------------------------------------------------------------------------*/
-    if (cols > INT_MAX - 2)
+    if (cols > INT_MAX / (sizeof(gray)) || cols > INT_MAX - 2)
         pm_error("image width (%u) too large to be processed", cols);
     if (rows > INT_MAX - 2)
         pm_error("image height (%u) too large to be processed", rows);
@@ -164,7 +168,8 @@ pgm_readpgminit(FILE * const fileP,
         break;
 
     default:
-        pm_error("bad magic number - not a Netpbm file");
+        pm_error("bad magic number 0x%x - not a PPM, PGM, PBM, or PAM file",
+                 realFormat);
     }
     validateComputableSize(*colsP, *rowsP);
 }
@@ -172,11 +177,44 @@ pgm_readpgminit(FILE * const fileP,
 
 
 static void
+validateRpgmRow(gray *         const grayrow, 
+                unsigned int   const cols,
+                gray           const maxval,
+                const char **  const errorP) {
+/*----------------------------------------------------------------------------
+  Check for sample values above maxval in input.  
+
+  Note: a program that wants to deal with invalid sample values itself can
+  simply make sure it uses a sufficiently high maxval on the read function
+  call, so this validation never fails.
+-----------------------------------------------------------------------------*/
+    if (maxval == 255 || maxval == 65535) {
+        /* There's no way a sample can be invalid, so we don't need to look at
+           the samples individually.
+        */
+        *errorP = NULL;
+    } else {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            if (grayrow[col] > maxval) {
+                pm_asprintf(errorP,
+                            "gray value %u is greater than maxval (%u)",
+                            grayrow[col], maxval);
+                return;
+            }
+        }
+        *errorP = NULL;
+    }
+}  
+
+
+
+static void
 readRpgmRow(FILE * const fileP,
-               gray * const grayrow, 
-               int    const cols,
-               gray   const maxval,
-               int    const format) {
+            gray * const grayrow, 
+            int    const cols,
+            gray   const maxval,
+            int    const format) {
 
     unsigned int const bytesPerSample = maxval < 256 ? 1 : 2;
     int          const bytesPerRow    = cols * bytesPerSample;
@@ -186,19 +224,18 @@ readRpgmRow(FILE * const fileP,
     
     MALLOCARRAY(rowBuffer, bytesPerRow);
     if (rowBuffer == NULL)
-        asprintfN(&error, "Unable to allocate memory for row buffer "
-                  "for %u columns", cols);
+        pm_asprintf(&error, "Unable to allocate memory for row buffer "
+                    "for %u columns", cols);
     else {
-        ssize_t rc;
+        size_t rc;
         rc = fread(rowBuffer, 1, bytesPerRow, fileP);
         if (rc == 0)
-            asprintfN(&error, "Error reading row.  fread() errno=%d (%s)",
-                      errno, strerror(errno));
+            pm_asprintf(&error, "Error reading row.  fread() errno=%d (%s)",
+                        errno, strerror(errno));
         else if (rc != bytesPerRow)
-            asprintfN(&error, "Error reading row.  Short read of %u bytes "
-                      "instead of %u", rc, bytesPerRow);
+            pm_asprintf(&error, "Error reading row.  Short read of %u bytes "
+                        "instead of %u", (unsigned)rc, bytesPerRow);
         else {
-            error = NULL;
             if (maxval < 256) {
                 unsigned int col;
                 for (col = 0; col < cols; ++col)
@@ -218,12 +255,13 @@ readRpgmRow(FILE * const fileP,
                     grayrow[col] = g;
                 }
             }
+            validateRpgmRow(grayrow, cols, maxval, &error); 
         }
         free(rowBuffer);
     }
     if (error) {
         pm_errormsg("%s", error);
-        strfree(error);
+        pm_strfree(error);
         pm_longjmp();
     }
 } 
@@ -241,7 +279,7 @@ readPbmRow(FILE * const fileP,
     jmp_buf * origJmpbufP;
     bit * bitrow;
     
-    bitrow = pbm_allocrow(cols);
+    bitrow = pbm_allocrow_packed(cols);
     if (setjmp(jmpbuf) != 0) {
         pbm_freerow(bitrow);
         pm_setjmpbuf(origJmpbufP);
@@ -251,10 +289,14 @@ readPbmRow(FILE * const fileP,
 
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
-        pbm_readpbmrow(fileP, bitrow, cols, format);
-        for (col = 0; col < cols; ++col)
-            grayrow[col] = (bitrow[col] == PBM_WHITE ) ? maxval : 0;
+        pbm_readpbmrow_packed(fileP, bitrow, cols, format);
 
+        for (col = 0; col < cols; ++col) {
+            grayrow[col] =
+                ((bitrow[col/8] >> (7 - col%8)) & 0x1) == PBM_WHITE ?
+                maxval : 0
+                ;
+        }
         pm_setjmpbuf(origJmpbufP);
     }
     pbm_freerow(bitrow);
@@ -338,28 +380,30 @@ pgm_readpgm(FILE * const fileP,
 
 void
 pgm_check(FILE *               const file, 
-          enum pm_check_type   const check_type, 
+          enum pm_check_type   const checkType, 
           int                  const format, 
           int                  const cols, 
           int                  const rows, 
           gray                 const maxval,
-          enum pm_check_code * const retval_p) {
+          enum pm_check_code * const retvalP) {
 
     if (rows < 0)
         pm_error("Invalid number of rows passed to pgm_check(): %d", rows);
     if (cols < 0)
         pm_error("Invalid number of columns passed to pgm_check(): %d", cols);
     
-    if (check_type != PM_CHECK_BASIC) {
-        if (retval_p) *retval_p = PM_CHECK_UNKNOWN_TYPE;
+    if (checkType != PM_CHECK_BASIC) {
+        if (retvalP)
+            *retvalP = PM_CHECK_UNKNOWN_TYPE;
     } else if (PGM_FORMAT_TYPE(format) == PBM_TYPE) {
-        pbm_check(file, check_type, format, cols, rows, retval_p);
+        pbm_check(file, checkType, format, cols, rows, retvalP);
     } else if (format != RPGM_FORMAT) {
-        if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE;
+        if (retvalP)
+            *retvalP = PM_CHECK_UNCHECKABLE;
     } else {        
-        pm_filepos const bytes_per_row = cols * (maxval > 255 ? 2 : 1);
-        pm_filepos const need_raster_size = rows * bytes_per_row;
+        pm_filepos const bytesPerRow    = cols * (maxval > 255 ? 2 : 1);
+        pm_filepos const needRasterSize = rows * bytesPerRow;
         
-        pm_check(file, check_type, need_raster_size, retval_p);
+        pm_check(file, checkType, needRasterSize, retvalP);
     }
 }
diff --git a/lib/libpgm2.c b/lib/libpgm2.c
index 650d2cb5..80b5cf42 100644
--- a/lib/libpgm2.c
+++ b/lib/libpgm2.c
@@ -13,8 +13,8 @@
 #include <string.h>
 #include <errno.h>
 
-#include "pm_c_util.h"
-#include "mallocvar.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
 #include "pgm.h"
 
 
@@ -37,10 +37,6 @@ pgm_writepgminit(FILE * const fileP,
             PGM_MAGIC1, 
             plainFormat || maxval >= 1<<16 ? PGM_MAGIC2 : RPGM_MAGIC2, 
             cols, rows, maxval );
-#ifdef VMS
-    if (!plainFormat)
-        set_outfile_binary();
-#endif
 }
 
 
@@ -125,10 +121,12 @@ writepgmrowraw(FILE *       const fileP,
     if (rc < 0)
         pm_error("Error writing row.  fwrite() errno=%d (%s)",
                  errno, strerror(errno));
-    else if (rc != bytesPerRow)
-        pm_error("Error writing row.  Short write of %u bytes "
-                 "instead of %u", rc, bytesPerRow);
-
+    else {
+        size_t const bytesWritten = rc;
+        if (bytesWritten != bytesPerRow)
+            pm_error("Error writing row.  Short write of %u bytes "
+                     "instead of %u", (unsigned)bytesWritten, bytesPerRow);
+    }
     free(rowBuffer);
 }
 
diff --git a/lib/libpm.c b/lib/libpm.c
index 910d5666..4374bbe3 100644
--- a/lib/libpm.c
+++ b/lib/libpm.c
@@ -8,8 +8,12 @@
   Netpbm library subroutines.
 **************************************************************************/
 
+#define _BSD_SOURCE          /* Make sure strdup is in string.h */
 #define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko are defined */
 
+#include "netpbm/pm_config.h"
+
+#include <assert.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <stdarg.h>
@@ -18,13 +22,17 @@
 #include <setjmp.h>
 #include <time.h>
 #include <limits.h>
+#if HAVE_FORK
+#include <sys/wait.h>
+#endif
+#include <sys/types.h>
 
-#include "pm_c_util.h"
-#include "mallocvar.h"
-#include "version.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/version.h"
+#include "netpbm/nstring.h"
+#include "netpbm/shhopt.h"
 #include "compile.h"
-#include "nstring.h"
-#include "shhopt.h"
 
 #include "pm.h"
 
@@ -95,6 +103,86 @@ pm_longjmp(void) {
 
 
 void
+pm_fork(int *         const iAmParentP,
+        pid_t *       const childPidP,
+        const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Same as POSIX fork, except with a nicer interface and works
+   (fails cleanly) on systems that don't have POSIX fork().
+-----------------------------------------------------------------------------*/
+#if HAVE_FORK
+    int rc;
+
+    rc = fork();
+
+    if (rc < 0) {
+        pm_asprintf(errorP, "Failed to fork a process.  errno=%d (%s)",
+                    errno, strerror(errno));
+    } else {
+        *errorP = NULL;
+
+        if (rc == 0) {
+            *iAmParentP = FALSE;
+        } else {
+            *iAmParentP = TRUE;
+            *childPidP = rc;
+        }
+    }
+#else
+    pm_asprintf(errorP, "Cannot fork a process, because this system does "
+                "not have POSIX fork()");
+#endif
+}
+
+
+
+void
+pm_waitpid(pid_t         const pid,
+           int *         const statusP,
+           int           const options,
+           pid_t *       const exitedPidP,
+           const char ** const errorP) {
+
+#if HAVE_FORK
+    pid_t rc;
+    rc = waitpid(pid, statusP, options);
+    if (rc == (pid_t)-1) {
+        pm_asprintf(errorP, "Failed to wait for process exit.  "
+                    "waitpid() errno = %d (%s)",
+                    errno, strerror(errno));
+    } else {
+        *exitedPidP = rc;
+        *errorP = NULL;
+    }
+#else
+    pm_error("INTERNAL ERROR: Attempt to wait for a process we created on "
+             "a system on which we can't create processes");
+#endif
+}
+
+
+
+void
+pm_waitpidSimple(pid_t const pid) {
+
+    int status;
+    pid_t exitedPid;
+    const char * error;
+
+    pm_waitpid(pid, &status, 0, &exitedPid, &error);
+
+    if (error) {
+        pm_errormsg("%s", error);
+        pm_strfree(error);
+        pm_longjmp();
+    } else {
+        assert(exitedPid != 0);
+    }
+}
+
+
+
+void
 pm_setusererrormsgfn(pm_usererrormsgfn * fn) {
 
     userErrorMsgFn = fn;
@@ -126,14 +214,14 @@ pm_message(const char format[], ...) {
 
     if (pm_showmessages) {
         const char * msg;
-        vasprintfN(&msg, format, args);
+        pm_vasprintf(&msg, format, args);
 
         if (userMessageFn)
             userMessageFn(msg);
         else
             fprintf(stderr, "%s: %s\n", pm_progname, msg);
 
-        strfree(msg);
+        pm_strfree(msg);
     }
     va_end(args);
 }
@@ -159,11 +247,11 @@ pm_errormsg(const char format[], ...) {
 
     va_start(args, format);
 
-    vasprintfN(&msg, format, args);
+    pm_vasprintf(&msg, format, args);
     
     errormsg(msg);
 
-    strfree(msg);
+    pm_strfree(msg);
 
     va_end(args);
 }
@@ -177,11 +265,11 @@ pm_error(const char format[], ...) {
 
     va_start(args, format);
 
-    vasprintfN(&msg, format, args);
+    pm_vasprintf(&msg, format, args);
     
     errormsg(msg);
 
-    strfree(msg);
+    pm_strfree(msg);
 
     va_end(args);
 
@@ -190,6 +278,20 @@ pm_error(const char format[], ...) {
 
 
 
+int
+pm_have_float_format(void) {
+/*----------------------------------------------------------------------------
+  Return 1 if %f, %e, and %g work in format strings for pm_message, etc.;
+  0 otherwise.
+
+  Where they don't "work," that means the specifier just appears itself in
+  the formatted strings, e.g. "the number is g".
+-----------------------------------------------------------------------------*/
+    return pm_vasprintf_knows_float();
+}
+
+
+
 static void *
 mallocz(size_t const size) {
 
@@ -224,122 +326,30 @@ pm_freerow(void * const itrow) {
 
 
 
-static void
-allocarrayNoHeap(unsigned char ** const rowIndex,
-                 unsigned int     const cols,
-                 unsigned int     const rows,
-                 unsigned int     const size,
-                 const char **    const errorP) {
-
-    if (cols != 0 && UINT_MAX / cols < size)
-        asprintfN(errorP,
-                  "Arithmetic overflow multiplying %u by %u to get the "
-                  "size of a row to allocate.", cols, size);
-    else {
-        unsigned int rowsDone;
-
-        rowsDone = 0;
-        *errorP = NULL;
-
-        while (rowsDone < rows && !*errorP) {
-            unsigned char * const rowSpace = mallocz(cols * size);
-            if (rowSpace == NULL)
-                asprintfN(errorP,
-                          "Unable to allocate a %u-column by %u byte row",
-                          cols, size);
-            else
-                rowIndex[rowsDone++] = rowSpace;
-        }
-        if (*errorP) {
-            unsigned int row;
-            for (row = 0; row < rowsDone; ++row)
-                free(rowIndex[row]);
-        }
-    }
-}
-
-
-
-static unsigned char *
-allocRowHeap(unsigned int const cols,
-             unsigned int const rows,
-             unsigned int const size) {
-
-    unsigned char * retval;
-
-    if (cols != 0 && rows != 0 && UINT_MAX / cols / rows < size)
-        /* Too big even to request the memory ! */
-        retval = NULL;
-    else
-        retval = mallocz(rows * cols * size);
-
-    return retval;
-}
-
-
-
 char **
 pm_allocarray(int const cols,
               int const rows,
-              int const size )  {
+              int const elementSize ) {
 /*----------------------------------------------------------------------------
-   Allocate an array of 'rows' rows of 'cols' columns each, with each
-   element 'size' bytes.
-
-   We use a special format where we tack on an extra element to the row
-   index to indicate the format of the array.
-
-   We have two ways of allocating the space: fragmented and
-   unfragmented.  In both, the row index (plus the extra element) is
-   in one block of memory.  In the fragmented format, each row is
-   also in an independent memory block, and the extra row pointer is
-   NULL.  In the unfragmented format, all the rows are in a single
-   block of memory called the row heap and the extra row pointer is
-   the address of that block.
-
-   We use unfragmented format if possible, but if the allocation of the
-   row heap fails, we fall back to fragmented.
------------------------------------------------------------------------------*/
-    unsigned char ** rowIndex;
-    const char * error;
+   This is for backward compatibility.  MALLOCARRAY2 is usually better.
 
-    MALLOCARRAY(rowIndex, rows + 1);
-    if (rowIndex == NULL)
-        asprintfN(&error,
-                  "out of memory allocating row index (%u rows) for an array",
-                  rows);
-    else {
-        unsigned char * rowheap;
+   A problem with pm_allocarray() is that its return type is char **
+   even though 'elementSize' can be other than 1.  So users have
+   traditionally type cast the result.  In the old days, that was just
+   messy; modern compilers can produce the wrong code if you do that.
+-----------------------------------------------------------------------------*/
+    char ** retval;
+    void * result;
 
-        rowheap = allocRowHeap(cols, rows, size);
+    pm_mallocarray2(&result, rows, cols, elementSize);
 
-        if (rowheap) {
-            /* It's unfragmented format */
+    if (result == NULL)
+        pm_error("Failed to allocate a raster array of %u columns x %u rows",
+                 cols, rows);
 
-            rowIndex[rows] = rowheap;  /* Declare it unfragmented format */
+    retval = result;
 
-            if (rowheap) {
-                unsigned int row;
-                
-                for (row = 0; row < rows; ++row)
-                    rowIndex[row] = &(rowheap[row * cols * size]);
-            }
-            error = NULL;
-        } else {
-            /* We couldn't get the whole heap in one block, so try fragmented
-               format.
-            */
-            rowIndex[rows] = NULL;   /* Declare it fragmented format */
-            
-            allocarrayNoHeap(rowIndex, cols, rows, size, &error);
-        }
-    }
-    if (error) {
-        pm_errormsg("Couldn't allocate %u-row array.  %s", rows, error);
-        strfree(error);
-        pm_longjmp();
-    }
-    return (char **)rowIndex;
+    return retval;
 }
 
 
@@ -348,16 +358,9 @@ void
 pm_freearray(char ** const rowIndex, 
              int     const rows) {
 
-    void * const rowheap = rowIndex[rows];
+    void * const rowIndexVoid = rowIndex;
 
-    if (rowheap != NULL)
-        free(rowheap);
-    else {
-        unsigned int row;
-        for (row = 0; row < rows; ++row)
-            pm_freerow(rowIndex[row]);
-    }
-    free(rowIndex);
+    pm_freearray2(rowIndexVoid);
 }
 
 
@@ -475,37 +478,6 @@ pm_lcm(unsigned int const x,
 }
 
 
-/* Initialization. */
-
-
-#ifdef VMS
-static const char *
-vmsProgname(int * const argcP, char * argv[]) {   
-    char **temp_argv = argv;
-    int old_argc = *argcP;
-    int i;
-    const char * retval;
-    
-    getredirection( argcP, &temp_argv );
-    if (*argcP > old_argc) {
-        /* Number of command line arguments has increased */
-        fprintf( stderr, "Sorry!! getredirection() for VMS has "
-                 "changed the argument list!!!\n");
-        fprintf( stderr, "This is intolerable at the present time, "
-                 "so we must stop!!!\n");
-        exit(1);
-    }
-    for (i=0; i<*argcP; i++)
-        argv[i] = temp_argv[i];
-    retval = strrchr( argv[0], '/');
-    if ( retval == NULL ) retval = rindex( argv[0], ']');
-    if ( retval == NULL ) retval = rindex( argv[0], '>');
-
-    return retval;
-}
-#endif
-
-
 
 void
 pm_init(const char * const progname,
@@ -555,11 +527,7 @@ showVersion(void) {
     pm_message( "BSD defined" );
 #endif /*BSD*/
 #ifdef SYSV
-#ifdef VMS
-    pm_message( "VMS & SYSV defined" );
-#else
     pm_message( "SYSV defined" );
-#endif
 #endif /*SYSV*/
 #ifdef MSDOS
     pm_message( "MSDOS defined" );
@@ -626,6 +594,8 @@ showNetpbmHelp(const char progname[]) {
         if (docurl == NULL)
             pm_message("No 'docurl=' line in Netpbm configuration file '%s'.",
                        netpbmConfigFileName);
+
+        fclose(netpbmConfigFile);
     }
     if (docurl == NULL)
         pm_message("We have no reliable indication of where the Netpbm "
@@ -639,71 +609,85 @@ showNetpbmHelp(const char progname[]) {
 
 
 
+static void
+parseCommonOptions(int *         const argcP,
+                   const char ** const argv,
+                   bool *        const showMessagesP,
+                   bool *        const showVersionP,
+                   bool *        const showHelpP,
+                   bool *        const plainOutputP) {
+
+    unsigned int inCursor;
+    unsigned int outCursor;
+
+    *showMessagesP = true;   /* initial assumption */
+    *showVersionP  = false;  /* initial assumption */
+    *showHelpP     = false;  /* initial assumption */
+    *plainOutputP  = false;  /* initial assumption */
+
+    for (inCursor = 1, outCursor = 1; inCursor < *argcP; ++inCursor) {
+            if (strcaseeq(argv[inCursor], "-quiet") ||
+                strcaseeq(argv[inCursor], "--quiet")) 
+                *showMessagesP = false;
+            else if (strcaseeq(argv[inCursor], "-version") ||
+                     strcaseeq(argv[inCursor], "--version")) 
+                *showVersionP = true;
+            else if (strcaseeq(argv[inCursor], "-help") ||
+                     strcaseeq(argv[inCursor], "--help") ||
+                     strcaseeq(argv[inCursor], "-?")) 
+                *showHelpP = true;
+            else if (strcaseeq(argv[inCursor], "-plain") ||
+                     strcaseeq(argv[inCursor], "--plain"))
+                *plainOutputP = true;
+            else
+                argv[outCursor++] = argv[inCursor];
+        }
+    *argcP = outCursor;
+}
+
+
+
 void
-pm_proginit(int * const argcP, const char * argv[]) {
+pm_proginit(int *         const argcP,
+            const char ** const argv) {
 /*----------------------------------------------------------------------------
    Do various initialization things that all programs in the Netpbm package,
    and programs that emulate such programs, should do.
 
-   This includes processing global options.
+   This includes processing global options.  We scan argv[], which has *argcP
+   elements, for common options and execute the functions for the ones we
+   find.  We remove the common options from argv[], updating *argcP
+   accordingly.
 
    This includes calling pm_init() to initialize the Netpbm libraries.
 -----------------------------------------------------------------------------*/
-    int argn, i;
-    const char * progname;
-    bool showmessages;
-    bool show_version;
+    const char * const progname = pm_arg0toprogname(argv[0]);
+        /* points to static memory in this library */
+    bool showMessages;
+    bool plain;
+    bool justShowVersion;
         /* We're supposed to just show the version information, then exit the
            program.
         */
-    bool show_help;
+    bool justShowHelp;
         /* We're supposed to just tell user where to get help, then exit the
            program.
         */
-    
-    /* Extract program name. */
-#ifdef VMS
-    progname = vmsProgname(argcP, argv);
-#else
-    progname = strrchr( argv[0], '/');
-#endif
-    if (progname == NULL)
-        progname = argv[0];
-    else
-        ++progname;
 
     pm_init(progname, 0);
 
-    /* Check for any global args. */
-    showmessages = TRUE;
-    show_version = FALSE;
-    show_help = FALSE;
-    pm_plain_output = FALSE;
-    for (argn = i = 1; argn < *argcP; ++argn) {
-        if (pm_keymatch(argv[argn], "-quiet", 6) ||
-            pm_keymatch(argv[argn], "--quiet", 7)) 
-            showmessages = FALSE;
-        else if (pm_keymatch(argv[argn], "-version", 8) ||
-                   pm_keymatch(argv[argn], "--version", 9)) 
-            show_version = TRUE;
-        else if (pm_keymatch(argv[argn], "-help", 5) ||
-                 pm_keymatch(argv[argn], "--help", 6) ||
-                 pm_keymatch(argv[argn], "-?", 2)) 
-            show_help = TRUE;
-        else if (pm_keymatch(argv[argn], "-plain", 6) ||
-                 pm_keymatch(argv[argn], "--plain", 7))
-            pm_plain_output = TRUE;
-        else
-            argv[i++] = argv[argn];
-    }
-    *argcP=i;
+    parseCommonOptions(argcP, argv,
+                       &showMessages, &justShowVersion, &justShowHelp,
+                       &plain);
 
-    pm_setMessage((unsigned int) showmessages, NULL);
+    pm_plain_output = plain ? 1 : 0;
 
-    if (show_version) {
+    pm_setMessage(showMessages ? 1 : 0, NULL);
+
+    if (justShowVersion) {
         showVersion();
-        exit( 0 );
-    } else if (show_help) {
+        exit(0);
+    } else if (justShowHelp) {
         pm_error("Use 'man %s' for help.", progname);
         /* If we can figure out a way to distinguish Netpbm programs from 
            other programs using the Netpbm libraries, we can do better here.
@@ -715,8 +699,10 @@ pm_proginit(int * const argcP, const char * argv[]) {
 }
 
 
+
 void
-pm_setMessage(int const newState, int * const oldStateP) {
+pm_setMessage(int   const newState,
+              int * const oldStateP) {
     
     if (oldStateP)
         *oldStateP = pm_showmessages;
@@ -726,6 +712,39 @@ pm_setMessage(int const newState, int * const oldStateP) {
 
 
 
+int
+pm_getMessage(void) {
+
+    return pm_showmessages;
+}
+
+
+
+static void
+extractAfterLastSlash(const char * const fullPath,
+                      char *       const retval,
+                      size_t       const retvalSize) {
+    
+    char * slashPos;
+
+    /* Chop any directories off the left end */
+    slashPos = strrchr(fullPath, '/');
+
+    if (slashPos == NULL) {
+        strncpy(retval, fullPath, retvalSize);
+        retval[retvalSize-1] = '\0';
+    } else {
+        strncpy(retval, slashPos +1, retvalSize);
+        retval[retvalSize-1] = '\0';
+    }
+
+    /* Chop any .exe off the right end */
+    if (strlen(retval) >= 4 && strcmp(retval+strlen(retval)-4, ".exe") == 0)
+        retval[strlen(retval)-4] = 0;
+}
+
+
+
 char *
 pm_arg0toprogname(const char arg0[]) {
 /*----------------------------------------------------------------------------
@@ -742,25 +761,23 @@ pm_arg0toprogname(const char arg0[]) {
    The return value is in static storage within.  It is NUL-terminated,
    but truncated at 64 characters.
 -----------------------------------------------------------------------------*/
-    static char retval[64+1];
-    char *slash_pos;
-
-    /* Chop any directories off the left end */
-    slash_pos = strrchr(arg0, '/');
-
-    if (slash_pos == NULL) {
-        strncpy(retval, arg0, sizeof(retval));
-        retval[sizeof(retval)-1] = '\0';
-    } else {
-        strncpy(retval, slash_pos +1, sizeof(retval));
-        retval[sizeof(retval)-1] = '\0';
-    }
-
-    /* Chop any .exe off the right end */
-    if (strlen(retval) >= 4 && strcmp(retval+strlen(retval)-4, ".exe") == 0)
-        retval[strlen(retval)-4] = 0;
+#define MAX_RETVAL_SIZE 64
+#if MSVCRT
+    /* Note that there exists _splitpath_s, which takes a size argument,
+       but it is only in "secure" extensions of MSVCRT that come only with
+       MSVC; but _splitpath() comes with Windows.  MinGW has a header file
+       for it.
+    */
+    static char retval[_MAX_FNAME];
+    _splitpath(arg0, 0, 0,  retval, 0);
+    if (MAX_RETVAL_SIZE < _MAX_FNAME)
+        retval[MAX_RETVAL_SIZE] = '\0';
+#else
+    static char retval[MAX_RETVAL_SIZE+1];
+    extractAfterLastSlash(arg0, retval, sizeof(retval));
+#endif
 
-    return(retval);
+    return retval;
 }
 
 
@@ -784,11 +801,11 @@ pm_parse_width(const char * const arg) {
     unsigned int width;
     const char * error;
 
-    interpret_uint(arg, &width, &error);
+    pm_interpret_uint(arg, &width, &error);
 
     if (error) {
         pm_error("'%s' is invalid as an image width.  %s", arg, error);
-        strfree(error);
+        pm_strfree(error);
     } else {
         if (width > INT_MAX-10)
             pm_error("Width %u is too large for computations.", width);
@@ -809,11 +826,11 @@ pm_parse_height(const char * const arg) {
     unsigned int height;
     const char * error;
 
-    interpret_uint(arg, &height, &error);
+    pm_interpret_uint(arg, &height, &error);
 
     if (error) {
         pm_error("'%s' is invalid as an image height.  %s", arg, error);
-        strfree(error);
+        pm_strfree(error);
     } else {
         if (height > INT_MAX-10)
             pm_error("Height %u is too large for computations.", height);
diff --git a/lib/libpnm1.c b/lib/libpnm1.c
index 536e5dc4..db21b078 100644
--- a/lib/libpnm1.c
+++ b/lib/libpnm1.c
@@ -13,6 +13,8 @@
 #include <string.h>
 #include <errno.h>
 
+#include "netpbm/mallocvar.h"
+
 #include "pnm.h"
 
 #include "ppm.h"
@@ -27,8 +29,6 @@
 #include "pam.h"
 #include "libpam.h"
 
-#include "mallocvar.h"
-
 
 
 xel *
@@ -47,15 +47,12 @@ pnm_allocrow(unsigned int const cols) {
 
 
 void
-pnm_init( argcP, argv )
-    int* argcP;
-    char* argv[];
-    {
+pnm_init(int * const argcP, char ** const argv) {
     ppm_init( argcP, argv );
-    }
+}
 
 void
-pnm_nextimage(FILE *file, int * const eofP) {
+pnm_nextimage(FILE * const file, int * const eofP) {
     pm_nextimage(file, eofP);
 }
 
@@ -72,10 +69,13 @@ validateComputableSize(unsigned int const cols,
    you expect.  That failed expectation can be disastrous if you use
    it to allocate memory.
 
+   It is very normal to allocate space for a pixel row, so we make sure
+   the size of a pixel row, in bytes, can be represented by an 'int'.
+
    A common operation is adding 1 or 2 to the highest row or
    column number in the image, so we make sure that's possible.
 -----------------------------------------------------------------------------*/
-    if (cols > INT_MAX - 2)
+    if (cols > INT_MAX/(sizeof(pixval) * 3) || cols > INT_MAX - 2)
         pm_error("image width (%u) too large to be processed", cols);
     if (rows > INT_MAX - 2)
         pm_error("image height (%u) too large to be processed", rows);
@@ -126,7 +126,8 @@ pnm_readpnminit(FILE *   const fileP,
     break;
 
     default:
-        pm_error("bad magic number - not a ppm, pgm, or pbm file");
+        pm_error("bad magic number 0x%x - not a PPM, PGM, PBM, or PAM file",
+                 realFormat);
     }
     validateComputableSize(*colsP, *rowsP);
 }
@@ -178,10 +179,10 @@ readpbmrow(FILE * const fileP,
     jmp_buf * origJmpbufP;
     bit * bitrow;
 
-    bitrow = pbm_allocrow(cols);
+    bitrow = pbm_allocrow_packed(cols);
 
     if (setjmp(jmpbuf) != 0) {
-        pbm_freerow(bitrow);
+        pbm_freerow_packed(bitrow);
         pm_setjmpbuf(origJmpbufP);
         pm_longjmp();
     } else {
@@ -189,11 +190,14 @@ readpbmrow(FILE * const fileP,
 
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
-        pbm_readpbmrow(fileP, bitrow, cols, format);
-
-        for (col = 0; col < cols; ++col)
-            PNM_ASSIGN1(xelrow[col], bitrow[col] == PBM_BLACK ? 0 : maxval);
+        pbm_readpbmrow_packed(fileP, bitrow, cols, format);
 
+        for (col = 0; col < cols; ++col) {
+            pixval const g =
+                ((bitrow[col/8] >> (7 - col%8)) & 0x1) == PBM_WHITE ?
+                maxval : 0;
+            PNM_ASSIGN1(xelrow[col], g);
+        }
         pm_setjmpbuf(origJmpbufP);
     }
     pbm_freerow(bitrow);
diff --git a/lib/libpnm2.c b/lib/libpnm2.c
index 01ffa389..fa4bb8be 100644
--- a/lib/libpnm2.c
+++ b/lib/libpnm2.c
@@ -10,7 +10,7 @@
 ** implied warranty.
 */
 
-#include "pm_c_util.h"
+#include "netpbm/pm_c_util.h"
 
 #include "pnm.h"
 
@@ -20,6 +20,8 @@
 
 #include "pbm.h"
 
+
+
 void
 pnm_writepnminit(FILE * const fileP, 
                  int    const cols, 
diff --git a/lib/libpnm3.c b/lib/libpnm3.c
index c41a2108..0426ebcb 100644
--- a/lib/libpnm3.c
+++ b/lib/libpnm3.c
@@ -45,7 +45,7 @@ mean4(int const format,
         break;
 
     default:
-        pm_error( "Invalid format passed to pnm_backgroundxel()");
+        pm_error("Invalid format passed to pnm_backgroundxel()");
     }
     return retval;
 }
diff --git a/lib/libppm1.c b/lib/libppm1.c
index 97110730..c1eb152c 100644
--- a/lib/libppm1.c
+++ b/lib/libppm1.c
@@ -20,6 +20,8 @@
 #include <stdio.h>
 #include <errno.h>
 
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
 #include "ppm.h"
 #include "libppm.h"
 #include "pgm.h"
@@ -29,8 +31,6 @@
 #include "pam.h"
 #include "libpam.h"
 #include "fileio.h"
-#include "mallocvar.h"
-#include "nstring.h"
 
 
 pixel *
@@ -49,12 +49,11 @@ ppm_allocrow(unsigned int const cols) {
 
 
 void
-ppm_init( argcP, argv )
-    int* argcP;
-    char* argv[];
-    {
+ppm_init(int * const argcP, char ** const argv) {
     pgm_init( argcP, argv );
-    }
+}
+
+
 
 void
 ppm_nextimage(FILE * const fileP, 
@@ -79,7 +78,7 @@ ppm_readppminitrest(FILE *   const fileP,
     maxval = pm_getuint(fileP);
     if (maxval > PPM_OVERALLMAXVAL)
         pm_error("maxval of input image (%u) is too large.  "
-                 "The maximum allowed by the PPM is %u.",
+                 "The maximum allowed by the PPM format is %u.",
                  maxval, PPM_OVERALLMAXVAL); 
     if (maxval == 0)
         pm_error("maxval of input image is zero.");
@@ -100,10 +99,13 @@ validateComputableSize(unsigned int const cols,
    you expect.  That failed expectation can be disastrous if you use
    it to allocate memory.
 
+   It is very normal to allocate space for a pixel row, so we make sure
+   the size of a pixel row, in bytes, can be represented by an 'int'.
+
    A common operation is adding 1 or 2 to the highest row or
    column number in the image, so we make sure that's possible.
 -----------------------------------------------------------------------------*/
-    if (cols > INT_MAX - 2)
+    if (cols > INT_MAX/(sizeof(pixval) * 3) || cols > INT_MAX - 2)
         pm_error("image width (%u) too large to be processed", cols);
     if (rows > INT_MAX - 2)
         pm_error("image height (%u) too large to be processed", rows);
@@ -184,6 +186,92 @@ readPpmRow(FILE *       const fileP,
 
 
 static void
+interpRasterRowRaw(const unsigned char * const rowBuffer,
+                   pixel *               const pixelrow,
+                   unsigned int          const cols,
+                   unsigned int          const bytesPerSample) {
+
+    unsigned int bufferCursor;
+
+    bufferCursor = 0;  /* start at beginning of rowBuffer[] */
+        
+    if (bytesPerSample == 1) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            pixval const r = rowBuffer[bufferCursor++];
+            pixval const g = rowBuffer[bufferCursor++];
+            pixval const b = rowBuffer[bufferCursor++];
+            PPM_ASSIGN(pixelrow[col], r, g, b);
+        }
+    } else  {
+        /* two byte samples */
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            pixval r, g, b;
+                    
+            r = rowBuffer[bufferCursor++] << 8;
+            r |= rowBuffer[bufferCursor++];
+                    
+            g = rowBuffer[bufferCursor++] << 8;
+            g |= rowBuffer[bufferCursor++];
+                    
+            b = rowBuffer[bufferCursor++] << 8;
+            b |= rowBuffer[bufferCursor++];
+                    
+            PPM_ASSIGN(pixelrow[col], r, g, b);
+        }
+    }
+}
+
+
+
+static void
+validateRppmRow(pixel *       const pixelrow,
+                unsigned int  const cols,
+                pixval        const maxval,
+                const char ** const errorP) {
+/*----------------------------------------------------------------------------
+  Check for sample values above maxval in input.  
+
+  Note: a program that wants to deal with invalid sample values itself can
+  simply make sure it uses a sufficiently high maxval on the read function
+  call, so this validation never fails.
+-----------------------------------------------------------------------------*/
+    if (maxval == 255 || maxval == 65535) {
+        /* There's no way a sample can be invalid, so we don't need to look at
+           the samples individually.
+        */
+        *errorP = NULL;
+    } else {
+        unsigned int col;
+
+        for (col = 0, *errorP = NULL; col < cols && !*errorP; ++col) {
+            pixval const r = PPM_GETR(pixelrow[col]);
+            pixval const g = PPM_GETG(pixelrow[col]);
+            pixval const b = PPM_GETB(pixelrow[col]);
+
+            if (r > maxval)
+                pm_asprintf(
+                    errorP,
+                    "Red sample value %u is greater than maxval (%u)",
+                    r, maxval);
+            else if (g > maxval)
+                pm_asprintf(
+                    errorP,
+                    "Green sample value %u is greater than maxval (%u)",
+                    g, maxval);
+            else if (b > maxval)
+                pm_asprintf(
+                    errorP,
+                    "Blue sample value %u is greater than maxval (%u)",
+                    b, maxval);
+        }
+    }
+}
+
+
+
+static void
 readRppmRow(FILE *       const fileP, 
             pixel *      const pixelrow, 
             unsigned int const cols, 
@@ -199,60 +287,35 @@ readRppmRow(FILE *       const fileP,
     MALLOCARRAY(rowBuffer, bytesPerRow);
         
     if (rowBuffer == NULL)
-        asprintfN(&error, "Unable to allocate memory for row buffer "
-                  "for %u columns", cols);
+        pm_asprintf(&error, "Unable to allocate memory for row buffer "
+                    "for %u columns", cols);
     else {
         ssize_t rc;
 
         rc = fread(rowBuffer, 1, bytesPerRow, fileP);
     
         if (feof(fileP))
-            asprintfN(&error, "Unexpected EOF reading row of PPM image.");
+            pm_asprintf(&error, "Unexpected EOF reading row of PPM image.");
         else if (ferror(fileP))
-            asprintfN(&error, "Error reading row.  fread() errno=%d (%s)",
-                      errno, strerror(errno));
-        else if (rc != bytesPerRow)
-            asprintfN(&error, "Error reading row.  Short read of %u bytes "
-                      "instead of %u", rc, bytesPerRow);
+            pm_asprintf(&error, "Error reading row.  fread() errno=%d (%s)",
+                        errno, strerror(errno));
         else {
-            unsigned int bufferCursor;
-
-            error = NULL;
-
-            bufferCursor = 0;  /* start at beginning of rowBuffer[] */
-        
-            if (bytesPerSample == 1) {
-                unsigned int col;
-                for (col = 0; col < cols; ++col) {
-                    pixval const r = rowBuffer[bufferCursor++];
-                    pixval const g = rowBuffer[bufferCursor++];
-                    pixval const b = rowBuffer[bufferCursor++];
-                    PPM_ASSIGN(pixelrow[col], r, g, b);
-                }
-            } else  {
-                /* two byte samples */
-                unsigned int col;
-                for (col = 0; col < cols; ++col) {
-                    pixval r, g, b;
-                    
-                    r = rowBuffer[bufferCursor++] << 8;
-                    r |= rowBuffer[bufferCursor++];
-                    
-                    g = rowBuffer[bufferCursor++] << 8;
-                    g |= rowBuffer[bufferCursor++];
-                    
-                    b = rowBuffer[bufferCursor++] << 8;
-                    b |= rowBuffer[bufferCursor++];
-                    
-                    PPM_ASSIGN(pixelrow[col], r, g, b);
-                }
+            size_t const bytesRead = rc;
+            if (bytesRead != bytesPerRow)
+                pm_asprintf(&error,
+                            "Error reading row.  Short read of %u bytes "
+                            "instead of %u", (unsigned)bytesRead, bytesPerRow);
+            else {
+                interpRasterRowRaw(rowBuffer, pixelrow, cols, bytesPerSample);
+
+                validateRppmRow(pixelrow, cols, maxval, &error);
             }
         }
         free(rowBuffer);
     }
     if (error) {
         pm_errormsg("%s", error);
-        strfree(error);
+        pm_strfree(error);
         pm_longjmp();
     }
 }
@@ -305,10 +368,10 @@ readPbmRow(FILE *       const fileP,
     jmp_buf * origJmpbufP;
     bit * bitrow;
 
-    bitrow = pbm_allocrow(cols);
+    bitrow = pbm_allocrow_packed(cols);
 
     if (setjmp(jmpbuf) != 0) {
-        pbm_freerow(bitrow);
+        pbm_freerow_packed(bitrow);
         pm_setjmpbuf(origJmpbufP);
         pm_longjmp();
     } else {
@@ -316,10 +379,12 @@ readPbmRow(FILE *       const fileP,
 
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
-        pbm_readpbmrow(fileP, bitrow, cols, format);
+        pbm_readpbmrow_packed(fileP, bitrow, cols, format);
 
         for (col = 0; col < cols; ++col) {
-            pixval const g = (bitrow[col] == PBM_WHITE) ? maxval : 0;
+            pixval const g =
+                ((bitrow[col/8] >> (7 - col%8)) & 0x1) == PBM_WHITE ?
+                maxval : 0;
             PPM_ASSIGN(pixelrow[col], g, g, g);
         }
         pm_setjmpbuf(origJmpbufP);
@@ -408,30 +473,32 @@ ppm_readppm(FILE *   const fileP,
 
 void
 ppm_check(FILE *               const fileP, 
-          enum pm_check_type   const check_type, 
+          enum pm_check_type   const checkType, 
           int                  const format, 
           int                  const cols, 
           int                  const rows, 
           pixval               const maxval,
-          enum pm_check_code * const retval_p) {
+          enum pm_check_code * const retvalP) {
 
     if (rows < 0)
         pm_error("Invalid number of rows passed to ppm_check(): %d", rows);
     if (cols < 0)
         pm_error("Invalid number of columns passed to ppm_check(): %d", cols);
     
-    if (check_type != PM_CHECK_BASIC) {
-        if (retval_p) *retval_p = PM_CHECK_UNKNOWN_TYPE;
+    if (checkType != PM_CHECK_BASIC) {
+        if (retvalP)
+            *retvalP = PM_CHECK_UNKNOWN_TYPE;
     } else if (PPM_FORMAT_TYPE(format) == PBM_TYPE) {
-        pbm_check(fileP, check_type, format, cols, rows, retval_p);
+        pbm_check(fileP, checkType, format, cols, rows, retvalP);
     } else if (PPM_FORMAT_TYPE(format) == PGM_TYPE) {
-        pgm_check(fileP, check_type, format, cols, rows, maxval, retval_p);
+        pgm_check(fileP, checkType, format, cols, rows, maxval, retvalP);
     } else if (format != RPPM_FORMAT) {
-        if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE;
+        if (retvalP)
+            *retvalP = PM_CHECK_UNCHECKABLE;
     } else {        
-        pm_filepos const bytes_per_row = cols * 3 * (maxval > 255 ? 2 : 1);
-        pm_filepos const need_raster_size = rows * bytes_per_row;
+        pm_filepos const bytesPerRow    = cols * 3 * (maxval > 255 ? 2 : 1);
+        pm_filepos const needRasterSize = rows * bytesPerRow;
         
-        pm_check(fileP, check_type, need_raster_size, retval_p);
+        pm_check(fileP, checkType, needRasterSize, retvalP);
     }
 }
diff --git a/lib/libppm2.c b/lib/libppm2.c
index 3bf74bd4..694ecdd7 100644
--- a/lib/libppm2.c
+++ b/lib/libppm2.c
@@ -14,8 +14,8 @@
 #include <errno.h>
 
 
-#include "pm_c_util.h"
-#include "mallocvar.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
 #include "ppm.h"
 
 void
@@ -36,10 +36,6 @@ ppm_writeppminit(FILE*  const fileP,
             PPM_MAGIC1, 
             plainFormat || maxval >= 1<<16 ? PPM_MAGIC2 : RPPM_MAGIC2, 
             cols, rows, maxval );
-#ifdef VMS
-    if (!plainFormat)
-        set_outfile_binary();
-#endif
 }
 
 
@@ -133,10 +129,13 @@ ppm_writeppmrowraw(FILE *        const fileP,
     if (rc < 0)
         pm_error("Error writing row.  fwrite() errno=%d (%s)",
                  errno, strerror(errno));
-    else if (rc != bytesPerRow)
-        pm_error("Error writing row.  Short write of %u bytes "
-                 "instead of %u", rc, bytesPerRow);
+    else {
+        size_t const bytesWritten = rc;
 
+        if (bytesWritten != bytesPerRow)
+            pm_error("Error writing row.  Short write of %u bytes "
+                     "instead of %u", (unsigned)bytesWritten, bytesPerRow);
+    }
     free(rowBuffer);
 }
 
diff --git a/lib/libppmcmap.c b/lib/libppmcmap.c
index 055bfc8b..f78d0516 100644
--- a/lib/libppmcmap.c
+++ b/lib/libppmcmap.c
@@ -12,18 +12,26 @@
 ** implied warranty.
 */
 
-#include "pm_c_util.h"
-#include "nstring.h"
-#include "mallocvar.h"
+#include "netpbm/pm_config.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/nstring.h"
+#include "netpbm/mallocvar.h"
 #include "ppm.h"
 #include "ppmcmap.h"
 
 #define HASH_SIZE 20023
 
-#define ppm_hashpixel(p) ( ( ( (long) PPM_GETR(p) * 33023 + \
-                               (long) PPM_GETG(p) * 30013 + \
-                               (long) PPM_GETB(p) * 27011 ) \
-                             & 0x7fffffff ) % HASH_SIZE )
+
+
+static __inline__ unsigned int
+ppm_hashpixel(pixel const p) {
+
+    return (unsigned int) (PPM_GETR(p) * 33 * 33
+                           + PPM_GETG(p) * 33
+                           + PPM_GETB(p)) % HASH_SIZE;
+}
+
+
 
 colorhist_vector
 ppm_computecolorhist( pixel ** const pixels, 
@@ -153,7 +161,7 @@ readppmrow(FILE *        const fileP,
     
     if (setjmp(jmpbuf) != 0) {
         pm_setjmpbuf(origJmpbufP);
-        asprintfN(errorP, "Failed to read row of image.");
+        pm_asprintf(errorP, "Failed to read row of image.");
     } else {
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
@@ -227,8 +235,8 @@ buildHashTable(FILE *          const ifP,
                 else {
                     MALLOCVAR(chl);
                     if (chl == NULL)
-                        asprintfN(errorP,
-                                  "out of memory computing hash table");
+                        pm_asprintf(errorP,
+                                    "out of memory computing hash table");
                     chl->ch.color = apixel;
                     chl->ch.value = 1;
                     chl->next = cht[hash];
@@ -282,7 +290,7 @@ computecolorhash(pixel **          const pixels,
     MALLOCARRAY(rowbuffer, cols);
         
     if (rowbuffer == NULL)
-        asprintfN(errorP, "Unable to allocate %u-column row buffer.", cols);
+        pm_asprintf(errorP, "Unable to allocate %u-column row buffer.", cols);
     else {
         colorhash_table cht;
         bool tooManyColors;
@@ -290,7 +298,7 @@ computecolorhash(pixel **          const pixels,
         cht = alloccolorhash();
 
         if (cht == NULL)
-            asprintfN(errorP, "Unable to allocate color hash.");
+            pm_asprintf(errorP, "Unable to allocate color hash.");
         else {
             buildHashTable(ifP, pixels, cols, rows, maxval, format, maxcolors,
                            cht, rowbuffer,
@@ -326,7 +334,7 @@ ppm_computecolorhash(pixel ** const pixels,
 
     if (error) {
         pm_errormsg("%s", error);
-        strfree(error);
+        pm_strfree(error);
         pm_longjmp();
     }
     return cht;
@@ -351,7 +359,7 @@ ppm_computecolorhash2(FILE * const ifP,
 
     if (error) {
         pm_errormsg("%s", error);
-        strfree(error);
+        pm_strfree(error);
         pm_longjmp();
     }
     return cht;
@@ -484,7 +492,7 @@ ppm_colorhisttocolorhash(colorhist_vector const chv,
 
     cht = alloccolorhash( );  /* Initializes to NULLs */
     if (cht == NULL)
-        asprintfN(&error, "Unable to allocate color hash");
+        pm_asprintf(&error, "Unable to allocate color hash");
     else {
         unsigned int i;
 
@@ -496,13 +504,13 @@ ppm_colorhisttocolorhash(colorhist_vector const chv,
 
             for (chl = cht[hash]; chl && !error; chl = chl->next)
                 if (PPM_EQUAL(chl->ch.color, color))
-                    asprintfN(&error, "same color found twice: (%u %u %u)",
-                              PPM_GETR(color),
-                              PPM_GETG(color),
-                              PPM_GETB(color));
+                    pm_asprintf(&error, "same color found twice: (%u %u %u)",
+                                PPM_GETR(color),
+                                PPM_GETG(color),
+                                PPM_GETB(color));
             MALLOCVAR(chl);
             if (chl == NULL)
-                asprintfN(&error, "out of memory");
+                pm_asprintf(&error, "out of memory");
             else {
                 chl->ch.color = color;
                 chl->ch.value = i;
@@ -515,7 +523,7 @@ ppm_colorhisttocolorhash(colorhist_vector const chv,
     }
     if (error) {
         pm_errormsg("%s", error);
-        strfree(error);
+        pm_strfree(error);
         pm_longjmp();
     } else
         retval = cht;
@@ -681,9 +689,33 @@ fail:
 }
 
 
+
+static int (*customCmp)(pixel *, pixel *);
+
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn customStub;
+#endif
+
 static int
-pixel_cmp(const void * const a, const void * const b) {
-    const pixel *p1 = (const pixel *)a, *p2 = (const pixel *)b;
+customStub(const void * const a,
+           const void * const b) {
+
+    return (*customCmp)((pixel *)a, (pixel *)b);
+}
+
+
+
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn pixelCmp;
+#endif
+
+static int
+pixelCmp(const void * const a,
+         const void * const b) {
+
+    const pixel * const p1 = (const pixel *)a;
+    const pixel * const p2 = (const pixel *)b;
+
     int diff;
 
     diff = PPM_GETR(*p1) - PPM_GETR(*p2);
@@ -695,23 +727,18 @@ pixel_cmp(const void * const a, const void * const b) {
     return diff;
 }
 
-static int (*custom_cmp)(pixel *, pixel *);
-
-static int
-custom_stub(const void * const a, const void * const b) {
-    return (*custom_cmp)((pixel *)a, (pixel *)b);
-}
 
 
 void
-ppm_sortcolorrow(pixel * const colorrow, const int ncolors, 
+ppm_sortcolorrow(pixel * const colorrow,
+                 int     const ncolors, 
                  int (*cmpfunc)(pixel *, pixel *)) {
 
-    if( cmpfunc ) {
-        custom_cmp = cmpfunc;
-        qsort((void *)colorrow, ncolors, sizeof(pixel), custom_stub);
+    if (cmpfunc) {
+        customCmp = cmpfunc;
+        qsort((void *)colorrow, ncolors, sizeof(pixel), customStub);
     } else
-        qsort((void *)colorrow, ncolors, sizeof(pixel), pixel_cmp);
+        qsort((void *)colorrow, ncolors, sizeof(pixel), pixelCmp);
 }
 
 
diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c
index 58663c94..aee8fd83 100644
--- a/lib/libppmcolor.c
+++ b/lib/libppmcolor.c
@@ -17,9 +17,9 @@
 #include <string.h>
 #include <math.h>
 
-#include "pm_c_util.h"
-#include "mallocvar.h"
-#include "nstring.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
 #include "ppm.h"
 #include "colorname.h"
 
@@ -264,7 +264,7 @@ parseOldX11(char       const colorname[],
     
     computeHexTable(hexit);
 
-    if (!strishex(&colorname[1]))
+    if (!pm_strishex(&colorname[1]))
         pm_error("Non-hexadecimal characters in #-type color specification");
 
     switch (strlen(colorname) - 1 /* (Number of hex digits) */) {
@@ -451,6 +451,43 @@ ppm_colorname(const pixel * const colorP,
 
 #define MAXCOLORNAMES 1000u
 
+static const char **
+allocColorNames() {
+
+    const char ** colornames;
+
+    MALLOCARRAY(colornames, MAXCOLORNAMES);
+
+    if (colornames) {
+        unsigned int i;
+        for (i = 0; i < MAXCOLORNAMES; ++i)
+            colornames[i] = NULL;
+    }
+    return colornames;
+}
+
+
+
+static colorhash_table
+allocColorHash(void) {
+
+    colorhash_table cht;
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+
+    if (setjmp(jmpbuf) != 0)
+        cht = NULL;
+    else {
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+        cht = ppm_alloccolorhash();
+    }
+    pm_setjmpbuf(origJmpbufP);
+
+    return cht;
+}
+
+
+
 static void
 processColorfileEntry(struct colorfile_entry const ce,
                       colorhash_table        const cht,
@@ -460,8 +497,8 @@ processColorfileEntry(struct colorfile_entry const ce,
                       const char **          const errorP) {
 
     if (*colornameIndexP >= MAXCOLORNAMES)
-        asprintfN(errorP, "Too many colors in colorname dictionary.  "
-                  "Max allowed is %u", MAXCOLORNAMES);
+        pm_asprintf(errorP, "Too many colors in colorname dictionary.  "
+                    "Max allowed is %u", MAXCOLORNAMES);
     else {
         pixel color;
 
@@ -479,7 +516,7 @@ processColorfileEntry(struct colorfile_entry const ce,
             colornames[*colornameIndexP] = strdup(ce.colorname);
             colors[*colornameIndexP] = color;
             if (colornames[*colornameIndexP] == NULL)
-                asprintfN(errorP, "Unable to allocate space for color name");
+                pm_asprintf(errorP, "Unable to allocate space for color name");
             else {
                 *errorP = NULL;
                 ++(*colornameIndexP);
@@ -500,7 +537,7 @@ openColornameFile(const char *  const fileName,
     jmp_buf * origJmpbufP;
 
     if (setjmp(jmpbuf) != 0) {
-        asprintfN(errorP, "Failed to open color name file");
+        pm_asprintf(errorP, "Failed to open color name file");
         pm_setjmpbuf(origJmpbufP);
         pm_longjmp();
     } else {
@@ -525,6 +562,9 @@ readOpenColorFile(FILE *          const colorFileP,
    Read the color dictionary file *colorFileP and add the colors in it
    to colornames[], colors[], and 'cht'.
 
+   colornames[] and colors[] must be allocated with MAXCOLORNAMES entries
+   at entry.
+
    We may add colors to 'cht' even if we fail.
 -----------------------------------------------------------------------------*/
     unsigned int nColorsDone;
@@ -543,43 +583,18 @@ readOpenColorFile(FILE *          const colorFileP,
             processColorfileEntry(ce, cht, colornames, colors,
                                   &nColorsDone, errorP);
     }
-    if (!*errorP) {
-        *nColorsP = nColorsDone;
-        
-        while (nColorsDone < MAXCOLORNAMES)
-            colornames[nColorsDone++] = NULL;
-    }
+    *nColorsP = nColorsDone;
     
     if (*errorP) {
         unsigned int colorIndex;
 
         for (colorIndex = 0; colorIndex < nColorsDone; ++colorIndex)
-            strfree(colornames[colorIndex]);
+            pm_strfree(colornames[colorIndex]);
     }
 }
 
 
 
-static colorhash_table
-allocColorHash(void) {
-
-    colorhash_table cht;
-    jmp_buf jmpbuf;
-    jmp_buf * origJmpbufP;
-
-    if (setjmp(jmpbuf) != 0)
-        cht = NULL;
-    else {
-        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
-        cht = ppm_alloccolorhash();
-    }
-    pm_setjmpbuf(origJmpbufP);
-
-    return cht;
-}
-
-
-
 static void
 readColorFile(const char *    const fileName,
               bool            const mustOpen,
@@ -588,7 +603,20 @@ readColorFile(const char *    const fileName,
               pixel *         const colors,
               colorhash_table const cht,
               const char **   const errorP) {
+/*----------------------------------------------------------------------------
+   Read the color dictionary file named 'fileName' and add the colors in it
+   to colornames[], colors[], and 'cht'.  Return as *nColorsP the number
+   of colors in it.
 
+   If the file is not openable (e.g. not file by that name exists), abort the
+   program if 'mustOpen' is true; otherwise, return values indicating a
+   dictionary with no colors.
+
+   colornames[] and colors[] must be allocated with MAXCOLORNAMES entries
+   at entry.
+
+   We may add colors to 'cht' even if we fail.
+-----------------------------------------------------------------------------*/
     FILE * colorFileP;
 
     openColornameFile(fileName, mustOpen, &colorFileP, errorP);
@@ -598,11 +626,6 @@ readColorFile(const char *    const fileName,
                empty file
             */
             *nColorsP = 0;
-            {
-                unsigned int i;
-                for (i = 0; i < MAXCOLORNAMES; ++i)
-                    colornames[i] = NULL;
-            }
             *errorP = NULL;
         } else {
             readOpenColorFile(colorFileP, nColorsP, colornames, colors, cht,
@@ -626,24 +649,24 @@ readcolordict(const char *      const fileName,
 
     const char ** colornames;
 
-    MALLOCARRAY(colornames, MAXCOLORNAMES);
+    colornames = allocColorNames();
 
     if (colornames == NULL)
-        asprintfN(errorP, "Unable to allocate space for colorname table.");
+        pm_asprintf(errorP, "Unable to allocate space for colorname table.");
     else {
         pixel * colors;
 
         MALLOCARRAY(colors, MAXCOLORNAMES);
         
         if (colors == NULL)
-            asprintfN(errorP, "Unable to allocate space for color table.");
+            pm_asprintf(errorP, "Unable to allocate space for color table.");
         else {
             colorhash_table cht;
 
             cht = allocColorHash();
             
             if (cht == NULL)
-                asprintfN(errorP, "Unable to allocate space for color hash");
+                pm_asprintf(errorP, "Unable to allocate space for color hash");
             else {
                 readColorFile(fileName, mustOpen,
                               nColorsP, colornames, colors, cht,
@@ -675,7 +698,28 @@ ppm_readcolordict(const char *      const fileName,
                   const char ***    const colornamesP,
                   pixel **          const colorsP,
                   colorhash_table * const chtP) {
+/*----------------------------------------------------------------------------
+   Read the color dictionary from the file named 'fileName'.  If we can't open
+   the file (e.g. because it does not exist), and 'mustOpen' is false, return
+   an empty dictionary (it contains no colors).  But if 'mustOpen' is true,
+   abort the program instead of returning an empty dictionary.
+
+   Return as *nColorsP the number of colors in the dictionary.
 
+   Return as *colornamesP the names of those colors.  *colornamesP is a
+   malloced array that Caller must free with ppm_freecolornames().
+   The first *nColorsP entries are valid; *chtP contains indices into this
+   array.
+
+   Return as *colorsP the colors.  *colorsP is a malloced array of size
+   MAXCOLORS with the first elements filled in and the rest undefined.
+
+   Return as *chtP a color hash table mapping each color in the dictionary
+   to the index into *colornamesP for the name of the color.
+
+   Each of 'nColorsP, 'colornamesP', and 'colorsP' may be null, in which case
+   we do not return the corresponding information (or allocate memory for it).
+-----------------------------------------------------------------------------*/
     colorhash_table cht;
     const char ** colornames;
     pixel * colors;
@@ -687,7 +731,7 @@ ppm_readcolordict(const char *      const fileName,
 
     if (error) {
         pm_errormsg("%s", error);
-        strfree(error);
+        pm_strfree(error);
         ppm_freecolorhash(cht);
     } else {
         if (chtP)
diff --git a/lib/libppmd.c b/lib/libppmd.c
index 2325dd68..262679ec 100644
--- a/lib/libppmd.c
+++ b/lib/libppmd.c
@@ -18,9 +18,9 @@
 #include <assert.h>
 #include <stdlib.h>
 
-#include "pm_config.h"
-#include "pm_c_util.h"
-#include "mallocvar.h"
+#include "netpbm/pm_config.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
 #include "ppm.h"
 #include "ppmdfont.h"
 #include "ppmdraw.h"
@@ -966,14 +966,25 @@ typedef struct
     int edge;
 } coord;
 
-typedef struct fillobj {
+typedef struct fillState {
     int n;
+        /* Number of elements in 'coords' */
     int size;
     int curedge;
     int segstart;
     int ydir;
     int startydir;
     coord * coords;
+} fillState;
+
+typedef struct fillobj {
+
+    /* The only reason we have a struct fillState separate from
+       struct fillobj is that the drawproc interface is defined to
+       have drawing not modify the fillobj, i.e. it passed
+       const fillobj * to the drawing program.
+    */
+    struct fillState * stateP;
 } fillobj;
 
 #define SOME 1000
@@ -984,16 +995,24 @@ struct fillobj *
 ppmd_fill_create(void) {
 
     fillobj * fillObjP;
+    struct fillState * stateP;
 
     MALLOCVAR(fillObjP);
     if (fillObjP == NULL)
         pm_error("out of memory allocating a fillhandle");
-    fillObjP->n = 0;
-    fillObjP->size = SOME;
-    MALLOCARRAY(fillObjP->coords, fillObjP->size);
-    if (fillObjP->coords == NULL)
+
+    MALLOCVAR(stateP);
+    if (stateP == NULL)
         pm_error("out of memory allocating a fillhandle");
-    fillObjP->curedge = 0;
+
+    stateP->n = 0;
+    stateP->size = SOME;
+    MALLOCARRAY(stateP->coords, stateP->size);
+    if (stateP->coords == NULL)
+        pm_error("out of memory allocating a fillhandle");
+    stateP->curedge = 0;
+
+    fillObjP->stateP = stateP;
     
     /* Turn off line clipping. */
     /* UGGH! We must eliminate this global variable */
@@ -1021,14 +1040,95 @@ ppmd_fill_init(void) {
 
 
 void
-ppmd_fill_destroy(struct fillobj * fillObjP) {
+ppmd_fill_destroy(struct fillobj * const fillObjP) {
 
-    free(fillObjP->coords);
+    free(fillObjP->stateP->coords);
+    free(fillObjP->stateP);
     free(fillObjP);
 }
 
 
 
+static void
+addCoord(struct fillState *  const stateP,
+         ppmd_point const point) {
+
+    stateP->coords[stateP->n].point = point;
+    stateP->coords[stateP->n].edge = stateP->curedge;
+
+    ++stateP->n;
+}
+
+
+
+static void
+startNewSegment(struct fillState * const stateP) {
+/*----------------------------------------------------------------------------
+   Close off the segment we're currently building and start a new one.
+-----------------------------------------------------------------------------*/
+    if (stateP->startydir != 0 && stateP->ydir != 0) {
+        /* There's stuff in the current segment.  */
+        if (stateP->startydir == stateP->ydir) {
+            /* Oops, first edge and last edge of current segment are the same.
+               Change all points in the first edge to be in the last.
+            */
+            int const firstEdge = stateP->coords[stateP->segstart].edge;
+            int const lastEdge  = stateP->coords[stateP->n - 1].edge;
+            coord * const segStartCoordP = &stateP->coords[stateP->segstart];
+            coord * const segEndCoordP   = &stateP->coords[stateP->n];
+
+            coord * fcP;
+
+            for (fcP = segStartCoordP;
+                 fcP < segEndCoordP && fcP->edge == firstEdge;
+                 ++fcP)
+                fcP->edge = lastEdge;
+        }
+    }
+    /* And start new segment. */
+    ++stateP->curedge;
+    stateP->segstart  = stateP->n;
+    stateP->ydir      = 0;
+    stateP->startydir = 0;
+}
+
+
+
+static void
+continueSegment(struct fillState * const stateP,
+                int                const dy) {
+/*----------------------------------------------------------------------------
+   'dy' is how much the current point is above the previous one.
+-----------------------------------------------------------------------------*/
+    if (dy != 0) {
+        if (stateP->ydir != 0 && stateP->ydir != dy) {
+            /* Direction changed.  Insert a fake coord, old
+               position but new edge number.
+            */
+            ++stateP->curedge;
+            addCoord(stateP, stateP->coords[stateP->n - 1].point);
+        }
+        stateP->ydir = dy;
+        if (stateP->startydir == 0)
+            stateP->startydir = dy;
+    }
+}
+
+
+
+
+/* ppmd_fill_drawprocp() is a drawproc that turns an outline drawing function
+   into a filled shape function.  This is a somewhat off-label application of
+   a drawproc:  A drawproc is intended just to draw a point.  So e.g. you
+   might draw a circle with a fat brush by calling ppmd_circle with a drawproc
+   that draws a point as a 10-pixel disk.
+
+   But ppmd_fill_drawproc() just draws a point the trivial way: as one pixel.
+   However, it tracks every point that is drawn in a form that a subsequent
+   ppmd_fill() call can use to to fill in the shape drawn, assuming it turns
+   out to be a closed shape.
+*/
+
 void
 ppmd_fill_drawprocp(pixel **     const pixels, 
                     unsigned int const cols, 
@@ -1037,85 +1137,39 @@ ppmd_fill_drawprocp(pixel **     const pixels,
                     ppmd_point   const p,
                     const void * const clientdata) {
 
-    fillobj * fh;
-    coord * cp;
-
-    fh = (fillobj*) clientdata;
+    const fillobj *    const fillObjP = clientdata;
+    struct fillState * const stateP   = fillObjP->stateP;
 
-    /* If these are the same coords we saved last time, don't bother. */
-    if (fh->n > 0) {
-        ppmd_point const lastPoint = fh->coords[fh->n - 1].point;
-        if (pointsEqual(p, lastPoint))
-            return;
-    }
-
-    /* Ok, these are new; make room for two more coords. */
-    if (fh->n + 1 >= fh->size) {
-        fh->size += SOME;
-        REALLOCARRAY(fh->coords, fh->size);
-        if (fh->coords == NULL)
+    /* Make room for two more coords, the max we might add. */
+    if (stateP->n + 2 > stateP->size) {
+        stateP->size += SOME;
+        REALLOCARRAY(stateP->coords, stateP->size);
+        if (stateP->coords == NULL)
             pm_error("out of memory enlarging a fillhandle");
     }
 
-    /* Check for extremum and set the edge number. */
-    if (fh->n == 0) {
+    if (stateP->n == 0) {
         /* Start first segment. */
-        fh->segstart = fh->n;
-        fh->ydir = 0;
-        fh->startydir = 0;
+        stateP->segstart = stateP->n;
+        stateP->ydir = 0;
+        stateP->startydir = 0;
+        addCoord(stateP, p);
     } else {
-        coord * const ocp = &(fh->coords[fh->n - 1]);
-        int dx, dy;
-
-        dx = p.x - ocp->point.x;
-        dy = p.y - ocp->point.y;
-        if (dx < -1 || dx > 1 || dy < -1 || dy > 1) {
-            /* Segment break.  Close off old one. */
-            if (fh->startydir != 0 && fh->ydir != 0)
-                if (fh->startydir == fh->ydir) {
-                    /* Oops, first edge and last edge are the same.
-                       Renumber the first edge in the old segment.
-                    */
-                    const coord * const fcpLast= &(fh->coords[fh->n -1]); 
-                    coord * fcp;
-
-                    int oldedge;
-
-                    fcp = &(fh->coords[fh->segstart]);
-                    oldedge = fcp->edge;
-                    for (; fcp <= fcpLast && fcp->edge == oldedge ; ++fcp)
-                        fcp->edge = ocp->edge;
-                }
-            /* And start new segment. */
-            ++fh->curedge;
-            fh->segstart = fh->n;
-            fh->ydir = 0;
-            fh->startydir = 0;
+        ppmd_point const prevPoint = stateP->coords[stateP->n - 1].point;
+        int const dx = p.x - prevPoint.x;
+        int const dy = p.y - prevPoint.y;
+
+        if (dx == 0 && dy == 0) {
+            /* These are the same coords we had last time; don't bother */
         } else {
-            /* Segment continues. */
-            if (dy != 0) {
-                if (fh->ydir != 0 && fh->ydir != dy) {
-                    /* Direction changed.  Insert a fake coord, old
-                       position but new edge number.
-                    */
-                    ++fh->curedge;
-                    cp = &fh->coords[fh->n];
-                    cp->point = ocp->point;
-                    cp->edge = fh->curedge;
-                    ++fh->n;
-                }
-                fh->ydir = dy;
-                if (fh->startydir == 0)
-                    fh->startydir = dy;
-            }
+            if (abs(dx) > 1 || abs(dy) > 1)
+                startNewSegment(stateP);
+            else
+                continueSegment(stateP, dy);
+
+            addCoord(stateP, p);
         }
     }
-
-    /* Save this coord. */
-    cp = &fh->coords[fh->n];
-    cp->point = p;
-    cp->edge = fh->curedge;
-    ++fh->n;
 }
 
 
@@ -1137,12 +1191,12 @@ ppmd_fill_drawproc(pixel**      const pixels,
 
 
 #ifndef LITERAL_FN_DEF_MATCH
-static qsort_comparison_fn yx_compare;
+static qsort_comparison_fn yxCompare;
 #endif
 
 static int
-yx_compare(const void * const c1Arg,
-           const void * const c2Arg) {
+yxCompare(const void * const c1Arg,
+          const void * const c2Arg) {
 
     const coord * const c1P = c1Arg;
     const coord * const c2P = c2Arg;
@@ -1173,10 +1227,12 @@ ppmd_fill(pixel **         const pixels,
           int              const cols, 
           int              const rows, 
           pixval           const maxval, 
-          struct fillobj * const fh,
+          struct fillobj * const fillObjP,
           ppmd_drawproc          drawProc,
           const void *     const clientdata) {
 
+    struct fillState * const fh = fillObjP->stateP;
+
     int pedge;
     int i, edge, lx, rx, py;
     coord * cp;
@@ -1202,7 +1258,7 @@ ppmd_fill(pixel **         const pixels,
     ppmd_setlineclip(oldclip);
 
     /* Sort the coords by Y, secondarily by X. */
-    qsort((char*) fh->coords, fh->n, sizeof(coord), yx_compare);
+    qsort((char*) fh->coords, fh->n, sizeof(coord), yxCompare);
 
     /* Find equal coords with different edge numbers, and swap if necessary. */
     edge = -1;
diff --git a/lib/libppmfloyd.c b/lib/libppmfloyd.c
index ec6256ff..d2b9c7b1 100644
--- a/lib/libppmfloyd.c
+++ b/lib/libppmfloyd.c
@@ -22,9 +22,9 @@ do Floyd-Steinberg.
 ** implied warranty.
 */
 
+#include "netpbm/mallocvar.h"
 #include "ppm.h"
 #include "ppmfloyd.h"
-#include "mallocvar.h"
 
 
 
diff --git a/lib/libppmfuzzy.c b/lib/libppmfuzzy.c
index 2a54e896..f1573c68 100644
--- a/lib/libppmfuzzy.c
+++ b/lib/libppmfuzzy.c
@@ -5,8 +5,8 @@
   <kenan@unix.ba> in 2006.
 =============================================================================*/
 
-#include "pm_c_util.h"
-#include "nstring.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/nstring.h"
 #include "ppm.h"
 
 typedef double fzLog;
diff --git a/lib/libsystem.c b/lib/libsystem.c
index 1c407ce4..fd3c52ec 100644
--- a/lib/libsystem.c
+++ b/lib/libsystem.c
@@ -13,6 +13,7 @@
    Contributed to the public domain.
 =============================================================================*/
 #define _XOPEN_SOURCE
+#define _BSD_SOURCE  /* Make SIGWINCH defined on OpenBSD */
 
 #include <stdarg.h>
 #include <unistd.h>
@@ -23,8 +24,8 @@
 #include <signal.h>
 #include <sys/wait.h>
 
-#include "pm_c_util.h"
-#include "mallocvar.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
 #include "pm.h"
 #include "pm_system.h"
 
@@ -133,7 +134,7 @@ createPipeFeeder(void          pipeFeederRtn(int, void *),
     int pipeToFeed[2];
     pid_t rc;
 
-    pipe(pipeToFeed);
+    pm_pipe(pipeToFeed);
     rc = fork();
     if (rc < 0) {
         pm_error("fork() of stdin feeder failed.  errno=%d (%s)", 
@@ -179,7 +180,7 @@ spawnProcessor(const char *  const progName,
     pid_t rc;
 
     if (pipeStdout)
-        pipe(stdoutpipe);
+        pm_pipe(stdoutpipe);
 
     rc = fork();
     if (rc < 0) {
@@ -220,6 +221,20 @@ spawnProcessor(const char *  const progName,
 static const char *
 signalName(unsigned int const signalClass) {
 
+/* There are various signal classes that are not universally defined,
+   so we make a half-hearted attempt to determine whether they are and
+   not try to recognize the ones that aren't.  We do this by testing
+   whether a macro is defind with the signal class name.  That could give
+   a false negative, because the signal class name isn't necessarily
+   defined as a macro, but it's a really, really small problem to miss
+   one of these signal classes here, so we don't bother with all the work
+   it would take to do it right.
+
+   OpenBSD does not have SIGWINCH and SIGIO in 2013.  Everyone else seems
+   to have them.  OpenBSD does have them if the code is not declared as
+   X/open code (i.e. OpenBSD seems to interpret _XOPEN_SOURCE backward -
+   it removes features rather than adds them).
+*/
     switch (signalClass) {
     case SIGHUP: /* POSIX.1 */
         return "SIGHUP";
@@ -263,6 +278,11 @@ signalName(unsigned int const signalClass) {
         return "SIGTTIN";
     case SIGTTOU: /* POSIX.1 */
         return "SIGTTOU";
+#ifdef SIGURG
+/* SCO Openserver 5.0.7/3.2 does not have SIGURG */
+    case SIGURG:
+        return "SIGURG";
+#endif
     case SIGXCPU:
         return "SIGXCPU";
     case SIGXFSZ:
@@ -271,17 +291,23 @@ signalName(unsigned int const signalClass) {
         return "SIGVTALRM";
     case SIGPROF:
         return "SIGPROF";
+#ifdef SIGWINCH
+    case SIGWINCH:
+        return "SIGWINCH";
+#endif
+#ifdef SIGIO
+/* SCO Openserver 5.0.7/3.2 does not have SIGIO */
+    case SIGIO:
+        return "SIGIO";
+#endif
+#ifdef SIGPWR
+    case SIGPWR:
+        return "SIGPWR";
+#endif
     case SIGSYS:
         return "SIGSYS";
     default:
         return "???";
-
-        /* There are various other signal classes on some systems, but
-           not defined by POSIX and not on at least one system we
-           know of for which someone wanted to compile Netpbm.  The
-           list includes: SIGPWR, SIGLOST, SIGINFO, SIGRTxx,
-           SIGURG, SIGWINCH, SIGIO.
-        */
     }
 }
 
@@ -508,20 +534,20 @@ pm_feed_from_memory(int    const pipeToFeedFd,
 
     struct bufferDesc * const inputBufferP = feederParm;
     
-    FILE * const outfile = fdopen(pipeToFeedFd, "w");
+    FILE * const outFileP = fdopen(pipeToFeedFd, "w");
     
-    int bytesTransferred;
+    size_t bytesTransferred;
 
     /* The following signals (and normally kills) the process with
        SIGPIPE if the pipe does not take all 'size' bytes.
     */
     bytesTransferred = 
-        fwrite(inputBufferP->buffer, 1, inputBufferP->size, outfile);
+        fwrite(inputBufferP->buffer, 1, inputBufferP->size, outFileP);
 
     if (inputBufferP->bytesTransferredP)
         *(inputBufferP->bytesTransferredP) = bytesTransferred;
 
-    fclose(outfile);
+    fclose(outFileP);
 }
 
 
@@ -532,14 +558,14 @@ pm_accept_to_memory(int             const pipetosuckFd,
 
     struct bufferDesc * const outputBufferP = accepterParm;
     
-    FILE * const infile = fdopen(pipetosuckFd, "r");
+    FILE * const inFileP = fdopen(pipetosuckFd, "r");
 
-    int bytesTransferred;
+    size_t bytesTransferred;
 
     bytesTransferred =
-        fread(outputBufferP->buffer, 1, outputBufferP->size, infile);
+        fread(outputBufferP->buffer, 1, outputBufferP->size, inFileP);
 
-    fclose(infile);
+    fclose(inFileP);
 
     if (outputBufferP->bytesTransferredP)
         *(outputBufferP->bytesTransferredP) = bytesTransferred;
diff --git a/lib/libsystem_dummy.c b/lib/libsystem_dummy.c
index db26ba7a..97dd8984 100644
--- a/lib/libsystem_dummy.c
+++ b/lib/libsystem_dummy.c
@@ -4,7 +4,7 @@
   This is a dummy version of libsystem.c, for use on systems that don't
   have the kind of process control that libsystem.c needs.
 
-  With this module, program will build cleanly, but if a program actually
+  With this module, programs will build cleanly, but if a program actually
   calls pm_system(), it will die with an error message saying that
   the facility is not available.
 =============================================================================*/
diff --git a/lib/lum.h b/lib/lum.h
index 34d3866e..3d20032b 100644
--- a/lib/lum.h
+++ b/lib/lum.h
@@ -1,3 +1,5 @@
+#include <netpbm/pm_config.h>
+
     /* Lookup tables for fast RGB -> luminance calculation. */
     static int times77[256] = {
 	    0,    77,   154,   231,   308,   385,   462,   539,
diff --git a/lib/pam.h b/lib/pam.h
index c28c5c2c..c58e36a2 100644
--- a/lib/pam.h
+++ b/lib/pam.h
@@ -1,11 +1,14 @@
-/*----------------------------------------------------------------------------
+/*=============================================================================
    These are declarations for use with the Portable Arbitrary Map (PAM)
    format and the Netpbm library functions specific to them.
------------------------------------------------------------------------------*/
 
+   This file was originally written by Bryan Henderson and is contributed
+   to the public domain by him and subsequent authors.
+=============================================================================*/
 #ifndef PAM_H
 #define PAM_H
 
+#include <netpbm/pm_config.h>
 #include <netpbm/pm.h>
 #include <netpbm/pnm.h>
 
@@ -49,9 +52,13 @@ struct pam {
         */
     FILE * file;
     int format;
-        /* The format code of the raw image.  This is PAM_FORMAT
+        /* The format code of the image.  This is PAM_FORMAT
            unless the PAM image is really a view of a PBM, PGM, or PPM
-           image.  Then it's PBM_FORMAT, RPBM_FORMAT, etc.
+           image.  Then it's PBM_FORMAT, RPBM_FORMAT, etc.  For output,
+           only the format _type_ is significant, e.g. PBM_FORMAT
+           and RPBM_FORMAT have identical effect.  This is because on
+           output, 'plainformat' determines whether the output is the
+           raw or plain format of the type given by 'format'.
            */
     unsigned int plainformat;
         /* Logical: On output, use plain version of the format type
@@ -64,6 +71,9 @@ struct pam {
            Before Netpbm 10.32, this was rather different.  It simply
            described for convenience the plainness of the format indicated
            by 'format'.
+
+           This is meaningless when 'format' is PAM_FORMAT, as PAM does not
+           have plain and raw variations.
         */
     int height;  /* Height of image in rows */
     int width;   
@@ -109,6 +119,23 @@ struct pam {
            libnetpbm does not return comments and does not allocate any
            storage.
         */
+    int visual;  /* boolean */
+        /* tuple_type is one of the PAM-defined tuple types for visual
+           images ("GRAYSCALE", "RGB_ALPHA", etc.).
+        */
+    unsigned int color_depth;
+        /* Number of color planes (i.e. 'depth', but without transparency).
+           The color planes are the lowest numbered ones.  Meaningless if
+           'visual' is false.
+        */
+    int have_opacity;   /* boolean */
+        /* The tuples have an opacity (transparency, alpha) plane.
+           Meaningless if 'visual' is false.
+        */
+    unsigned int opacity_plane;
+        /* The plane number of the opacity plane;  meaningless if
+           'haveOpacity' is false or 'visual' is false.
+        */
 };
 
 #define PAM_HAVE_ALLOCATION_DEPTH 1
@@ -136,6 +163,9 @@ struct pam {
 #define PAM_PBM_TUPLETYPE "BLACKANDWHITE"
 #define PAM_PGM_TUPLETYPE "GRAYSCALE"
 #define PAM_PPM_TUPLETYPE "RGB"
+#define PAM_PBM_ALPHA_TUPLETYPE "BLACKANDWHITE_ALPHA"
+#define PAM_PGM_ALPHA_TUPLETYPE "GRAYSCALE_ALPHA"
+#define PAM_PPM_ALPHA_TUPLETYPE "RGB_ALPHA"
 
 #define PAM_PBM_BLACK PAM_BLACK
 #define PAM_PBM_WHITE PAM_BW_WHITE
@@ -275,6 +305,14 @@ pnm_makearrayrgb(const struct pam * const pamP,
                  tuple **           const tuples);
 
 void
+pnm_makerowrgba(const struct pam * const pamP,
+                tuple *            const tuplerow);
+
+void
+pnm_addopacityrow(const struct pam * const pamP,
+                  tuple *            const tuplerow);
+
+void
 pnm_getopacity(const struct pam * const pamP,
                int *              const haveOpacityP,
                unsigned int *     const opacityPlaneP);
@@ -282,10 +320,6 @@ pnm_getopacity(const struct pam * const pamP,
 void
 pnm_createBlackTuple(const struct pam * const pamP, tuple * const blackTupleP);
 
-void
-createBlackTuple(const struct pam * const pamP, tuple * const blackTupleP);
-
-
 tuple
 pnm_allocpamtuple(const struct pam * const pamP);
 
@@ -483,9 +517,15 @@ pnm_YCbCr_to_rgbtuple(const struct pam * const pamP,
     ((tuple)[PAM_RED_PLANE] == (tuple)[PAM_GRN_PLANE] && \
      (tuple)[PAM_RED_PLANE] == (tuple)[PAM_BLU_PLANE])
 
+tuple
+pnm_backgroundtuple(struct pam *  const pamP,
+                    tuple      ** const tuples);
+
 /*----------------------------------------------------------------------------
    These are meant for passing to pm_system() as Standard Input feeder
    and Standard Output accepter.
+
+   The 'feederParm' or 'accepterParm' is a pointer to a struct pamtuples.
 -----------------------------------------------------------------------------*/
 
 void
diff --git a/lib/pamdraw.h b/lib/pamdraw.h
new file mode 100644
index 00000000..aaa6f20e
--- /dev/null
+++ b/lib/pamdraw.h
@@ -0,0 +1,317 @@
+/* pamdraw.h - header file for simple drawing routines in libnetpbm.
+**
+** Simple, yes, and also fairly slow if the truth be told; but also very
+** flexible and powerful.
+**
+** The two basic concepts are the drawproc and clientdata.  All the drawing
+** routines take a drawproc that does the actual drawing.  A drawproc draws
+** a single point, and it looks like this:
+*/
+
+#include <netpbm/pm_config.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#if 0
+} /* to fake out automatic code indenters */
+#endif
+
+
+typedef struct {
+    int x;
+    int y;
+} pamd_point;
+
+static __inline__ pamd_point
+pamd_makePoint(int const x,
+               int const y) {
+
+    pamd_point p;
+
+    p.x = x;
+    p.y = y;
+
+    return p;
+}
+
+void
+pamd_validateCoord(int const c);
+
+void
+pamd_validatePoint(pamd_point const p);
+
+typedef enum {
+    PAMD_PATHLEG_LINE
+} pamd_pathlegtype;
+
+struct pamd_linelegparms {
+    pamd_point end;
+};
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   A leg of a pamd_path.
+-----------------------------------------------------------------------------*/
+    pamd_pathlegtype type;
+    union {
+        struct pamd_linelegparms linelegparms;
+    } u;
+} pamd_pathleg;
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   A closed path
+-----------------------------------------------------------------------------*/
+    unsigned int version;
+        /* Must be zero.  For future expansion. */
+    pamd_point   begPoint;  
+    unsigned int legCount;
+        /* Number of legs in the path; i.e. size of 'legs' array */
+    size_t       legSize;
+        /* Size of storage occupied by one pamd_pathleg.  I.e.
+           sizeof(pamd_pathleg).  Used for
+           binary backward compatibility between callers and libpamd
+           as the definition of pamd_pathleg changes.
+        */
+    pamd_pathleg * legs;
+} pamd_path;
+
+
+
+typedef void pamd_drawproc(tuple **, unsigned int, unsigned int, unsigned int,
+                           sample, pamd_point, const void *);
+
+pamd_drawproc pamd_point_drawproc;
+
+/*
+** So, you call a drawing routine, e.g. pamd_line(), and pass it a drawproc;
+** it calls the drawproc for each point it wants to draw.  Why so complicated?
+** Because you can define your own drawprocs to do more interesting things than
+** simply draw the point.  For example, you could make one that calls back into
+** another drawing routine, say pamd_circle() to draw a circle at each point
+** of a line.
+**
+** Slow?  Well sure, we're talking results here, not realtime.  You can do
+** tricks with this arrangement that you couldn't even think of before.
+** Still, to speed things up for the 90% case you can use this:
+*/
+#define PAMD_NULLDRAWPROC NULL
+/*
+** Just like pamd_point_drawproc() it simply draws the point, but it's done
+** inline, and clipping is assumed to be handled at a higher level.
+**
+** Now, what about clientdata.  Well, it's an arbitrary pointer, and can
+** mean something different to each different drawproc.  For the above two
+** drawprocs, clientdata should be a pointer to a tuple holding the color
+** to be drawn.  Other drawprocs can use it to point to something else,
+** e.g. some structure to be modified, or they can ignore it.
+*/
+
+
+/* Outline drawing routines.  Lines, splines, circles, etc. */
+
+int 
+pamd_setlinetype(int const type);
+
+#define PAMD_LINETYPE_NORMAL 0
+#define PAMD_LINETYPE_NODIAGS 1
+/* If you set NODIAGS, all tuples drawn by pamd_line() will be 4-connected
+** instead of 8-connected; in other words, no diagonals.  This is useful
+** for some applications, for example when you draw many parallel lines
+** and you want them to fit together without gaps.
+*/
+
+int
+pamd_setlineclip(int const clip);
+
+#define pamd_setlineclipping(x)     pamd_setlineclip(x)
+/* Normally, pamd_line() clips to the edges of the pixmap.  You can use this
+** routine to disable the clipping, for example if you are using a drawproc
+** that wants to do its own clipping.
+*/
+
+void
+pamd_line(tuple **      const tuples, 
+          int           const cols, 
+          int           const rows, 
+          int           const depth, 
+          sample        const maxval, 
+          pamd_point    const p0,
+          pamd_point    const p1,
+          pamd_drawproc       drawProc,
+          const void *  const clientdata);
+
+    /* Draws a line from p0 to p1.  */
+
+void
+pamd_spline3(tuple **      const tuples, 
+             int           const cols, 
+             int           const rows, 
+             int           const depth, 
+             sample        const maxval, 
+             pamd_point    const p0,
+             pamd_point    const p1,
+             pamd_point    const p2,
+             pamd_drawproc       drawProc,
+             const void *  const clientdata);
+
+    /* Draws a three-point spline from p0 to p2, with p1
+       as the control point.  All drawing is done via pamd_line(),
+       so the routines that control it control pamd_spline3p() as well. 
+    */
+
+void
+pamd_polyspline(tuple **      const tuples, 
+                unsigned int  const cols, 
+                unsigned int  const rows, 
+                unsigned int  const depth, 
+                sample        const maxval, 
+                pamd_point    const p0,
+                unsigned int  const nc,
+                pamd_point *  const c,
+                pamd_point    const p1,
+                pamd_drawproc       drawProc,
+                const void *  const clientdata);
+
+    /* Draws a bunch of splines end to end.  p0 and p1 are the initial and
+       final points, and the c[] are the intermediate control points.  nc is
+       the number of these control points.
+    */
+
+void
+pamd_spline4(tuple **      const tuples, 
+             unsigned int  const cols, 
+             unsigned int  const rows, 
+             unsigned int  const depth, 
+             sample        const maxval, 
+             pamd_point    const endPt0,
+             pamd_point    const endPt1,
+             pamd_point    const ctlPt0,
+             pamd_point    const ctlPt1,
+             pamd_drawproc       drawProc,
+             const void *  const clientdata);
+
+void
+pamd_circle(tuple **      const tuples, 
+            unsigned int  const cols, 
+            unsigned int  const rows, 
+            unsigned int  const depth, 
+            sample        const maxval, 
+            pamd_point    const center,
+            unsigned int  const radius, 
+            pamd_drawproc       drawProc,
+            const void *  const clientData);
+    /* Draws a circle centered at 'center' with radius 'radius' */
+
+/* Simple filling routines.  */
+
+void
+pamd_filledrectangle(tuple **      const tuples, 
+                     int           const cols, 
+                     int           const rows, 
+                     int           const depth, 
+                     sample        const maxval, 
+                     int           const left, 
+                     int           const top, 
+                     int           const width, 
+                     int           const height, 
+                     pamd_drawproc       drawProc,
+                     const void *  const clientdata);
+    /* Fills in the rectangle [left, top, width, height]. */
+
+
+void
+pamd_fill_path(tuple **      const tuples, 
+               int           const cols, 
+               int           const rows, 
+               int           const depth, 
+               sample        const maxval,
+               pamd_path *   const pathP,
+               tuple         const color);
+    /* Fills in a closed path.  Not much different from pamd_fill(),
+       but with a different interface.
+    */
+
+
+/* Arbitrary filling routines.  With these you can fill any outline that
+** you can draw with the outline routines.
+*/
+
+struct fillobj;
+
+struct fillobj *
+pamd_fill_create(void);
+    /* Returns a blank fillhandle. */
+
+void
+pamd_fill_destroy(struct fillobj * fillObjP);
+
+void
+pamd_fill_drawproc(tuple **     const tuples, 
+                   unsigned int const cols, 
+                   unsigned int const rows, 
+                   unsigned int const depth, 
+                   sample       const maxval, 
+                   pamd_point   const p,
+                   const void * const clientdata);
+    /* Use this drawproc to trace the outline you want filled.  Use
+       the fillhandle as the clientdata.
+    */
+
+void
+pamd_fill(tuple **         const tuples, 
+          int              const cols, 
+          int              const rows, 
+          int              const depth, 
+          sample           const maxval, 
+          struct fillobj * const fillObjP,
+          pamd_drawproc          drawProc,
+          const void *     const clientdata);
+
+    /* Once you've traced the outline, give the fillhandle to this routine to
+       do the actual drawing.  As usual, it takes a drawproc and clientdata;
+       you could define drawprocs to do stipple fills and such.
+    */
+
+/* Text drawing routines. */
+
+void
+pamd_text(tuple**       const tuples, 
+          int           const cols, 
+          int           const rows, 
+          int           const depth, 
+          sample        const maxval, 
+          pamd_point    const pos,
+          int           const height, 
+          int           const angle, 
+          const char *  const sArg, 
+          pamd_drawproc       drawProc,
+          const void *  const clientdata);
+
+    /* Draws the null-terminated string 's' left justified at the point ('x',
+       'y').  The text will be 'height' tuples high and will be aligned on a
+       baseline inclined 'angle' degrees with the X axis.  The supplied
+       drawproc and clientdata are passed to pamd_line() which performs the
+       actual drawing.
+    */
+
+void
+pamd_text_box(int          const height, 
+              int          const angle, 
+              const char * const s, 
+              int *        const leftP, 
+              int *        const topP, 
+              int *        const rightP, 
+              int *        const bottomP);
+    /* Calculates the extents box for text drawn by pamd_text with the given
+       string, size, and orientation.  Most extent box calculations should use
+       an angle specification of zero to calculate the unrotated box enclosing
+       the text.  If you need the extents of rotated text, however, you can
+       call pamd_text_box with a nonzero angle.
+    */
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/lib/pammap.h b/lib/pammap.h
index c9c1deed..da2e2470 100644
--- a/lib/pammap.h
+++ b/lib/pammap.h
@@ -1,11 +1,11 @@
-/******************************************************************************
+/*=============================================================================
                                 pammap.h
-*******************************************************************************
+===============================================================================
 
   Interface header file for hash table and lookup table pam functions
   in libpnm.
 
-******************************************************************************/
+=============================================================================*/
 
 #ifndef PAMMAP_H
 #define PAMMAP_H
diff --git a/lib/path.c b/lib/path.c
index 5a1d4988..10ae92d2 100644
--- a/lib/path.c
+++ b/lib/path.c
@@ -10,8 +10,8 @@
 
 #include <assert.h>
 
-#include "pm_c_util.h"
-#include "mallocvar.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
 #include "ppm.h"
 #include "ppmdfont.h"
 #include "ppmdraw.h"
diff --git a/lib/pbm.h b/lib/pbm.h
index 24574d07..a29adb48 100644
--- a/lib/pbm.h
+++ b/lib/pbm.h
@@ -58,9 +58,21 @@ pbm_allocrow(unsigned int const cols);
 #define pbm_freearray_packed(packed_bits, rows) \
     pm_freearray((char **) packed_bits, rows)
 
-bit** pbm_readpbm(FILE* file, int* colsP, int* rowsP);
-void pbm_readpbminit(FILE* file, int* colsP, int* rowsP, int* formatP);
-void pbm_readpbmrow(FILE* file, bit* bitrow, int cols, int format);
+bit**
+pbm_readpbm(FILE * const file,
+            int  * const colsP,
+            int  * const rowsP);
+
+void
+pbm_readpbminit(FILE * const file,
+                int  * const colsP,
+                int  * const rowsP, int * const formatP);
+
+void
+pbm_readpbmrow(FILE * const file,
+               bit  * const bitrow,
+               int    const cols,
+               int    const format);
 
 void
 pbm_readpbmrow_packed(FILE *          const file, 
@@ -76,6 +88,10 @@ pbm_readpbmrow_bitoffset(FILE *          const fileP,
                          unsigned int    const offset);
 
 void
+pbm_cleanrowend_packed(unsigned char * const packedBits,
+                       unsigned int    const cols);
+
+void
 pbm_writepbminit(FILE * const fileP, 
                  int    const cols, 
                  int    const rows, 
diff --git a/lib/pbmfont.h b/lib/pbmfont.h
index a977a11f..bedf57b9 100644
--- a/lib/pbmfont.h
+++ b/lib/pbmfont.h
@@ -1,6 +1,8 @@
 /* pbmfont.h - header file for font routines in libpbm
 */
 
+#include "pbm.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/lib/pm.h b/lib/pm.h
index a0c2005e..47cbfe83 100644
--- a/lib/pm.h
+++ b/lib/pm.h
@@ -23,10 +23,6 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 
-#ifdef VMS
-#include <perror.h>
-#endif
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -90,7 +86,7 @@ extern "C" {
    We're ignoring S_IREAD now to see if anyone misses it.  If there are still
    users that need it, we can handle it here.
 */
-#ifdef WIN32
+#if MSVCRT
   #define PM_S_IWUSR _S_IWRITE
   #define PM_S_IRUSR _S_IREAD
 #else
@@ -123,6 +119,9 @@ pm_proginit(int * const argcP, const char * argv[]);
 void
 pm_setMessage(int const newState, int * const oldStateP);
 
+int
+pm_getMessage(void);
+
 FILE * 
 pm_tmpfile(void);
 
@@ -185,6 +184,21 @@ pm_setjmpbufsave(jmp_buf *  const jmpbufP,
 void
 pm_longjmp(void);
 
+void
+pm_fork(int *         const iAmParentP,
+        pid_t *       const childPidP,
+        const char ** const errorP);
+
+void
+pm_waitpid(pid_t         const pid,
+           int *         const statusP,
+           int           const options,
+           pid_t *       const exitedPidP,
+           const char ** const errorP);
+
+
+void
+pm_waitpidSimple(pid_t const pid);
 
 typedef void pm_usermessagefn(const char * msg);
 
@@ -205,6 +219,9 @@ pm_errormsg(const char format[], ...);
 void PM_GNU_PRINTF_ATTR(1,2)
 pm_error (const char reason[], ...);       
 
+int
+pm_have_float_format(void);
+
 /* Obsolete - use shhopt and user's manual instead */
 void 
 pm_usage (const char usage[]);             
@@ -280,6 +297,16 @@ pm_readbiglongu(FILE *          const ifP,
 }
 
 int
+pm_readbiglong2(FILE * const ifP, 
+                int32_t * const lP);
+
+static __inline__ int
+pm_readbiglongu2(FILE *     const ifP,
+                 uint32_t * const lP) {
+    return pm_readbiglong2(ifP, (int32_t *) lP);
+}
+
+int
 pm_writebiglong(FILE * const ofP,
                 long   const l);
 
@@ -320,6 +347,16 @@ pm_readlittlelongu(FILE *          const ifP,
 }
 
 int
+pm_readlittlelong2(FILE *    const ifP,
+                   int32_t * const lP);
+
+static __inline__ int
+pm_readlittlelong2u(FILE *     const ifP,
+                    uint32_t * const lP) {
+    return pm_readlittlelong2(ifP, (int32_t *) lP);
+}
+
+int
 pm_writelittlelong(FILE * const ofP,
                    long   const l);
 
diff --git a/lib/pmfileio.c b/lib/pmfileio.c
index 84ab53ab..8176ae6a 100644
--- a/lib/pmfileio.c
+++ b/lib/pmfileio.c
@@ -10,7 +10,8 @@
        does it in other libc's).  pm_config.h defines TMPDIR as P_tmpdir
        in some environments.
     */
-#define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko are defined */
+#define _BSD_SOURCE    /* Make sure strdup is defined */
+#define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko, strdup are defined */
 #define _LARGEFILE_SOURCE 1  /* Make sure ftello, fseeko are defined */
 #define _LARGEFILE64_SOURCE 1 
 #define _FILE_OFFSET_BITS 64
@@ -28,19 +29,21 @@
 #define _LARGE_FILE_API
     /* This makes the the x64() functions available on AIX */
 
+#include "netpbm/pm_config.h"
 #include <unistd.h>
+#include <assert.h>
 #include <stdio.h>
 #include <fcntl.h>
 #include <stdarg.h>
 #include <string.h>
 #include <errno.h>
-#ifdef __DJGPP__
-  #include <io.h>
+#if HAVE_IO_H
+  #include <io.h>  /* For mktemp */
 #endif
 
-#include "pm_c_util.h"
-#include "mallocvar.h"
-#include "nstring.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
 
 #include "pm.h"
 
@@ -48,18 +51,14 @@
 
 /* File open/close that handles "-" as stdin/stdout and checks errors. */
 
-FILE*
+FILE *
 pm_openr(const char * const name) {
-    FILE* f;
+    FILE * f;
 
-    if (strcmp(name, "-") == 0)
+    if (streq(name, "-"))
         f = stdin;
     else {
-#ifndef VMS
         f = fopen(name, "rb");
-#else
-        f = fopen(name, "r", "ctx=stm");
-#endif
         if (f == NULL) 
             pm_error("Unable to open file '%s' for reading.  "
                      "fopen() returns errno %d (%s)", 
@@ -70,18 +69,14 @@ pm_openr(const char * const name) {
 
 
 
-FILE*
+FILE *
 pm_openw(const char * const name) {
-    FILE* f;
+    FILE * f;
 
-    if (strcmp(name, "-") == 0)
+    if (streq(name, "-"))
         f = stdout;
     else {
-#ifndef VMS
         f = fopen(name, "wb");
-#else
-        f = fopen(name, "w", "mbc=32", "mbf=2");  /* set buffer factors */
-#endif
         if (f == NULL) 
             pm_error("Unable to open file '%s' for writing.  "
                      "fopen() returns errno %d (%s)", 
@@ -129,10 +124,10 @@ tempFileOpenFlags(void) {
     retval = 0
         | O_CREAT
         | O_RDWR
-#ifndef WIN32
+#if !MSVCRT
         | O_EXCL
 #endif
-#ifdef WIN32
+#if MSVCRT
         | O_BINARY
 #endif
         ;
@@ -226,25 +221,25 @@ makeTmpfileWithTemplate(const char *  const filenameTemplate,
     filenameBuffer = strdup(filenameTemplate);
 
     if (filenameBuffer == NULL)
-        asprintfN(errorP, "Unable to allocate storage for temporary "
-                  "file name");
+        pm_asprintf(errorP, "Unable to allocate storage for temporary "
+                    "file name");
     else {
         int rc;
         
         rc = mkstemp2(filenameBuffer);
         
         if (rc < 0)
-            asprintfN(errorP,
-                      "Unable to create temporary file according to name "
-                      "pattern '%s'.  mkstemp() failed with errno %d (%s)",
-                      filenameTemplate, errno, strerror(errno));
+            pm_asprintf(errorP,
+                        "Unable to create temporary file according to name "
+                        "pattern '%s'.  mkstemp() failed with errno %d (%s)",
+                        filenameTemplate, errno, strerror(errno));
         else {
             *fdP = rc;
             *filenameP = filenameBuffer;
             *errorP = NULL;
         }
         if (*errorP)
-            strfree(filenameBuffer);
+            pm_strfree(filenameBuffer);
     }
 }
 
@@ -255,13 +250,10 @@ pm_make_tmpfile_fd(int *         const fdP,
                    const char ** const filenameP) {
 
     const char * filenameTemplate;
-    unsigned int fnamelen;
     const char * tmpdir;
     const char * dirseparator;
     const char * error;
 
-    fnamelen = strlen(pm_progname) + 10; /* "/" + "_XXXXXX\0" */
-
     tmpdir = tmpDir();
 
     if (tmpdir[strlen(tmpdir) - 1] == '/')
@@ -269,20 +261,20 @@ pm_make_tmpfile_fd(int *         const fdP,
     else
         dirseparator = "/";
     
-    asprintfN(&filenameTemplate, "%s%s%s%s", 
-              tmpdir, dirseparator, pm_progname, "_XXXXXX");
+    pm_asprintf(&filenameTemplate, "%s%s%s%s", 
+                tmpdir, dirseparator, pm_progname, "_XXXXXX");
 
-    if (filenameTemplate == strsol)
-        asprintfN(&error,
-                  "Unable to allocate storage for temporary file name");
+    if (filenameTemplate == pm_strsol)
+        pm_asprintf(&error,
+                    "Unable to allocate storage for temporary file name");
     else {
         makeTmpfileWithTemplate(filenameTemplate, fdP, filenameP, &error);
 
-        strfree(filenameTemplate);
+        pm_strfree(filenameTemplate);
     }
     if (error) {
         pm_errormsg("%s", error);
-        strfree(error);
+        pm_strfree(error);
         pm_longjmp();
     }
 }
@@ -302,7 +294,7 @@ pm_make_tmpfile(FILE **       const filePP,
     if (*filePP == NULL) {
         close(fd);
         unlink(*filenameP);
-        strfree(*filenameP);
+        pm_strfree(*filenameP);
 
         pm_error("Unable to create temporary file.  "
                  "fdopen() failed with errno %d (%s)",
@@ -312,17 +304,130 @@ pm_make_tmpfile(FILE **       const filePP,
 
 
 
+bool const canUnlinkOpen = 
+#if CAN_UNLINK_OPEN
+    1
+#else
+    0
+#endif
+    ;
+
+
+
+typedef struct UnlinkListEntry {
+/*----------------------------------------------------------------------------
+   This is an entry in the linked list of files to close and unlink as the
+   program exits.
+-----------------------------------------------------------------------------*/
+    struct UnlinkListEntry * next;
+    int                      fd;
+    char                     fileName[1];  /* Actually variable length */
+} UnlinkListEntry;
+
+static UnlinkListEntry * unlinkListP;
+
+
+
+static void
+unlinkTempFiles(void) {
+/*----------------------------------------------------------------------------
+  Close and unlink (so presumably delete) the files in the list
+  *unlinkListP.
+
+  This is an atexit function.
+-----------------------------------------------------------------------------*/
+    while (unlinkListP) {
+        UnlinkListEntry * const firstEntryP = unlinkListP;
+
+        unlinkListP = unlinkListP->next;
+
+        close(firstEntryP->fd);
+        unlink(firstEntryP->fileName);
+
+        free(firstEntryP);
+    }
+}
+
+
+
+static UnlinkListEntry *
+newUnlinkListEntry(const char * const fileName,
+                   int          const fd) {
+
+    UnlinkListEntry * const unlinkListEntryP =
+        malloc(sizeof(*unlinkListEntryP) + strlen(fileName) + 1);
+
+    if (unlinkListEntryP) {
+        strcpy(unlinkListEntryP->fileName, fileName);
+        unlinkListEntryP->fd   = fd;
+        unlinkListEntryP->next = NULL;
+    }
+    return unlinkListEntryP;
+}
+
+
+
+static void
+addUnlinkListEntry(const char * const fileName,
+                   int          const fd) {
+
+    UnlinkListEntry * const unlinkListEntryP =
+        newUnlinkListEntry(fileName, fd);
+
+    if (unlinkListEntryP) {
+        unlinkListEntryP->next = unlinkListP;
+        unlinkListP = unlinkListEntryP;
+    }
+}
+
+
+
+static void
+scheduleUnlinkAtExit(const char * const fileName,
+                     int          const fd) {
+/*----------------------------------------------------------------------------
+   Set things up to have the file unlinked as the program exits.
+
+   This is messy and probably doesn't work in all situations; it is a hack
+   to get Unix code essentially working on Windows, without messing up the
+   code too badly for Unix.
+-----------------------------------------------------------------------------*/
+    static bool unlinkListEstablished = false;
+    
+    if (!unlinkListEstablished) {
+        atexit(unlinkTempFiles);
+        unlinkListP = NULL;
+        unlinkListEstablished = true;
+    }
+
+    addUnlinkListEntry(fileName, fd);
+}
+
+
+
+static void
+arrangeUnlink(const char * const fileName,
+              int          const fd) {
+
+    if (canUnlinkOpen)
+        unlink(fileName);
+    else
+        scheduleUnlinkAtExit(fileName, fd);
+}
+
+
+
 FILE * 
 pm_tmpfile(void) {
 
     FILE * fileP;
-    const char * tmpfile;
+    const char * tmpfileNm;
 
-    pm_make_tmpfile(&fileP, &tmpfile);
+    pm_make_tmpfile(&fileP, &tmpfileNm);
 
-    unlink(tmpfile);
+    arrangeUnlink(tmpfileNm, fileno(fileP));
 
-    strfree(tmpfile);
+    pm_strfree(tmpfileNm);
 
     return fileP;
 }
@@ -333,13 +438,13 @@ int
 pm_tmpfile_fd(void) {
 
     int fd;
-    const char * tmpfile;
+    const char * tmpfileNm;
 
-    pm_make_tmpfile_fd(&fd, &tmpfile);
+    pm_make_tmpfile_fd(&fd, &tmpfileNm);
 
-    unlink(tmpfile);
+    arrangeUnlink(tmpfileNm, fd);
 
-    strfree(tmpfile);
+    pm_strfree(tmpfileNm);
 
     return fd;
 }
@@ -557,6 +662,23 @@ pm_readbiglong(FILE * const ifP,
 
 
 int
+pm_readbiglong2(FILE *    const ifP,
+                int32_t * const lP) {
+    int rc;
+    long l;
+
+    rc = pm_readbiglong(ifP, &l);
+
+    assert((int32_t)l == l);
+
+    *lP = (int32_t)l;
+
+    return rc;
+}
+
+
+
+int
 pm_writebiglong(FILE * const ofP, 
                 long   const l) {
 
@@ -615,6 +737,23 @@ pm_readlittlelong(FILE * const ifP,
 
 
 int
+pm_readlittlelong2(FILE *    const ifP,
+                   int32_t * const lP) {
+    int rc;
+    long l;
+
+    rc = pm_readlittlelong(ifP, &l);
+
+    assert((int32_t)l == l);
+
+    *lP = (int32_t)l;
+
+    return rc;
+}
+
+
+
+int
 pm_writelittlelong(FILE * const ofP, 
                    long   const l) {
 
@@ -892,10 +1031,6 @@ pm_check(FILE *               const file,
     pm_filepos curpos;  /* Current position of file; -1 if none */
     int rc;
 
-#ifdef LARGEFILEDEBUG
-    pm_message("pm_filepos received by pm_check() is %u bytes.",
-               sizeof(pm_filepos));
-#endif
     /* Note: FTELLO() is either ftello() or ftell(), depending on the
        capabilities of the underlying C library.  It is defined in
        pm_config.h.  ftello(), in turn, may be either ftell() or
diff --git a/lib/pnm.h b/lib/pnm.h
index e4bd34a8..3b490552 100644
--- a/lib/pnm.h
+++ b/lib/pnm.h
@@ -20,6 +20,9 @@ typedef pixval xelval;
 #define PNM_OVERALLMAXVAL PPM_OVERALLMAXVAL
 #define PNM_MAXMAXVAL PPM_MAXMAXVAL
 #define PNM_GET1(x) PPM_GETB(x)
+#define PNM_GETR(x) PPM_GETR(x)
+#define PNM_GETG(x) PPM_GETG(x)
+#define PNM_GETB(x) PPM_GETB(x)
 #define PNM_ASSIGN1(x,v) PPM_ASSIGN(x,0,0,v)
 #define PNM_ASSIGN(x,r,g,b) PPM_ASSIGN(x,r,g,b)
 #define PNM_EQUAL(x,y) PPM_EQUAL(x,y)
@@ -34,7 +37,7 @@ pnm_init(int *   const argcP,
          char ** const argv);
 
 void
-pnm_nextimage(FILE *file, int * const eofP);
+pnm_nextimage(FILE * const file, int * const eofP);
 
 xel *
 pnm_allocrow(unsigned int const cols);
diff --git a/lib/ppm.h b/lib/ppm.h
index 719189bf..82241b70 100644
--- a/lib/ppm.h
+++ b/lib/ppm.h
@@ -3,6 +3,7 @@
 #ifndef _PPM_H_
 #define _PPM_H_
 
+#include <netpbm/pm_config.h>
 #include <netpbm/pm.h>
 #include <netpbm/pgm.h>
 
@@ -76,7 +77,7 @@ ppm_blackpixel(void) {
     return retval;
 }
 
-void ppm_init(int * argcP, char* argv[]);
+void ppm_init(int * const argcP, char ** const argv);
 
 #define ppm_allocarray(cols, rows) \
   ((pixel**) pm_allocarray(cols, rows, sizeof(pixel)))
@@ -250,6 +251,12 @@ ppm_hsv_from_color(pixel  const color,
                    pixval const maxval);
 
 static __inline__ pixval
+ppm_luminosity(pixel const p) {
+
+    return (pixval)(PPM_LUMIN(p) + 0.5);
+}
+
+static __inline__ pixval
 ppm_colorvalue(pixel const p) {
 /*----------------------------------------------------------------------------
   The color value (V is HSV) as a pixval
diff --git a/lib/ppmdfont.c b/lib/ppmdfont.c
index c0db3f51..f64cd10f 100644
--- a/lib/ppmdfont.c
+++ b/lib/ppmdfont.c
@@ -3,8 +3,8 @@
 #include <errno.h>
 #include <string.h>
 
+#include "netpbm/mallocvar.h"
 #include "pm.h"
-#include "mallocvar.h"
 #include "ppmdfont.h"
 
 
diff --git a/lib/ppmdraw.h b/lib/ppmdraw.h
index d7a02e79..df22b44d 100644
--- a/lib/ppmdraw.h
+++ b/lib/ppmdraw.h
@@ -8,6 +8,8 @@
 ** a single point, and it looks like this:
 */
 
+#include <netpbm/pm_config.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/lib/rgb.txt b/lib/rgb.txt
index ad4d9d87..28231080 100644
--- a/lib/rgb.txt
+++ b/lib/rgb.txt
@@ -876,6 +876,16 @@
 139   0   0 DarkRed
 144 238 144 LightGreen
 
+# From Wikipedia article on Ochre, 10.04.21
+
+204 119  34 Ochre
+
+# From Wikipedia article on Azure, 15.11.30
+# Note that this is much different from the older colors of that name in this
+# file.
+
+  0 127 255 Azure5
+
 
 # These were more or less invented for use with Netpbm:
 255 255 255  D65
diff --git a/lib/standardppmdfont.c b/lib/standardppmdfont.c
index aa4707e2..bc0c8ff8 100644
--- a/lib/standardppmdfont.c
+++ b/lib/standardppmdfont.c
@@ -1,4 +1,4 @@
-/* THIS FILE WAS GENERATED BY 'ppmdcfont' from a ppmfont file. */
+/* THIS FILE WAS GENERATED BY 'ppmdcfont' from a ppmdfont file. */
 
 #include "ppmdfont.h"
 
diff --git a/lib/util/Makefile b/lib/util/Makefile
index 8b3fa1c1..02119edf 100644
--- a/lib/util/Makefile
+++ b/lib/util/Makefile
@@ -5,23 +5,35 @@ endif
 SUBDIR = lib/util
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
+default:all
+
 include $(BUILDDIR)/config.mk
 
 # nstring is required for asprintf(), etc.  Also some systems don't have
 # snprintf(), e.g. Solaris 2.5.1.  2002.03.29.
-UTILOBJECTS = shhopt.o nstring.o vasprintf.o filename.o nsleep.o
+UTILOBJECTS = \
+  bitio.o \
+  filename.o \
+  io.o \
+  mallocvar.o \
+  matrix.o \
+  nsleep.o \
+  nstring.o \
+  runlength.o \
+  shhopt.o \
+  token.o \
+  vasprintf.o \
 
 MERGE_OBJECTS =
 
-all: $(UTILOBJECTS)
-
 include $(SRCDIR)/common.mk
 
-INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc
+all: $(UTILOBJECTS)
+
+$(UTILOBJECTS): CFLAGS_TARGET=$(CFLAGS_SHLIB)
 
 $(UTILOBJECTS):%.o:%.c importinc
-	$(CC) -c $(INCLUDES) -DNDEBUG $(CPPFLAGS) $(CFLAGS) $(CFLAGS_SHLIB) \
-	  $(CFLAGS_PERSONAL) $(CADD) -o $@ $<
+	$(CC) -c $(INCLUDES) $(CFLAGS_ALL) -o $@ $<
 
 testnstring: test.c nstring.h nstring.o
-	$(CC) $(CFLAGS) $(CADD) -o $@ nstring.o $<
+	$(CC) $(CFLAGS_ALL) -o $@ nstring.o $<
diff --git a/lib/bitio.c b/lib/util/bitio.c
index ca1b55f9..ca1b55f9 100644
--- a/lib/bitio.c
+++ b/lib/util/bitio.c
diff --git a/lib/bitio.h b/lib/util/bitio.h
index dfc5a153..dfc5a153 100644
--- a/lib/bitio.h
+++ b/lib/util/bitio.h
diff --git a/lib/util/filename.c b/lib/util/filename.c
index e3a9a89f..18c12e3c 100644
--- a/lib/util/filename.c
+++ b/lib/util/filename.c
@@ -20,7 +20,7 @@ pm_basename(const char * const fileName) {
         if (fileName[i] == '/')
             basenameStart = i+1;
     }
-    asprintfN(&retval, "%s", &fileName[basenameStart]);
+    pm_asprintf(&retval, "%s", &fileName[basenameStart]);
 
     return retval;
 }
diff --git a/lib/util/floatcode.h b/lib/util/floatcode.h
index 23c57e9b..dc31d038 100644
--- a/lib/util/floatcode.h
+++ b/lib/util/floatcode.h
@@ -124,7 +124,7 @@ pm_doubleFromBigendDouble(pm_bigendDouble const arg) {
     }; break;
     case LITTLE_ENDIAN: {
         union {
-            unsigned char bytes[4];
+            unsigned char bytes[8];
             double native;
         } converter;
 
@@ -163,7 +163,7 @@ pm_bigendDoubleFromDouble(double const arg) {
     } break;
     case LITTLE_ENDIAN: {
         union {
-            unsigned char bytes[4];
+            unsigned char bytes[8];
             double native;
         } converter;
 
diff --git a/lib/util/intcode.h b/lib/util/intcode.h
index dd42f669..1066ee9b 100644
--- a/lib/util/intcode.h
+++ b/lib/util/intcode.h
@@ -188,8 +188,6 @@ typedef struct {
    This is an important data type because decent file formats use
    big-endian -- they don't care if some CPU happens to use some other
    code for its own work.
-
-   uint64_t is supported only when available.
 -----------------------------------------------------------------------------*/
     unsigned char bytes[8];
 } bigend64;
diff --git a/lib/util/io.c b/lib/util/io.c
new file mode 100644
index 00000000..54ecb6a8
--- /dev/null
+++ b/lib/util/io.c
@@ -0,0 +1,92 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "mallocvar.h"
+#include "nstring.h"
+
+#include "io.h"
+
+
+void
+pm_freadline(FILE *        const fileP,
+             const char ** const lineP,
+             const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Read a line (assuming the file is text with lines delimited by newlines)
+   from file *fileP.  Return that line in newly malloced storage as *lineP.
+
+   The newline delimiter is not part of the line.
+
+   As a special case, if the file doesn't end in a newline, the characters
+   after the last newline are a line.
+
+   If there are no more lines in the file, return *lineP == NULL.
+-----------------------------------------------------------------------------*/
+    char * buffer;
+    size_t bufferSize;
+    size_t cursor;
+    bool gotLine;
+    bool eof;
+
+    bufferSize = 1024;  /* initial value */
+    *errorP = NULL; /* initial value */
+    
+    MALLOCARRAY(buffer, bufferSize);
+
+    for (cursor = 0, gotLine = false, eof = false;
+         !gotLine && !eof && !*errorP; ) {
+        if (cursor + 1 >= bufferSize) {
+            if (bufferSize > INT_MAX/2) {
+                free(buffer);
+                buffer = NULL;
+            } else {
+                bufferSize *= 2;
+                REALLOCARRAY(buffer, bufferSize);
+            }
+        }
+
+        if (!buffer)
+            pm_asprintf(errorP,
+                        "Couldn't get memory for a %u-byte file read buffer.",
+                        (unsigned int)bufferSize);
+        else {
+            int const rc = getc(fileP);
+        
+            if (rc < 0) {
+                if (feof(fileP))
+                    eof = true;
+                else
+                    pm_asprintf(errorP,
+                                "Failed to read a character from file.  "
+                                "Errno = %d (%s)",
+                                errno, strerror(errno));
+            } else {
+                char const c = (char)rc;
+
+                if (c == '\n')
+                    gotLine = true;
+                else {
+                    buffer[cursor++] = c;
+                }
+            }
+        }
+    }
+    if (*errorP) {
+        if (buffer)
+            free(buffer);
+    } else {
+        if (eof && cursor == 0) {
+            *lineP = NULL;
+            free(buffer);
+        } else {
+            assert(cursor < bufferSize);
+            buffer[cursor++] = '\0';
+
+            *lineP = buffer;
+        }
+    }
+}
+
+
diff --git a/lib/util/io.h b/lib/util/io.h
new file mode 100644
index 00000000..39ccd14b
--- /dev/null
+++ b/lib/util/io.h
@@ -0,0 +1,11 @@
+#ifndef IO_H_INCLUDED
+#define IO_H_INCLUDED
+
+#include <stdio.h>
+
+void
+pm_freadline(FILE *        const fileP,
+             const char ** const lineP,
+             const char ** const errorP);
+
+#endif
diff --git a/lib/util/mallocvar.c b/lib/util/mallocvar.c
new file mode 100644
index 00000000..339d5d61
--- /dev/null
+++ b/lib/util/mallocvar.c
@@ -0,0 +1,185 @@
+#include <limits.h>
+#include <stdlib.h>
+
+#include "pm_c_util.h"
+#include "nstring.h"
+
+#include "mallocvar.h"
+
+
+static void *
+mallocz(size_t const size) {
+/*----------------------------------------------------------------------------
+   Same as malloc(), except it is legal to allocate zero bytes.
+-----------------------------------------------------------------------------*/
+    return malloc(MAX(1, size));
+}
+
+
+
+static void
+allocarrayNoHeap(void **      const rowIndex,
+                 unsigned int const rows,
+                 unsigned int const cols,
+                 unsigned int const elementSize,
+                 bool *       const failedP) {
+
+    unsigned int rowsDone;
+
+    for (rowsDone = 0, *failedP = false; rowsDone < rows && !*failedP; ) {
+        void * rowSpace;
+
+        mallocProduct(&rowSpace, cols, elementSize);
+        
+        if (rowSpace == NULL) {
+            unsigned int row;
+
+            *failedP = true;
+
+            /* unwind partially completed job */
+            for (row = 0; row < rowsDone; ++row)
+                free(rowIndex[row]);
+        } else
+            rowIndex[rowsDone++] = rowSpace;
+    }
+}
+
+
+
+static unsigned char *
+allocRowHeap(unsigned int const rows,
+             unsigned int const cols,
+             unsigned int const size) {
+/*----------------------------------------------------------------------------
+   Allocate a row heap.  That's a chunk of memory for use in a
+   pm_mallocarray2 two-dimensional array to contain the rows.
+
+   The heap must fit 'rows' rows of 'cols' columns each of elements
+   'size' bytes in size.
+
+   Return NULL if we can't get the memory.
+-----------------------------------------------------------------------------*/
+    unsigned char * retval;
+
+    if (cols != 0 && rows != 0 && UINT_MAX / cols / rows < size)
+        /* Too big even to request the memory ! */
+        retval = NULL;
+    else
+        retval = mallocz(rows * cols * size);
+
+    return retval;
+}
+
+
+
+void
+pm_mallocarray2(void **      const resultP,
+                unsigned int const rows,
+                unsigned int const cols,
+                unsigned int const elementSize)  {
+/*----------------------------------------------------------------------------
+   Allocate an array of 'rows' rows of 'cols' columns each, with each
+   element 'size' bytes.
+
+   We use the C multidimensional array paradigm:  The array is a row
+   index (array of pointers to rows) plus an array of elements for each
+   of those rows.  So a[row][col] gives you the element of the two
+   dimensional array at Row 'row', Column 'col'.
+
+   Each array element is ideally aligned to an 'elementSize' boundary.  But we
+   guarantee this only for 1, 2, 4, 8, and 16, because of limitations of libc
+   malloc() (which we use to allocate all the memory).
+
+   We tack on two extra elements to the end of the row index, transparent to
+   the user, for use in memory management (in particular, in destroying the
+   array).  The first is a NULL pointer, so you can tell the vertical
+   dimension of the array.  The second points to the row heap (see below).
+
+   We have two ways of allocating the space: fragmented and unfragmented.  In
+   both, the row index (plus the extra elements) is in one block of memory.
+   In the fragmented format, each row is also in an independent memory block,
+   and the row heap pointer (see above) is NULL.  In the unfragmented format,
+   all the rows are in a single block of memory called the row heap and the
+   row heap pointer points to that.
+
+   We use unfragmented format if possible, but if the allocation of the
+   row heap fails, we fall back to fragmented.
+-----------------------------------------------------------------------------*/
+    void ** rowIndex;
+    bool failed;
+
+    MALLOCARRAY(rowIndex, rows + 1 + 1);
+    if (rowIndex == NULL)
+        failed = true;
+    else {
+        unsigned char * rowheap;
+
+        rowheap = allocRowHeap(cols, rows, elementSize);
+
+        if (rowheap) {
+            /* It's unfragmented format */
+
+            rowIndex[rows+1] = rowheap;  /* Declare it unfragmented format */
+
+            if (rowheap) {
+                unsigned int row;
+                
+                for (row = 0; row < rows; ++row)
+                    rowIndex[row] = &(rowheap[row * cols * elementSize]);
+            }
+            failed = false;
+        } else {
+            /* We couldn't get the whole heap in one block, so try fragmented
+               format.
+            */
+            rowIndex[rows+1] = NULL;   /* Declare it fragmented format */
+            
+            allocarrayNoHeap(rowIndex, rows, cols, elementSize, &failed);
+        }
+        rowIndex[rows+0] = NULL;   /* mark end of rows */
+    }
+    if (failed)
+        *resultP = NULL;
+    else
+        *resultP = rowIndex;
+}
+
+
+
+static unsigned int
+array2RowCount(void ** const rowIndex) {
+/*----------------------------------------------------------------------------
+   Return the number of rows in the 2-dimensional array.
+-----------------------------------------------------------------------------*/
+    /* The end of the rows is marked by a null pointer where a row 
+       pointer otherwise would be.
+    */
+
+    unsigned int row;
+
+    for (row = 0; rowIndex[row]; ++row);
+
+    return row;
+}
+
+
+
+void
+pm_freearray2(void ** const rowIndex) {
+
+    unsigned int const rows = array2RowCount(rowIndex);
+
+    void * const rowheap = rowIndex[rows+1];
+
+    if (rowheap != NULL)
+        free(rowheap);
+    else {
+        /* Free each individually malloced row */
+        unsigned int row;
+        for (row = 0; row < rows; ++row)
+            pm_freerow(rowIndex[row]);
+    }
+    free(rowIndex);
+}
+
+
diff --git a/lib/util/mallocvar.h b/lib/util/mallocvar.h
index 1f2be127..e92e3fe4 100644
--- a/lib/util/mallocvar.h
+++ b/lib/util/mallocvar.h
@@ -87,7 +87,7 @@ reallocProduct(void **      const blockP,
     void * array; \
     array = arrayName; \
     reallocProduct(&array, nElements, sizeof(arrayName[0])); \
-    if (!array) \
+    if (!array && arrayName) \
         free(arrayName); \
     arrayName = array; \
 } while (0)
@@ -107,6 +107,21 @@ do { \
         abort(); \
 } while(0)
 
+#define MALLOCARRAY2(arrayName, nRows, nCols) do { \
+    void * array; \
+    pm_mallocarray2(&array, nRows, nCols, sizeof(arrayName[0][0]));  \
+    arrayName = array; \
+} while (0)
+
+#define MALLOCARRAY2_NOFAIL(arrayName, nRows, nCols) do { \
+    MALLOCARRAY2(arrayName, nRows, nCols);       \
+    if ((arrayName) == NULL) \
+        abort(); \
+} while (0)
+
+void
+pm_freearray2(void ** const rowIndex);
+
 
 #define MALLOCVAR(varName) \
     varName = malloc(sizeof(*varName))
@@ -114,6 +129,12 @@ do { \
 #define MALLOCVAR_NOFAIL(varName) \
     do {if ((varName = malloc(sizeof(*varName))) == NULL) abort();} while(0)
 
+void
+pm_mallocarray2(void **      const resultP,
+                unsigned int const cols,
+                unsigned int const rows,
+                unsigned int const elementSize);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/util/matrix.c b/lib/util/matrix.c
new file mode 100644
index 00000000..e9456e93
--- /dev/null
+++ b/lib/util/matrix.c
@@ -0,0 +1,223 @@
+/*=============================================================================
+                                matrix
+===============================================================================
+
+  Matrix math.
+
+=============================================================================*/
+
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+
+#include "matrix.h"
+
+
+static double const epsilon = 1e-10;
+
+
+
+static void
+swap(double * const aP,
+     double * const bP) {
+
+    double const oldA = *aP;
+
+    *aP = *bP;
+    *bP = oldA;
+}
+
+
+
+static void
+initializeWorkMatrices(unsigned int   const n,
+                       double **      const aInit,
+                       const double * const cInit,
+                       double ***     const aP,
+                       double **      const cP,
+                       const char **  const errorP) {
+/*----------------------------------------------------------------------------
+   Allocate memory for an n x n matrix, initialize it to the value of
+   aInit[], and return it as *aP.
+
+   Allocate memory for an n x 1 matrix, initialize it to the value of
+   cInit[], and return it as *cP.
+-----------------------------------------------------------------------------*/
+    double ** a;
+    double * c;
+
+    MALLOCARRAY2(a, n, n);
+    if (a == NULL)
+        pm_asprintf(errorP, "Could not get memory for a %u x %u matrix", n, n);
+    else {
+        unsigned int i;
+        for (i = 0; i < n; ++i) {
+            unsigned int j;
+            for (j = 0; j < n; ++j)
+                a[i][j] = aInit[i][j];
+        }
+        MALLOCARRAY(c, n);
+        if (c == NULL)
+            pm_asprintf(errorP, "Could not get memory for a %u x 1 matrix", n);
+        else {
+            unsigned int i;
+            for (i = 0; i < n; ++i)
+                c[i] = cInit[i];
+
+            *errorP = NULL;
+        }
+        if (*errorP)
+            free(a);
+    }
+    *aP = a;
+    *cP = c;
+}
+
+
+
+static void
+findLargestIthCoeff(unsigned int   const n,
+                    double **      const a,
+                    unsigned int   const i,
+                    unsigned int * const istarP,
+                    const char **  const errorP) {
+/*----------------------------------------------------------------------------
+  Among the 'i'th and following rows in 'a' (which has 'n' total rows),
+  find the one with the largest 'i'th column.
+
+  And it had better be greater than zero; if not, we fail (return *errorP
+  non-null).
+
+  Return its index as *istarP.
+-----------------------------------------------------------------------------*/
+    double maxSoFar;
+    unsigned int maxIdx;
+    unsigned int ii;
+
+    for (ii = i, maxSoFar = 0.0; ii < n; ++ii) {
+        double const thisA = fabs(a[ii][i]);
+        if (thisA >= maxSoFar) {
+            maxIdx = ii;
+            maxSoFar = thisA;
+        }
+    }
+    if (maxSoFar < epsilon) {
+        const char * const baseMsg = "Matrix equation has no unique solution";
+        if (pm_have_float_format())
+            pm_asprintf(errorP, "%s.  (debug: coeff %u %e < %e)",
+                        baseMsg, i, maxSoFar, epsilon);
+        else
+            pm_asprintf(errorP, "%s", baseMsg);
+    } else {
+        *istarP = maxIdx;
+        *errorP = NULL;
+    }
+}
+
+
+
+static void
+eliminateOneUnknown(unsigned int  const i,
+                    unsigned int  const n,
+                    double **     const a,
+                    double *      const c,
+                    const char ** const errorP) {
+
+    unsigned int maxRow;
+
+    findLargestIthCoeff(n, a, i, &maxRow, errorP);
+
+    if (!*errorP) {
+        /* swap rows 'i' and 'maxRow' in 'a' and 'c', so that the ith
+           row has the largest ith coefficient.
+        */
+        unsigned int j;
+        for (j = 0; j < n; j++)
+            swap(&a[maxRow][j], &a[i][j]);
+
+        swap(&c[maxRow], &c[i]);
+
+        /* Combine rows so that the ith coefficient in every row below
+           the ith is zero.
+        */
+        {
+            unsigned int ii;
+            for (ii = i+1; ii < n; ++ii) {
+                double const multiplier = a[ii][i] / a[i][i];
+                    /* This is what we multiply the whole ith row by to make
+                       its ith coefficient equal to that in the iith row.
+                    */
+                unsigned int j;
+
+                /* Combine ith row into iith row so that the ith coefficient
+                   in the iith is zero.
+                */
+                c[ii] = c[ii] - multiplier * c[i];
+
+                for (j = 0; j < n; ++j)
+                    a[ii][j] = a[ii][j] - multiplier * a[i][j];
+
+                assert(a[ii][i] < epsilon);
+            }
+        }
+        *errorP = NULL;
+    }
+}
+
+
+
+void
+pm_solvelineareq(double **     const aArg,
+                 double *      const x,
+                 double *      const cArg,
+                 unsigned int  const n,
+                 const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Solve the matrix equation 'a' * 'x' = 'c' for 'x'.
+
+   'n' is the dimension of the matrices.  'a' is 'n' x 'n',
+   while 'x' and 'c' are 'n' x 1.
+-----------------------------------------------------------------------------*/
+    /* We use Gaussian reduction. */
+
+    double ** a;
+    double * c;
+
+    initializeWorkMatrices(n, aArg, cArg, &a, &c, errorP);
+
+    if (!*errorP) {
+        unsigned int i;
+
+        for (i = 0, *errorP = NULL; i < n && !*errorP; ++i)
+            eliminateOneUnknown(i, n, a, c, errorP);
+
+        if (!*errorP) {
+            /* a[] now has all zeros in the lower left triangle. */
+            /* Work from the bottom up to solve for the unknowns x[], from
+               the a and c rows in question and all the x[] below it
+            */
+            
+            unsigned int k;
+            for (k = 0; k < n; ++k) {
+                unsigned int const m = n - k - 1;
+                unsigned int j;
+                double xwork;
+
+                for (j = m+1, xwork = c[m]; j < n; ++j)
+                    xwork -= a[m][j] * x[j];
+                    
+                x[m] = xwork / a[m][m];
+            }
+        }
+    }
+    pm_freearray2((void**)a);
+    free(c);
+}
+
+
+
diff --git a/lib/util/matrix.h b/lib/util/matrix.h
new file mode 100644
index 00000000..13ae0373
--- /dev/null
+++ b/lib/util/matrix.h
@@ -0,0 +1,11 @@
+#ifndef MATRIX_H_INCLUDED
+#define MATRIX_H_INCLUDED
+
+void
+pm_solvelineareq(double **     const aArg,
+                 double *      const x,
+                 double *      const cArg,
+                 unsigned int  const n,
+                 const char ** const errorP);
+
+#endif
diff --git a/lib/util/nsleep.c b/lib/util/nsleep.c
index 943b8c77..24d48207 100644
--- a/lib/util/nsleep.c
+++ b/lib/util/nsleep.c
@@ -1,4 +1,6 @@
-#ifdef WIN32
+#include "pm_config.h"
+
+#if MSVCRT
   #include <windows.h>
   #include <process.h>
 #else
@@ -10,9 +12,9 @@
 
 
 void
-sleepN(unsigned int const milliseconds) {
+pm_sleep(unsigned int const milliseconds) {
 
-#ifdef WIN32
+#if MSVCRT
     SleepEx(milliseconds, TRUE);
 #else
 
diff --git a/lib/util/nsleep.h b/lib/util/nsleep.h
index 372b8008..fdf45ab7 100644
--- a/lib/util/nsleep.h
+++ b/lib/util/nsleep.h
@@ -2,6 +2,6 @@
 #define NSLEEP_H_INCLUDED
 
 void
-sleepN(unsigned int const milliseconds);
+pm_sleep(unsigned int const milliseconds);
 
 #endif
diff --git a/lib/util/nstring.c b/lib/util/nstring.c
index 8842aa05..711cfca9 100644
--- a/lib/util/nstring.c
+++ b/lib/util/nstring.c
@@ -39,7 +39,7 @@
  * IMPLEMENTED CONVERSION SPECIFIERS AND DATA TYPES
  *
  * This snprintf implements only the following conversion specifiers:
- * s, c, d, u, o, x, X, p  (and synonyms: i, D, U, O - see below)
+ * s, c, d, u, o, x, X, p, f  (and synonyms: i, D, U, O - see below)
  * with flags: '-', '+', ' ', '0' and '#'.
  * An asterisk is acceptable for field width as well as precision.
  *
@@ -66,7 +66,7 @@
  *
  * The following is specifically NOT implemented:
  *   - flag ' (thousands' grouping character) is recognized but ignored
- *   - numeric conversion specifiers: f, e, E, g, G and synonym F,
+ *   - numeric conversion specifiers: e, E, g, G and synonym F,
  *     as well as the new a and A conversion specifiers
  *   - length modifier 'L' (long double) and 'q' (quad - use 'll' instead)
  *   - wide character/string conversions: lc, ls, and nonstandard
@@ -113,6 +113,12 @@
  */
 
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+#define _BSD_SOURCE  /* Make sure strdup() is in string.h */
+#define _GNU_SOURCE
+   /* Because of conditional compilation, this is GNU source only if the C
+      library is GNU.
+   */
 #define PORTABLE_SNPRINTF_VERSION_MAJOR 2
 #define PORTABLE_SNPRINTF_VERSION_MINOR 2
 
@@ -190,12 +196,12 @@ static char credits[] = "\n\
 
 
 void
-vsnprintfN(char *       const str,
-           size_t       const str_m,
-           const char * const fmt,
-           va_list            ap,
-           size_t *     const sizeP) {
-
+pm_vsnprintf(char *       const str,
+             size_t       const str_m,
+             const char * const fmt,
+             va_list            ap,
+             size_t *     const sizeP) {
+    
     size_t str_l = 0;
     const char *p = fmt;
 
@@ -215,16 +221,19 @@ vsnprintfN(char *       const str,
             const char *q = strchr(p + 1,'%');
             size_t n = !q ? strlen(p) : (q - p);
             if (str_l < str_m) {
-                size_t avail = str_m - str_l;
-                fast_memcpy(str + str_l, p, (n > avail ? avail : n));
+                size_t const avail = str_m - str_l;
+                fast_memcpy(str + str_l, p, (MIN(n, avail)));
             }
             p += n; str_l += n;
         } else {
             const char *starting_p;
-            size_t min_field_width = 0, precision = 0;
-            int zero_padding = 0, precision_specified = 0, justify_left = 0;
-            int alternate_form = 0, force_sign = 0;
-            int space_for_positive = 1;
+            size_t min_field_width;
+            size_t precision = 0;
+            bool precision_specified;
+            bool justify_left;
+            bool alternate_form;
+            bool force_sign;
+            bool space_for_positive;
                 /* If both the ' ' and '+' flags appear,
                    the ' ' flag should be ignored.
                 */
@@ -242,16 +251,18 @@ vsnprintfN(char *       const str,
                    argument for the c conversion is unsigned.
                 */
 
-            size_t number_of_zeros_to_pad = 0;
+            bool zero_padding;
+
+            size_t number_of_zeros_to_pad;
                 /* number of zeros to be inserted for numeric
                    conversions as required by the precision or minimal
                    field width
                 */
 
-            size_t zero_padding_insertion_ind = 0;
+            size_t zero_padding_insertion_ind;
                 /* index into tmp where zero padding is to be inserted */
 
-            char fmt_spec = '\0';
+            char fmt_spec;
                 /* current conversion specifier character */
 
             str_arg = credits;
@@ -261,17 +272,26 @@ vsnprintfN(char *       const str,
             ++p;  /* skip '%' */
 
             /* parse flags */
+            justify_left = false;  /* initial value */
+            alternate_form = false;  /* initial value */
+            force_sign = false;  /* initial value */
+            space_for_positive = false;  /* initial value */
+            zero_padding = false;  /* initial value */
+            number_of_zeros_to_pad = 0;  /* initial value */
+            zero_padding_insertion_ind = 0;  /* initial value */
+            fmt_spec = '\0';  /* initial value */
+
             while (*p == '0' || *p == '-' || *p == '+' ||
                    *p == ' ' || *p == '#' || *p == '\'') {
                 switch (*p) {
-                case '0': zero_padding = 1; break;
-                case '-': justify_left = 1; break;
-                case '+': force_sign = 1; space_for_positive = 0; break;
-                case ' ': force_sign = 1; break;
+                case '0': zero_padding = true; break;
+                case '-': justify_left = true; break;
+                case '+': force_sign = true; space_for_positive = false; break;
+                case ' ': force_sign = true; break;
                     /* If both the ' ' and '+' flags appear, the ' '
                        flag should be ignored
                     */
-                case '#': alternate_form = 1; break;
+                case '#': alternate_form = true; break;
                 case '\'': break;
                 }
                 ++p;
@@ -283,9 +303,10 @@ vsnprintfN(char *       const str,
             /* parse field width */
             if (*p == '*') {
                 int j;
-                p++; j = va_arg(ap, int);
-                if (j >= 0) min_field_width = j;
-                else { min_field_width = -j; justify_left = 1; }
+                ++p;
+                j = va_arg(ap, int);
+                if (j >= 0) { min_field_width = j; justify_left = false; }
+                else { min_field_width = -j; justify_left = true; }
             } else if (isdigit((int)(*p))) {
                 /* size_t could be wider than unsigned int; make sure
                    we treat argument like common implementations do
@@ -294,16 +315,19 @@ vsnprintfN(char *       const str,
                 while (isdigit((int)(*p)))
                     uj = 10*uj + (unsigned int)(*p++ - '0');
                 min_field_width = uj;
-            }
+            } else
+                min_field_width = 0;
+
             /* parse precision */
             if (*p == '.') {
-                p++; precision_specified = 1;
+                ++p;
+                precision_specified = true;
                 if (*p == '*') {
                     int j = va_arg(ap, int);
                     p++;
                     if (j >= 0) precision = j;
                     else {
-                        precision_specified = 0; precision = 0;
+                        precision_specified = false; precision = 0;
                         /* NOTE: Solaris 2.6 man page claims that in
                            this case the precision should be set to 0.
                            Digital Unix 4.0, HPUX 10 and BSD man page
@@ -322,7 +346,9 @@ vsnprintfN(char *       const str,
                         uj = 10*uj + (unsigned int)(*p++ - '0');
                     precision = uj;
                 }
-            }
+            } else
+                precision_specified = false;
+
             /* parse 'h', 'l' and 'll' length modifiers */
             if (*p == 'h' || *p == 'l') {
                 length_modifier = *p; p++;
@@ -357,7 +383,7 @@ vsnprintfN(char *       const str,
                     Unix and Linux does not.
                 */
 
-                zero_padding = 0;
+                zero_padding = false;
                     /* turn zero padding off for string conversions */
                 str_arg_l = 1;
                 switch (fmt_spec) {
@@ -387,9 +413,7 @@ vsnprintfN(char *       const str,
                     else {
                         /* memchr on HP does not like n > 2^31  !!! */
                         const char * q =
-                            memchr(str_arg, '\0',
-                                   precision <= 0x7fffffff ?
-                                   precision : 0x7fffffff);
+                            memchr(str_arg, '\0', MIN(precision, 0x7fffffff));
                         str_arg_l = !q ? precision : (q-str_arg);
                     }
                     break;
@@ -483,7 +507,7 @@ vsnprintfN(char *       const str,
                    Perl.
                 */
                 if (precision_specified)
-                    zero_padding = 0;
+                    zero_padding = false;
                 if (fmt_spec == 'd') {
                     if (force_sign && arg_sign >= 0)
                         tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
@@ -549,9 +573,9 @@ vsnprintfN(char *       const str,
                     */
                     if (zero_padding_insertion_ind < str_arg_l &&
                         tmp[zero_padding_insertion_ind] == '-') {
-                        zero_padding_insertion_ind++;
+                        zero_padding_insertion_ind += 1;
                     }
-                    if (zero_padding_insertion_ind+1 < str_arg_l &&
+                    if (zero_padding_insertion_ind + 1 < str_arg_l &&
                         tmp[zero_padding_insertion_ind]   == '0' &&
                         (tmp[zero_padding_insertion_ind+1] == 'x' ||
                          tmp[zero_padding_insertion_ind+1] == 'X') ) {
@@ -559,7 +583,7 @@ vsnprintfN(char *       const str,
                     }
                 }
                 {
-                    size_t num_of_digits =
+                    size_t const num_of_digits =
                         str_arg_l - zero_padding_insertion_ind;
                     if (alternate_form && fmt_spec == 'o'
                         /* unless zero is already the first character */
@@ -576,7 +600,7 @@ vsnprintfN(char *       const str,
                                explicit precision of zero
                             */
                             precision = num_of_digits+1;
-                            precision_specified = 1;
+                            precision_specified = true;
                         }
                     }
                     /* zero padding to specified precision? */
@@ -585,23 +609,37 @@ vsnprintfN(char *       const str,
                 }
                 /* zero padding to specified minimal field width? */
                 if (!justify_left && zero_padding) {
-                    int n =
+                    int const n =
                         min_field_width - (str_arg_l+number_of_zeros_to_pad);
-                    if (n > 0) number_of_zeros_to_pad += n;
+                    if (n > 0)
+                        number_of_zeros_to_pad += n;
                 }
             } break;
+            case 'f': {
+                char f[10];
+                if (precision_specified)
+                    snprintf(f, ARRAY_SIZE(f), "%%%u.%uf",
+                             (unsigned)min_field_width, (unsigned)precision);
+                else
+                    snprintf(f, ARRAY_SIZE(f), "%%%uf",
+                             (unsigned)min_field_width);
+
+                str_arg_l = sprintf(tmp, f, va_arg(ap, double));
+                str_arg = &tmp[0];
+
+                min_field_width = 0;
+                zero_padding_insertion_ind = 0;
+            } break;
             default:
-                /* unrecognized conversion specifier, keep format
-                   string as-is
+                /* Unrecognized conversion specifier.  Discard the
+                   unrecognized conversion, just keep the unrecognized
+                   conversion character.
                 */
-                zero_padding = 0;
+                zero_padding = false;
                     /* turn zero padding off for non-numeric convers. */
                 /* reset flags */
-                justify_left = 1;
+                justify_left = true;
                 min_field_width = 0;
-                /* discard the unrecognized conversion, just keep the
-                   unrecognized conversion character
-                */
                 str_arg = p;
                 str_arg_l = 0;
                 if (*p)
@@ -620,12 +658,12 @@ vsnprintfN(char *       const str,
 
             if (!justify_left) {
                 /* left padding with blank or zero */
-                int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
+                int n = min_field_width - (str_arg_l + number_of_zeros_to_pad);
                 if (n > 0) {
                     if (str_l < str_m) {
-                        size_t avail = str_m-str_l;
-                        fast_memset(str+str_l, (zero_padding ? '0' : ' '),
-                                    (n > avail ? avail : n));
+                        size_t const avail = str_m - str_l;
+                        fast_memset(str + str_l, (zero_padding ? '0' : ' '),
+                                    (MIN(n, avail)));
                     }
                     str_l += n;
                 }
@@ -639,40 +677,44 @@ vsnprintfN(char *       const str,
                 */
                 zero_padding_insertion_ind = 0;
             } else {
-                /* insert first part of numerics (sign or '0x') before
-                   zero padding
-                */
-                int n = zero_padding_insertion_ind;
-                if (n > 0) {
-                    if (str_l < str_m) {
-                        size_t avail = str_m-str_l;
-                        fast_memcpy(str+str_l, str_arg, (n>avail?avail:n));
+                {
+                    /* insert first part of numerics (sign or '0x') before
+                       zero padding
+                    */
+                    int const n = zero_padding_insertion_ind;
+                    if (n > 0) {
+                        if (str_l < str_m) {
+                            size_t const avail = str_m - str_l;
+                            fast_memcpy(str + str_l, str_arg, (MIN(n, avail)));
+                        }
+                        str_l += n;
                     }
-                    str_l += n;
                 }
-                /* insert zero padding as requested by the precision
-                   or min field width
-                */
-                n = number_of_zeros_to_pad;
-                if (n > 0) {
-                    if (str_l < str_m) {
-                        size_t avail = str_m - str_l;
-                        fast_memset(str + str_l, '0', (n > avail ? avail : n));
+                {
+                    /* insert zero padding as requested by the precision
+                       or min field width
+                    */
+                    int const n = number_of_zeros_to_pad;
+                    if (n > 0) {
+                        if (str_l < str_m) {
+                            size_t const avail = str_m - str_l;
+                            fast_memset(str + str_l, '0', (MIN(n, avail)));
+                        }
+                        str_l += n;
                     }
-                    str_l += n;
                 }
             }
             /* insert formatted string (or as-is conversion specifier
                for unknown conversions)
             */
             {
-                int n = str_arg_l - zero_padding_insertion_ind;
+                int const n = str_arg_l - zero_padding_insertion_ind;
                 if (n > 0) {
                     if (str_l < str_m) {
-                        size_t avail = str_m-str_l;
+                        size_t const avail = str_m-str_l;
                         fast_memcpy(str + str_l,
                                     str_arg + zero_padding_insertion_ind,
-                                    (n > avail ? avail : n));
+                                    MIN(n, avail));
                     }
                     str_l += n;
                 }
@@ -680,11 +722,12 @@ vsnprintfN(char *       const str,
             /* insert right padding */
             if (justify_left) {
                 /* right blank padding to the field width */
-                int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
+                int const n =
+                    min_field_width - (str_arg_l + number_of_zeros_to_pad);
                 if (n > 0) {
                     if (str_l < str_m) {
-                        size_t avail = str_m-str_l;
-                        fast_memset(str+str_l, ' ', (n>avail?avail:n));
+                        size_t const avail = str_m - str_l;
+                        fast_memset(str+str_l, ' ', (MIN(n, avail)));
                     }
                     str_l += n;
                 }
@@ -696,7 +739,7 @@ vsnprintfN(char *       const str,
            of overwriting the last character (shouldn't happen, but
            just in case)
         */
-        str[str_l <= str_m-1 ? str_l : str_m-1] = '\0';
+        str[MIN(str_l, str_m - 1)] = '\0';
     }
     *sizeP = str_l;
 }
@@ -704,17 +747,17 @@ vsnprintfN(char *       const str,
 
 
 int
-snprintfN(char *       const dest,
-          size_t       const str_m,
-          const char * const fmt,
-          ...) {
+pm_snprintf(char *       const dest,
+            size_t       const str_m,
+            const char * const fmt,
+            ...) {
 
     size_t size;
     va_list ap;
 
     va_start(ap, fmt);
     
-    vsnprintfN(dest, str_m, fmt, ap, &size);
+    pm_vsnprintf(dest, str_m, fmt, ap, &size);
 
     va_end(ap);
 
@@ -726,73 +769,96 @@ snprintfN(char *       const dest,
 
 
 /* When a function that is supposed to return a malloc'ed string cannot
-   get the memory for it, it should return 'strsol'.  That has a much
+   get the memory for it, it should return 'pm_strsol'.  That has a much
    better effect on the caller, if the caller doesn't explicitly allow for
    the out of memory case, than returning NULL.  Note that it is very
    rare for the system not to have enough memory to return a small string,
    so it's OK to have somewhat nonsensical behavior when it happens.  We
    just don't want catastrophic behavior.
 
-   'strsol' is an external symbol, so if Caller wants to detect the
+   'pm_strsol' is an external symbol, so if Caller wants to detect the
    out-of-memory failure, he certainly can.
 */
-const char * const strsol = "NO MEMORY TO CREATE STRING!";
+const char * const pm_strsol = "NO MEMORY TO CREATE STRING!";
+
+
+
+const char *
+pm_strdup(const char * const arg) {
+
+    const char * const dup = strdup(arg);
+
+    return dup ? dup : pm_strsol;
+}
 
 
 
 void PM_GNU_PRINTF_ATTR(2,3)
-asprintfN(const char ** const resultP,
-          const char *  const fmt, 
-          ...) {
+pm_asprintf(const char ** const resultP,
+            const char *  const fmt, 
+            ...) {
 
+    const char * result;
     va_list varargs;
     
+#if HAVE_VASPRINTF
+    int rc;
+    va_start(varargs, fmt);
+    rc = vasprintf((char **)&result, fmt, varargs);
+    va_end(varargs);
+    if (rc < 0)
+        result = pm_strsol;
+#else
     size_t dryRunLen;
     
     va_start(varargs, fmt);
-    
-    vsnprintfN(NULL, 0, fmt, varargs, &dryRunLen);
+
+    pm_vsnprintf(NULL, 0, fmt, varargs, &dryRunLen);
 
     va_end(varargs);
 
     if (dryRunLen + 1 < dryRunLen)
         /* arithmetic overflow */
-        *resultP = strsol;
+        result = pm_strsol;
     else {
         size_t const allocSize = dryRunLen + 1;
-        char * result;
-        result = malloc(allocSize);
-        if (result == NULL)
-            *resultP = strsol;
-        else {
+        char * buffer;
+        buffer = malloc(allocSize);
+        if (buffer != NULL) {
             va_list varargs;
             size_t realLen;
 
             va_start(varargs, fmt);
 
-            vsnprintfN(result, allocSize, fmt, varargs, &realLen);
+            pm_vsnprintf(buffer, allocSize, fmt, varargs, &realLen);
                 
             assert(realLen == dryRunLen);
             va_end(varargs);
 
-            *resultP = result;
+            result = buffer;
         }
     }
+#endif
+
+    if (result == NULL)
+        *resultP = pm_strsol;
+    else
+        *resultP = result;
 }
 
 
 
 void
-strfree(const char * const string) {
+pm_strfree(const char * const string) {
 
-    if (string != strsol)
+    if (string != pm_strsol)
         free((void *) string);
 }
 
 
 
 const char *
-strsepN(char ** const stringP, const char * const delim) {
+pm_strsep(char ** const stringP, const char * const delim) {
     const char * retval;   
 
     if (stringP == NULL || *stringP == NULL)
@@ -824,65 +890,75 @@ strsepN(char ** const stringP, const char * const delim) {
 
 
 int
-stripeq(const char * const comparand,
-        const char * const comparator) {
+pm_stripeq(const char * const comparand,
+           const char * const comparator) {
 /*----------------------------------------------------------------------------
   Compare two strings, ignoring leading and trailing white space.
 
   Return 1 (true) if the strings are identical; 0 (false) otherwise.
 -----------------------------------------------------------------------------*/
-    char *p, *q, *px, *qx;
-    char equal;
+    const char * p;
+    const char * q;
+    const char * px;
+    const char * qx;
+    bool equal;
   
     /* Make p and q point to the first non-blank character in each string.
-     If there are no non-blank characters, make them point to the terminating
-     NULL.
-     */
+       If there are no non-blank characters, make them point to the terminating
+       NUL.
+    */
 
-    p = (char *) comparand;
-    while (ISSPACE(*p)) p++;
-    q = (char *) comparator;
-    while (ISSPACE(*q)) q++;
+    p = &comparand[0];
+    while (ISSPACE(*p))
+        p++;
+    q = &comparator[0];
+    while (ISSPACE(*q))
+        q++;
 
     /* Make px and qx point to the last non-blank character in each string.
        If there are no nonblank characters (which implies the string is
-       null), make them point to the terminating NULL.
+       null), make them point to the terminating NUL.
     */
 
-    if (*p == '\0') px = p;
+    if (*p == '\0')
+        px = p;
     else {
         px = p + strlen(p) - 1;
-        while (ISSPACE(*px)) px--;
+        while (ISSPACE(*px))
+            --px;
     }
 
-    if (*q == '\0') qx = q;
+    if (*q == '\0')
+        qx = q;
     else {
         qx = q + strlen(q) - 1;
-        while (ISSPACE(*qx)) qx--;
+        while (ISSPACE(*qx))
+            --qx;
     }
 
-    equal = 1;   /* initial assumption */
-  
-    /* If the stripped strings aren't the same length, 
-       we know they aren't equal 
-     */
-    if (px - p != qx - q) equal = 0;
-
-    else
-    while (p <= px) {
-        if (*p != *q) equal = 0;
-        p++; q++;
+    if (px - p != qx - q) {
+        /* The stripped strings aren't the same length, so we know they aren't
+           equal.
+        */
+        equal = false;
+    } else {
+        /* Move p and q through the nonblank characters, comparing. */
+        for (equal = true; p <= px; ++p, ++q) {
+            assert(q <= qx);  /* Because stripped strings are same length */
+            if (*p != *q)
+                equal = false;
+        }
     }
-    return equal;
+    return equal ? 1 : 0;
 }
 
 
 
 const void *
-memmemN(const void * const haystackArg,
-        size_t       const haystacklen,
-        const void * const needleArg,
-        size_t       const needlelen) {
+pm_memmem(const void * const haystackArg,
+          size_t       const haystacklen,
+          const void * const needleArg,
+          size_t       const needlelen) {
 
     const unsigned char * const haystack = haystackArg;
     const unsigned char * const needle   = needleArg;
@@ -902,7 +978,7 @@ memmemN(const void * const haystackArg,
 
 
 bool
-strishex(const char * const subject) {
+pm_strishex(const char * const subject) {
 
     bool retval;
     unsigned int i;
@@ -919,12 +995,12 @@ strishex(const char * const subject) {
 
 
 void
-interpret_uint(const char *   const string,
-               unsigned int * const valueP,
-               const char **  const errorP) {
+pm_interpret_uint(const char *   const string,
+                  unsigned int * const valueP,
+                  const char **  const errorP) {
 
     if (string[0] == '\0')
-        asprintfN(errorP, "Null string.");
+        pm_asprintf(errorP, "Null string.");
     else {
         /* strtoul() does a bizarre thing where if the number is out
            of range, it returns a clamped value but tells you about it
@@ -939,13 +1015,13 @@ interpret_uint(const char *   const string,
         ulongValue = strtoul(string, &tail, 10);
 
         if (tail[0] != '\0')
-            asprintfN(errorP, "Non-digit stuff in string: %s", tail);
+            pm_asprintf(errorP, "Non-digit stuff in string: %s", tail);
         else if (errno == ERANGE)
-            asprintfN(errorP, "Number too large");
+            pm_asprintf(errorP, "Number too large");
         else if (ulongValue > UINT_MAX)
-            asprintfN(errorP, "Number too large");
+            pm_asprintf(errorP, "Number too large");
         else if (string[0] == '-')
-            asprintfN(errorP, "Negative number");
+            pm_asprintf(errorP, "Negative number");
             /* Sleazy code; string may have leading spaces. */
         else {
             *valueP = ulongValue;
diff --git a/lib/util/nstring.h b/lib/util/nstring.h
index 53f1e4c0..7238a76e 100644
--- a/lib/util/nstring.h
+++ b/lib/util/nstring.h
@@ -137,62 +137,68 @@ strncaseeq(const char * const comparand,
      - The returned string is a const char * instead of a char *.  The
        const is more correct.
 
-     - If the function can't get the memory, it returns 'strsol',
+     - If the function can't get the memory, it returns 'pm_strsol',
        which is a string that is in static memory that contains text
        indicating an out of memory failure has occurred, intead of
        NULL.  This makes it much easier for programs to ignore this
        possibility.
 
    strfree() is strictly a Netpbm invention, to allow proper type checking
-   when freeing storage allocated by the Netpbm asprintfN().
+   when freeing storage allocated by the Netpbm pm_asprintf().
 */
 
-extern const char * const strsol;
+extern const char * const pm_strsol;
 
 int
-snprintfN(char *       const dest,
-          size_t       const str_m,
-          const char * const fmt,
-          ...) PM_GNU_PRINTF_ATTR(3,4);
+pm_snprintf(char *       const dest,
+            size_t       const str_m,
+            const char * const fmt,
+            ...) PM_GNU_PRINTF_ATTR(3,4);
 
 void
-vsnprintfN(char *       const str,
-           size_t       const str_m,
-           const char * const fmt,
-           va_list            ap,
-           size_t *     const sizeP);
+pm_vsnprintf(char *       const str,
+             size_t       const str_m,
+             const char * const fmt,
+             va_list            ap,
+             size_t *     const sizeP);
+
+const char *
+pm_strdup(const char * const arg);
 
 void
-asprintfN(const char ** const resultP,
-          const char *  const fmt,
-          ...) PM_GNU_PRINTF_ATTR(2,3);
+pm_asprintf(const char ** const resultP,
+            const char *  const fmt,
+            ...) PM_GNU_PRINTF_ATTR(2,3);
 
 void
-vasprintfN(const char ** const resultP,
-           const char *  const format,
-           va_list             args);
+pm_vasprintf(const char ** const resultP,
+             const char *  const format,
+             va_list             args);
+
+bool
+pm_vasprintf_knows_float(void);
 
 void 
-strfree(const char * const string);
+pm_strfree(const char * const string);
 
 const char *
-strsepN(char ** const stringP, const char * const delim);
+pm_strsep(char ** const stringP, const char * const delim);
 
 int
-stripeq(const char * const comparand,
-        const char * const comparator);
+pm_stripeq(const char * const comparand,
+           const char * const comparator);
 
 const void *
-memmemN(const void * const haystackArg,
-        size_t       const haystacklen,
-        const void * const needleArg,
-        size_t       const needlelen);
+pm_memmem(const void * const haystackArg,
+          size_t       const haystacklen,
+          const void * const needleArg,
+          size_t       const needlelen);
 
 bool
-strishex(const char * const subject);
+pm_strishex(const char * const subject);
 
 void
-interpret_uint(const char *   const string,
+pm_interpret_uint(const char *   const string,
                unsigned int * const valueP,
                const char **  const errorP);
 
diff --git a/lib/util/pm_c_util.h b/lib/util/pm_c_util.h
index 07913f30..01a07657 100644
--- a/lib/util/pm_c_util.h
+++ b/lib/util/pm_c_util.h
@@ -1,6 +1,11 @@
 #ifndef PM_C_UTIL_INCLUDED
 #define PM_C_UTIL_INCLUDED
 
+/* Note that for MAX and MIN, if either of the operands is a floating point
+   Not-A-Number, the result is the second operand.  So if you're computing a
+   running maximum and want to ignore the NaNs in the computation, put the
+   running maximum variable second.
+*/
 #undef MAX
 #define MAX(a,b) ((a) > (b) ? (a) : (b))
 #undef MIN
@@ -36,35 +41,49 @@
    use "int".
 */
 
-/* We used to assume that if TRUE was defined, then bool was too.
-   However, we had a report on 2001.09.21 of a Tru64 system that had
-   TRUE but not bool and on 2002.03.21 of an AIX 4.3 system that was
-   likewise.  So now we define bool all the time, unless the macro
-   HAVE_BOOL is defined.  If someone is using the Netpbm libraries and
-   also another library that defines bool, he can either make the
-   other library define/respect HAVE_BOOL or just define HAVE_BOOL in
-   the file that includes pm_config.h or with a compiler option.  Note
-   that C++ always has bool.  
+/* We will probably never again see a system that does not have
+   <stdbool.h>, but just in case, we have our own alternative here.
 
-   A preferred way of getting booleans is <stdbool.h>.  But it's not
-   available on all platforms, and it's easy to reproduce what it does
-   here.
+   Evidence shows that the compiler actually produces better code with
+   <stdbool.h> than with bool simply typedefed to int.
 */
+
+#ifdef __cplusplus
+  /* C++ has a bool type and false and true constants built in. */
+#else
+  /* The test for __STDC__ is paranoid.  It is there just in case some
+     nonstandard compiler defines __STDC_VERSION__ in an arbitrary manner.
+  */
+  #if ( defined(__GNUC__) && (__GNUC__ >= 3) ) || \
+      ( defined(__STDC__) && (__STDC__ ==1) && \
+        defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) ) 
+    #include <stdbool.h>
+  #else
+    /* We used to assume that if TRUE was defined, then bool was too.
+       However, we had a report on 2001.09.21 of a Tru64 system that had
+       TRUE but not bool and on 2002.03.21 of an AIX 4.3 system that was
+       likewise.  So now we define bool all the time, unless the macro
+       HAVE_BOOL is defined.  If someone is using the Netpbm libraries and
+       also another library that defines bool, he can either make the
+       other library define/respect HAVE_BOOL or just define HAVE_BOOL in
+       the file that includes pm_config.h or with a compiler option.  Note
+       that C++ always has bool.  
+    */
+    #ifndef HAVE_BOOL
+      #define HAVE_BOOL 1
+      typedef int bool;
+    #endif
+    #ifndef true
+      enum boolvalue {false=0, true=1};
+    #endif
+  #endif
+#endif
+
 #ifndef TRUE
-  #define TRUE 1
+  #define TRUE true
   #endif
 #ifndef FALSE
-  #define FALSE 0
-  #endif
-/* C++ has a bool type and false and true constants built in. */
-#ifndef __cplusplus
-  #ifndef HAVE_BOOL
-    #define HAVE_BOOL 1
-    typedef int bool;
-    #endif
-  #ifndef true
-    enum boolvalue {false=0, true=1};
-    #endif
+  #define FALSE false
   #endif
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
diff --git a/lib/util/runlength.c b/lib/util/runlength.c
new file mode 100644
index 00000000..e5c60db0
--- /dev/null
+++ b/lib/util/runlength.c
@@ -0,0 +1,374 @@
+/*=============================================================================
+                                  runlength.c
+===============================================================================
+  "Packbits" run-length encoding and variants.
+
+  Copyright (C) 2015 by Akira Urushibata (afu@wta.att.ne.jp).
+
+  Permission to use, copy, modify, and distribute this software and its
+  documentation for any purpose and without fee is hereby granted, provided
+  that the above copyright notice appear in all copies and that both that
+  copyright notice and this permission notice appear in supporting
+  documentation.  This software is provided "as is" without express or implied
+  warranty.
+
+  Functions pm_rlenc_byte() and pm_rlenc_word() are based on algorithm
+  originally by Robert A. Knop (rknop@mop.caltech.edu).
+
+  Those who wish to incorporate the code herein into their own programs are
+  strongly discouraged from removing the comments within borders consisting
+  of "+" marks.  This is a practical consideration based on code inspection
+  and bug fixes of various programs which use run-length compression.
+
+===============================================================================
+
+  Packbits is a simple yet efficient simple run-length compression method.  It
+  has a provision for uncompressed sequences which allows efficient encoding
+  of noisy input.
+
+  A survey of netpbm source code in 2015 found Packbits encoding in the
+  following Netpbm programs: pbmtoescp2, pbmtomacp, pnmtopalm, pnmtopclxl,
+  pnmtops, ppmtoilbm and ppmtopjxl.
+ 
+  Packbits is an option in the TIFF standard; pamtotiff can generate TIFF
+  images that use Packbits compression, but does so via code in the TIFF
+  library (not part of Netpbm).
+
+  Variants of Packbits are employed in pnmtosgi,  pamtopdbimg,  pbmtoppa
+  and  pbmtogo.
+
+  All the above programs formerly did the same compression through different
+  code.  This redundancy bloated the Netpbm package and made maintenance
+  difficult.  This maintenance difficulty surfaced as an issue when tests with
+  valgrind revealed multiple memory-related problems in the above programs.
+  Indeed, just determining which programs do this compression by this method
+  was not simple because of differences in terminology.  "Packbits" is often
+  called "run length encoding."  In ppmtoilbm the name is "byterun1."
+
+  Today, all Netpbm programs that do Packbits compression use the facilities
+  in this file for it.
+=============================================================================*/
+
+#include <string.h>
+
+#include "pm.h"
+#include "pm_c_util.h"
+#include "runlength.h"
+#include "mallocvar.h"
+
+
+
+static const char * const errorUndefinedMode =
+    "Internal error: compression mode %u not supported";
+
+
+
+/*-----------------------------------------------------------------------------
+   Run length encoding
+
+   In this simple run-length encoding scheme, compressed and uncompressed
+   strings follow a single index or "flag" byte N.  There are several
+   variations, differing in the format of the flag byte, maximum string length
+   and element size (byte or word).
+   
+   In the most widely used version, Packbits, the meaning of the flag byte N
+   is defined as follows:
+
+    0-127 means the next N+1 bytes are uncompressed.
+    129-255 means the next byte is to be repeated 257-N times.
+    128 is not used.
+
+   The name "Packbits" is misleading: it packs bytes, not bits.
+-----------------------------------------------------------------------------*/
+
+
+
+void
+pm_rlenc_compressbyte(const unsigned char * const inbuf,
+                      unsigned char       * const outbuf,
+                      enum pm_RleMode       const mode,
+                      size_t                const inSize,
+                      size_t              * const outputSizeP) {
+/*-----------------------------------------------------------------------------
+  Compress the contents of input buffer 'inbuf' with Packbits encoding into
+  output buffer 'outbuf'.  'inSize' is the number of bytes of data in 'inbuf'.
+  Return as *outputSizeP the number of bytes we put in 'outbuf'.
+
+  'outbuf' should be allocated with pm_rlenc_allocoutbuf().
+
+  Always encode 3-byte repeat sequences.
+
+  Encode 2-byte repeat sequences only when they are at the start of the block.
+  This ensures that the output is never unnecessary bloated.
+
+  Original algorithm by Robert A. Knop (rknop@mop.caltech.edu)
+-----------------------------------------------------------------------------*/
+    unsigned int const maxRun = 128;
+
+    size_t inCurs, outCurs;
+
+    if (mode != PM_RLE_PACKBITS)
+        pm_error(errorUndefinedMode, mode);
+
+    for (inCurs = 0, outCurs = 0; inCurs < inSize; ) {
+        if ((inCurs < inSize - 1) && (inbuf[inCurs] == inbuf[inCurs+1])) {
+            /*Begin replicate run*/
+            size_t const hold = inCurs;
+            size_t count;
+            for (count = 0;
+                 inCurs < inSize &&
+                     inbuf[inCurs] == inbuf[hold] &&
+                     count < maxRun; 
+                 ++inCurs, ++count)
+                ;
+
+            outbuf[outCurs++] = (unsigned char)(257 - count);
+            outbuf[outCurs++] = inbuf[hold];
+        } else {
+            /*Do a non-run*/
+            size_t const hold = outCurs;
+            size_t count;
+            ++outCurs;
+            count = 0;
+            while(((inCurs + 2 >= inSize) && (inCurs < inSize) ) || 
+                  ((inCurs + 2 <  inSize) &&
+                   ((inbuf[inCurs] != inbuf[inCurs+1]  )
+                    || (inbuf[inCurs] != inbuf[inCurs+2]  ) ) )    ) {
+                outbuf[outCurs++] = inbuf[inCurs++];
+                if (++count >= 128)
+                    break;
+            }
+            outbuf[hold] = (unsigned char)(count - 1);
+        }
+    }
+    *outputSizeP = outCurs;
+}
+
+
+
+static void
+setFlagElement(void            * const outP,
+               enum pm_RleMode   const mode,
+               bool              const isRepeatRun,
+               size_t            const count) {
+/*---------------------------------------------------------------------------
+  Write the flag byte or word at specified point in the output buffer.
+-----------------------------------------------------------------------------*/
+    switch (mode) {
+    case PM_RLE_SGI16:
+        * (uint16_t *) outP =  (isRepeatRun ? 0x00 : 0x80 ) | count;
+        break;
+    case PM_RLE_PALM16:
+        * (unsigned char *) outP = isRepeatRun ?
+            (unsigned char)(257 - count) : (unsigned char) (count - 1);
+        break;
+    default:
+        pm_error(errorUndefinedMode, mode);
+    }
+}
+
+
+
+void
+pm_rlenc_compressword(const uint16_t   * const inbuf,
+                      unsigned char *    const outbuf,
+                      enum pm_RleMode    const mode,
+                      size_t             const inSize,
+                      size_t           * const outputSizeP) {
+/*---------------------------------------------------------------------------
+   Similar to pm_rlenc_byte(), but this works with two-byte elements.  The
+   difference between SGI16 and PALM16 is the size of the flag.  SGI16 : 16
+   bits; PALM16 : 8 bits
+
+   'inSize' is a number of words,but *outputSizeP is a number of bytes.
+-----------------------------------------------------------------------------*/
+    size_t inCurs, outCurs;
+    size_t flagSz;
+    unsigned int maxRunSz;
+
+    switch (mode) {
+    case PM_RLE_SGI16:
+        flagSz = 2;
+        maxRunSz = 127;
+        break;
+    case PM_RLE_PALM16:
+        flagSz = 1;
+        maxRunSz = 128;
+        break;
+    default:
+        pm_error(errorUndefinedMode, mode);
+    }
+
+    for (inCurs = 0, outCurs = 0; inCurs < inSize; ) {
+        if ((inCurs < inSize - 1) && (inbuf[inCurs] == inbuf[inCurs+1])) {
+            uint16_t const runValue = inbuf[inCurs];
+            size_t count;
+            /* Do a run of 'runValue' values */
+            for (count = 0;
+                 count < maxRunSz &&
+                     inCurs < inSize &&
+                     inbuf[inCurs] == runValue;
+                 ++inCurs, ++count)
+                ;
+            setFlagElement(&outbuf[outCurs], mode, TRUE, count);
+            outCurs += flagSz;
+            * (uint16_t *) &outbuf[outCurs] = runValue;
+            outCurs += 2;
+        } else {
+            /*Do a non run*/
+            size_t const nonrunStart = inCurs;
+            size_t count;
+            count = 0;
+            while (count < maxRunSz &&
+                   ((inCurs + 2 >= inSize && inCurs < inSize) ||
+                    (inCurs + 2 <  inSize &&
+                     (inbuf[inCurs] != inbuf[inCurs+1]
+                      || inbuf[inCurs] != inbuf[inCurs+2])))) {
+                ++inCurs;
+                ++count;
+            }
+            setFlagElement(&outbuf[outCurs], mode, FALSE, count);
+            outCurs += flagSz;
+            memcpy(&outbuf[outCurs], &inbuf[nonrunStart], count * 2);
+            outCurs += count * 2;
+        }
+    }
+    
+    if (mode == PM_RLE_SGI16) {
+        * (uint16_t *) &outbuf[outCurs] = 0;     /* terminator */
+        outCurs += 2;
+    }
+
+    *outputSizeP = outCurs;
+}
+
+
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+   Worst case output size
+
+   The term "worst-case output size" can mean one of two things depending on
+   whether the encoder is efficient or not.
+
+   Sub-optimal encoder: the output size can be up to twice the input size.
+   This happens (or one may rather say "is achieved by a determined encoder")
+   when input is split into one-byte blocks.
+
+   Efficient encoder: the output is larger than the input by the minimum
+   number of flag bytes.  The worst case happens when there are no repeat
+   sequences in the input.
+
+   The key to an efficient encoder is correct handling of short, inefficient
+   sequences.  The following algorithm (not actually used in this file)
+   describes the idea.
+
+   A run is a maximal set of two or more consecutive identical values in the
+   input.  A nonrun is a maximal set of values in which every value is
+   different from its neighbors.
+
+   A compressed block is one that encodes a sequence of identical values
+   (which could be a run or just part of a run) as a value and a count.
+   count.  An uncompressed block is one that encodes a sequence of any values
+   by listing the values individually.
+
+   Start by encoding the entire input as uncompressed blocks.  Seek runs that
+   can be encoded with compressed blocks, but only if doing so doesn't make
+   the output larger.
+
+   Criteria to avoid bloat:
+
+     - Overhead for a single uncompressed block: 1 byte.
+
+     - Overhead for one uncompressed block and a compressed block: 2 bytes.
+
+     - Overhead for two uncompressed blocks and a compressed block: 3 bytes.
+
+     - New blocks at the edge of any existing block add 1 byte of overhead.
+       New blocks in the middle of existing blocks add 2 bytes of overhead.
+
+     - Whenever savings are larger than the added overhead, encode the run
+       as a compressed block.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+
+
+size_t
+pm_rlenc_maxbytes(size_t          const inSize,  /* number of elements */
+                  enum pm_RleMode const mode) {
+/*---------------------------------------------------------------------------
+   Calculate worst case output size from input size and encoding mode.
+
+   'inSize' counts the number of elements, not bytes: input size is (2 *
+   inSize) bytes if input is an array of 16-bit words.
+
+   Return value is the maximum possible output size in bytes regardless of
+   type of input.
+
+   Abort the program if the maximum possible output size is greater than
+   INT_MAX.
+-----------------------------------------------------------------------------*/
+   /* The upper limit could be raised above INT_MAX, but no program needs that
+      today.
+   */
+    size_t blockSize;
+    size_t flagSize;
+    size_t itemSize;
+    size_t miscSize;
+    size_t overhead;
+
+    switch (mode) {
+    case PM_RLE_PACKBITS:
+        blockSize = 128; flagSize = 1; itemSize = 1; miscSize = 0; break;
+    case PM_RLE_SGI8:
+        blockSize = 127; flagSize = 1; itemSize = 1; miscSize = 0; break;
+    case PM_RLE_GRAPHON:  case PM_RLE_PPA:
+        blockSize =  64; flagSize = 1; itemSize = 1; miscSize = 0; break;
+    case PM_RLE_SGI16:
+        blockSize = 127; flagSize = 2; itemSize = 2; miscSize = 2; break;
+    case PM_RLE_PALM16:
+        blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0; break;
+    default:
+        pm_error(errorUndefinedMode, mode);
+    }
+    
+    overhead = miscSize +
+        (inSize / blockSize + (inSize % blockSize > 0 ? 1 : 0) ) * flagSize;
+
+    if (inSize > INT_MAX / itemSize ||
+        inSize * itemSize > INT_MAX - overhead)
+        pm_error("Cannot do RLE compression.  Input too large.");
+
+    return inSize * itemSize + overhead;
+}
+
+
+
+void
+pm_rlenc_allocoutbuf(unsigned char ** const outbufP,
+                     size_t           const inSize,  /* element count */
+                     enum pm_RleMode  const mode) {
+/*---------------------------------------------------------------------------
+   Allocate an output buffer sufficient for input with inSize elements, using
+   compression mode 'mode'.  Element may be byte or word, whichever 'mode'
+   implies.
+-----------------------------------------------------------------------------*/
+    size_t const size = pm_rlenc_maxbytes(inSize, mode);
+
+    unsigned char * outbuf;
+
+    MALLOCARRAY(outbuf, size);
+    if (outbuf == NULL)
+        pm_error("Out of memory trying to get %u bytes for RLE output buffer",
+                 (unsigned)size);
+
+    *outbufP = outbuf;
+}
+
+
+
+void
+pm_rlenc_freebuf(void * const buf) {
+    free(buf);
+}
+
+
diff --git a/lib/util/runlength.h b/lib/util/runlength.h
new file mode 100644
index 00000000..4857ae61
--- /dev/null
+++ b/lib/util/runlength.h
@@ -0,0 +1,51 @@
+#ifndef RUNLENGTH_INCLUDED
+#define RUNLENGTH_INCLUDED
+
+#include "pm_config.h"
+
+#include <limits.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#if 0
+} /* to fake out automatic code indenters */
+#endif
+
+
+enum pm_RleMode { PM_RLE_PACKBITS,          /* most common mode */
+                  PM_RLE_GRAPHON,           /* reserved */ 
+                  PM_RLE_PPA,               /* reserved */
+                  PM_RLE_SGI8,              /* reserved */
+                  PM_RLE_SGI16,
+                  PM_RLE_PALM16
+                };
+
+size_t
+pm_rlenc_maxbytes (size_t          const inSize,
+                   enum pm_RleMode const mode);
+
+void
+pm_rlenc_allocoutbuf(unsigned char ** const outbufP,
+                     size_t           const inSize,
+                     enum pm_RleMode  const mode);
+
+
+void
+pm_rlenc_freebuf(void * const buf);
+
+void
+pm_rlenc_compressbyte(const unsigned char * const inbuf,
+                      unsigned char       * const outbuf,
+                      enum pm_RleMode       const mode,
+                      size_t                const inSize,
+                      size_t              * const outputSizeP);
+
+void
+pm_rlenc_compressword(const uint16_t      * const inbuf,
+                      unsigned char       * const outbuf,
+                      enum pm_RleMode       const mode,
+                      size_t                const itemCnt,
+                      size_t              * const outputSizeP);
+
+#endif
diff --git a/lib/util/shhopt.c b/lib/util/shhopt.c
index 718186fa..ccf2d1eb 100644
--- a/lib/util/shhopt.c
+++ b/lib/util/shhopt.c
@@ -31,6 +31,7 @@
 
 #include "mallocvar.h"
 #include "nstring.h"
+#include "token.h"
 #include "shhopt.h"
 
 /*-----------------------------------------------------------------------+
@@ -76,7 +77,7 @@ optFatalFunc(const char *format, ...)
  |
  |  RETURNS       Number of options in the given array.
  |
- |  DESCRIPTION   Count elements in an optStruct-array. The strcture must
+ |  DESCRIPTION   Count elements in an optStruct-array. The structure must
  |                be ended using an element of type OPT_END.
  */
 static int
@@ -237,6 +238,7 @@ optNeedsArgument(const optEntry opt)
 	|| opt.type == OPT_ULONG
     || opt.type == OPT_FLOAT
     || opt.type == OPT_NAMELIST
+    || opt.type == OPT_STRINGLIST
         ;
 }
 
@@ -281,46 +283,12 @@ getToken(const char *  const tokenStart,
    we return an empty string and *nextP == tokenStart, i.e. *nextP
    doesn't necessarily advance.
 -----------------------------------------------------------------------------*/
-    char * token;
-    const char * cursor;
-    unsigned int charCount;
-
-    /* Run through the token, counting characters */
-
-    charCount = 0;
-    cursor = tokenStart;
-
-    while (*cursor != delimiter && *cursor != '\0') {
-        if (*cursor == '\\') {
-            ++cursor;
-            if (*cursor == '\0')
-                optFatal("string ends with an escape character (\\)");
-        }
-        ++cursor;
-        ++charCount;
-    }
+    const char * error;
     
-    token = malloc(charCount + 1);
-    if (token == NULL)
-        optFatal("Could not allocate %u bytes of memory to parse a string",
-                 charCount + 1);
-
-    /* Go back and do it again, this time copying the characters */
-    charCount = 0;
-    cursor = tokenStart;
-
-    while (*cursor != delimiter && *cursor != '\0') {
-        if (*cursor == '\\')
-            ++cursor;
-
-        assert(*cursor != '\0');
+    pm_gettoken(tokenStart, delimiter, tokenP, nextP, &error);
 
-        token[charCount++] = *cursor++;
-    }
-    token[charCount] = '\0';
-
-    *tokenP = token;
-    *nextP = cursor;
+    if (error)
+        optFatal("error parsing a token: %s", error);
 }
 
 
@@ -375,6 +343,41 @@ parseNameList(const char *           const listText,
 
 
 
+static void
+parseStringList(const char *   const listText,
+                const char *** const listP) {
+
+    unsigned int const maxStringCount = 100;
+
+    const char * cursor;
+    unsigned int stringCount;
+    const char ** list;
+
+    MALLOCARRAY_NOFAIL(list, maxStringCount+1);
+
+    cursor = &listText[0];  /* initial value */
+
+    stringCount = 0;  /* initial value */
+
+    while (stringCount < maxStringCount && *cursor != '\0') {
+        const char * next;
+
+        getToken(cursor, ',', &list[stringCount++], &next);
+
+        cursor = next;
+
+        if (*cursor != '\0') {
+            assert(*cursor == ',');
+            ++cursor;
+        }
+    }
+    list[stringCount] = NULL;
+
+    *listP = list;
+}
+
+
+
 /*------------------------------------------------------------------------
  |  NAME          optExecute
  |
@@ -479,6 +482,15 @@ optExecute(optEntry  const opt, char *arg, int lng)
             parseNameList(arg, (struct optNameValue **)opt.arg);
 
     } break;
+    case OPT_STRINGLIST: {
+        if (arg == NULL)
+            optFatal("internal error: optExecute() called with NULL argument "
+                     "'%s'", optString(opt, lng));
+
+        if (opt.arg)
+            parseStringList(arg, (const char ***)opt.arg);
+
+    } break;
     default:
         break;
     }
@@ -502,19 +514,20 @@ optExecute(optEntry  const opt, char *arg, int lng)
  |                        that _must_ abort the program.
  */
 void
-optSetFatalFunc(void (*f)(const char *, ...))
-{
+pm_optSetFatalFunc(void (*f)(const char *, ...)) {
+
     optFatal = f;
 }
 
 
+
 /*------------------------------------------------------------------------
- |  NAME          optParseOptions
+ |  NAME          pm_optParseOptions
  |
  |  FUNCTION      Parse commandline options.
  |
  |  SYNOPSIS      #include "shhopt.h"
- |                void optParseOptions(int *argc, char *argv[],
+ |                void pm_optParseOptions(int *argc, char *argv[],
  |                                     optStruct opt[], int allowNegNum);
  |
  |  INPUT         argc    Pointer to number of options.
@@ -541,7 +554,7 @@ optSetFatalFunc(void (*f)(const char *, ...))
  |                Any error leads to program abortion.
  */
 void
-optParseOptions(int *argc, char *argv[], optStruct opt[], int allowNegNum)
+pm_optParseOptions(int *argc, char *argv[], optStruct opt[], int allowNegNum)
 {
     int  ai,        /* argv index. */
          optarg,    /* argv index of option argument, or -1 if none. */
@@ -724,13 +737,13 @@ fatalUnrecognizedLongOption(const char * const optionName,
         const char * entry;
 
         if (optEntryP->longName)
-            asprintfN(&entry, "-%s ", optEntryP->longName);
+            pm_asprintf(&entry, "-%s ", optEntryP->longName);
         else
-            asprintfN(&entry, "-%c ", optEntryP->shortName);
+            pm_asprintf(&entry, "-%c ", optEntryP->shortName);
 
         strncat(optList, entry, sizeof(optList) - strlen(optList) - 1);
 
-        strfree(entry);
+        pm_strfree(entry);
 
         if (strlen(optList) + 1 == sizeof(optList)) {
             /* Buffer is full.  Overwrite end of list with ellipsis */
@@ -809,12 +822,12 @@ parse_long_option(char *   const argv[],
 
 
 /*------------------------------------------------------------------------
- |  NAME          optParseOptions2
+ |  NAME          pm_optParseOptions2
  |
  |  FUNCTION      Parse commandline options.
  |
  |  SYNOPSIS      #include "shhopt.h"
- |                void optParseOptions2(int *argc, char *argv[],
+ |                void pm_optParseOptions2(int *argc, char *argv[],
  |                                      optStruct2 opt, unsigned long flags);
  |
  |  INPUT         argc    Pointer to number of options.
@@ -834,9 +847,9 @@ parse_long_option(char *   const argv[],
  |                extracted and stored in the variables or passed to
  |                functions pointed to by entries in opt.
  |
- |                This differs from optParseOptions in that it accepts
+ |                This differs from pm_optParseOptions in that it accepts
  |                long options with just one hyphen and doesn't accept
- |                any short options.  It also has accomodations for 
+ |                any short options.  It also has accommodations for 
  |                future expansion.
  |
  |                Options and arguments used are removed from the argv-
@@ -845,10 +858,10 @@ parse_long_option(char *   const argv[],
  |                Any error leads to program abortion.
  */
 void
-optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt, 
+pm_optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt, 
                  const unsigned long flags)
 /*----------------------------------------------------------------------------
-   This does the same thing as optParseOptions3(), except that there is no
+   This does the same thing as pm_optParseOptions3(), except that there is no
    "specified" return value.  
 
    This function exists for backward compatibility.
@@ -865,7 +878,7 @@ optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt,
         optFatal("Memory allocation failed (trying to allocate space for "
                  "new-format option table)");
     
-    optParseOptions3(argc_p, argv, opt3, sizeof(opt3), flags);
+    pm_optParseOptions3(argc_p, argv, opt3, sizeof(opt3), flags);
 
     free(opt3.opt_table);
 }
@@ -890,7 +903,7 @@ zero_specified(optEntry opt_table[]) {
 
 
 /*------------------------------------------------------------------------
- |  NAME          optParseOptions3
+ |  NAME          pm_optParseOptions3
  |
  |  FUNCTION      Parse commandline options.
  |
@@ -919,9 +932,9 @@ zero_specified(optEntry opt_table[]) {
  |                extracted and stored in the variables or passed to
  |                functions pointed to by entries in opt.
  |
- |                This differs from optParseOptions in that it accepts
+ |                This differs from pm_optParseOptions in that it accepts
  |                long options with just one hyphen and doesn't accept
- |                any short options.  It also has accomodations for 
+ |                any short options.  It also has accommodations for 
  |                future expansion.
  |
  |                Options and arguments used are removed from the argv-
@@ -930,7 +943,7 @@ zero_specified(optEntry opt_table[]) {
  |                Any error leads to program abortion.
  */
 void
-optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt, 
+pm_optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt, 
                  const unsigned int optStructSize, const unsigned long flags)
 {
     int  ai;        /* argv index. */
@@ -996,13 +1009,13 @@ optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt,
 
 
 void
-optDestroyNameValueList(struct optNameValue * const list) {
+pm_optDestroyNameValueList(struct optNameValue * const list) {
 
     unsigned int i;
 
     for (i = 0; list[i].name; ++i) {
-        strfree(list[i].name);
-        strfree(list[i].value);
+        pm_strfree(list[i].name);
+        pm_strfree(list[i].value);
     }
 
     free(list);
diff --git a/lib/util/shhopt.h b/lib/util/shhopt.h
index 99096a76..9a446290 100644
--- a/lib/util/shhopt.h
+++ b/lib/util/shhopt.h
@@ -1,4 +1,5 @@
-/*=============================================================================
+#if 0
+=============================================================================
 HERE IS AN EXAMPLE OF THE USE OF SHHOPT:
 
 
@@ -6,8 +7,9 @@ HERE IS AN EXAMPLE OF THE USE OF SHHOPT:
 int 
 main ( int argc, char **argv ) {
 
-    // initial values here are just to demonstrate what gets set and
-    // what doesn't by the code below.
+    /* initial values here are just to demonstrate what gets set and
+       what doesn't by the code below.
+    */
     int help_flag = 7;
     unsigned int help_spec =7;
     unsigned int height_spec =7;
@@ -20,7 +22,8 @@ main ( int argc, char **argv ) {
     
     optStruct3 opt;
     unsigned int option_def_index = 0;
-    optEntry *option_def = malloc(100*sizeof(option_def[0]));
+    optEntry * option_def;
+    MALLOCARRAY(option_def, 100);
 
     OPTENT3(0,   "help",     OPT_FLAG,     &help_flag,    &help_spec,   0);
     OPTENT3(0,   "height",   OPT_INT,      &height,       &height_spec, 0);
@@ -34,7 +37,7 @@ main ( int argc, char **argv ) {
     opt.allowNegNum = 1;
 
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
     
 
     printf("argc=%d\n", argc);
@@ -67,8 +70,15 @@ Now run this program with something like
   myprog -v /etc/passwd -name=Bryan --hei=4
 
 
-========================================================================*/
+  If your program takes no options (so you have no OPTENT3 macro invocations),
+  you need an OPTENTINIT call to establish the empty option table:
+
+    optEntry * option_def;
+    MALLOCARRAY(option_def, 1);
+    OPTENTINIT;
 
+========================================================================
+#endif /* 0 */
 
 #ifndef SHHOPT_H
 #define SHHOPT_H
@@ -90,13 +100,14 @@ typedef enum {
     OPT_LONG,              /* signed long integer argument. */
     OPT_ULONG,             /* unsigned long integer argument. */
     OPT_FLOAT,             /* floating point argument. */
+    OPT_STRINGLIST,        /* list like "arg1,arg2.arg3" */
     OPT_NAMELIST           /* list like "key1=val1,key2=val2" */
 } optArgType;
 
 
 typedef struct {
     /* This structure describes a single program option in a form for
-     use by the optParseOptions() or optParseOptions2() function.
+     use by the pm_optParseOptions() or pm_optParseOptions2() function.
     */
     char       shortName;  /* short option name. */
     const char *longName;  /* long option name, not including '--'. */
@@ -108,7 +119,7 @@ typedef struct {
     
 typedef struct {
     /* This structure describes a single program option in a form for
-     use by the optParseOptions3() function.
+     use by the pm_optParseOptions3() function.
     */
     char       shortName;  /* short option name. */
     const char *longName;  /* long option name, not including '--' or '-' */
@@ -129,7 +140,7 @@ typedef struct {
 
 typedef struct {
     /* This structure describes the options of a program in a form for
-       use with the optParseOptions2() function.
+       use with the pm_optParseOptions2() function.
        */
     unsigned char short_allowed;  /* boolean */
         /* The syntax may include short (i.e. one-character) options.
@@ -146,7 +157,7 @@ typedef struct {
 } optStruct2;
 
 typedef struct {
-    /* Same as optStruct2, but for optParseOptions3() */
+    /* Same as optStruct2, but for pm_optParseOptions3() */
     unsigned char short_allowed;  
     unsigned char allowNegNum;    
     optEntry *opt_table;
@@ -187,9 +198,10 @@ typedef struct {
     }
 
 /* OPTENT3 is the same as OPTENTRY except that it also sets the "specified"
-   element of the table entry (so it assumes OPTION_DEF is a table of
-   optEntry instead of optStruct).  It sets it to the number of times that
-   the option appears in the command line.
+   element of the table entry (so it assumes OPTION_DEF is a table of optEntry
+   instead of optStruct).  This is a pointer to a variable that the parser
+   will set to the number of times that the option appears in the command
+   line.
 
    Here is an example:
 
@@ -215,18 +227,21 @@ struct optNameValue {
 
 
         
-void optSetFatalFunc(void (*f)(const char *, ...));
-void optParseOptions(int *argc, char *argv[],
-		     optStruct opt[], int allowNegNum);
 void
-optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt, 
+pm_optSetFatalFunc(void (*f)(const char *, ...));
+
+void
+pm_optParseOptions(int *argc, char *argv[],
+                   optStruct opt[], int allowNegNum);
+void
+pm_optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt, 
                  const unsigned long flags);
 void
-optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt, 
+pm_optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt, 
                  const unsigned int optStructSize, const unsigned long flags);
 
 void
-optDestroyNameValueList(struct optNameValue * const list);
+pm_optDestroyNameValueList(struct optNameValue * const list);
 
 #ifdef __cplusplus
 }
diff --git a/lib/util/token.c b/lib/util/token.c
new file mode 100644
index 00000000..a68a4821
--- /dev/null
+++ b/lib/util/token.c
@@ -0,0 +1,80 @@
+#include <stdlib.h>
+#include <assert.h>
+
+#include "nstring.h"
+
+#include "token.h"
+
+
+void
+pm_gettoken(const char *  const tokenStart,
+            char          const delimiter,
+            const char ** const tokenP,
+            const char ** const nextP,
+            const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Find the token starting at 'tokenStart' up to but not including
+   the first 'delimiter' character or end of string.  Return it in newly
+   malloced memory as *tokenP, NUL-terminated.
+
+   Make *nextP point just past the token, i.e. to the delimiter or
+   end of string NUL character.
+
+   Note that if the string is empty, or starts with the delimiter,
+   we return an empty string and *nextP == tokenStart, i.e. *nextP
+   doesn't necessarily advance.
+-----------------------------------------------------------------------------*/
+    char * token;
+    const char * cursor;
+    unsigned int charCount;
+
+    /* Run through the token, counting characters */
+
+    charCount = 0;        /* initial value */
+    cursor = tokenStart;  /* initial value */
+    *errorP = NULL;       /* initial value */
+
+    while (*cursor != delimiter && *cursor != '\0' && !*errorP) {
+        if (*cursor == '\\') {
+            ++cursor;
+            if (*cursor == '\0')
+                pm_asprintf(errorP,
+                            "string ends with an escape character (\\)");
+        } else {
+            ++cursor;
+            ++charCount;
+        }
+    }
+    if (!*errorP) {
+        token = malloc(charCount + 1);
+        if (token == NULL)
+            pm_asprintf(errorP, "Could not allocate %u bytes of memory "
+                        "to parse a string",
+                        charCount + 1);
+        else {
+            /* Go back and do it again, this time copying the characters */
+            charCount = 0;
+            cursor = tokenStart;
+
+            while (*cursor != delimiter && *cursor != '\0') {
+                if (*cursor == '\\')
+                    ++cursor;
+
+                assert(*cursor != '\0');
+
+                token[charCount++] = *cursor++;
+            }
+            token[charCount] = '\0';
+            
+            *tokenP = token;
+            *nextP = cursor;
+        }
+    }
+}
+
+
+
+
+
+
+
diff --git a/lib/util/token.h b/lib/util/token.h
new file mode 100644
index 00000000..292a9164
--- /dev/null
+++ b/lib/util/token.h
@@ -0,0 +1,11 @@
+#ifndef TOKEN_H_INCLUDE
+#define TOKEN_H_INCLUDE
+
+void
+pm_gettoken(const char *  const tokenStart,
+            char          const delimiter,
+            const char ** const tokenP,
+            const char ** const nextP,
+            const char ** const errorP);
+
+#endif
diff --git a/lib/util/vasprintf.c b/lib/util/vasprintf.c
index 47b4079d..a947f763 100644
--- a/lib/util/vasprintf.c
+++ b/lib/util/vasprintf.c
@@ -1,19 +1,21 @@
 #define _GNU_SOURCE
-   /* Due to conditional compilation, this is GNU source only if the C library
-      is GNU.
+   /* Because of conditional compilation, this is GNU source only if the C
+      library is GNU.
    */
 #include <stdlib.h>
 #include <string.h>
 
 #include "pm_config.h"
+#include "pm_c_util.h"
+
 #include "nstring.h"
 
 
 
 void
-vasprintfN(const char ** const resultP,
-           const char *  const format,
-           va_list             varargs) {
+pm_vasprintf(const char ** const resultP,
+             const char *  const format,
+             va_list             varargs) {
 
     char * result;
 
@@ -23,7 +25,7 @@ vasprintfN(const char ** const resultP,
     rc = vasprintf(&result, format, varargs);
 
     if (rc < 0)
-        *resultP = strsol;
+        *resultP = pm_strsol;
     else
         *resultP = result;
 #else
@@ -38,16 +40,22 @@ vasprintfN(const char ** const resultP,
 
        So instead, we just allocate 4K and truncate or waste as
        necessary.
+
+       Note that we don't recognize the floating point specifiers (%f, %e, %g)
+       - we render them as 'f', 'e', and 'g'.  It would be too much work to
+       make this code handle those, just for the few systems on which it runs.
+       Instead, we have pm_vasprintf_knows_float(), and any caller that cares
+       enough can avoid using these specifiers where they don't work.
     */
     size_t const allocSize = 4096;
     result = malloc(allocSize);
     
     if (result == NULL)
-        *resultP = strsol;
+        *resultP = pm_strsol;
     else {
         size_t realLen;
 
-        vsnprintfN(result, allocSize, format, varargs, &realLen);
+        pm_vsnprintf(result, allocSize, format, varargs, &realLen);
         
         if (realLen >= allocSize)
             strcpy(result + allocSize - 15, "<<<TRUNCATED");
@@ -56,3 +64,14 @@ vasprintfN(const char ** const resultP,
     }
 #endif
 }
+
+
+
+bool
+pm_vasprintf_knows_float(void) {
+#if HAVE_VASPRINTF
+    return true;
+#else
+    return false;
+#endif
+}
diff --git a/lib/util/wordaccess.h b/lib/util/wordaccess.h
index 2eaa2b24..df0eaf12 100644
--- a/lib/util/wordaccess.h
+++ b/lib/util/wordaccess.h
@@ -44,33 +44,32 @@
 
 #include "pm_config.h"
 
-#if (!defined(WORDACCESS_GENERIC) && HAVE_GCC_BITCOUNT )
-
-    #if BYTE_ORDER == BIG_ENDIAN    /* See pm_config.h */
-        /* Sun Sparc 64, etc */ 
-        #include "wordaccess_gcc3_be.h"
-
-    #elif (BITS_PER_LONG == 64)
-        /* AMD Athlon 64, Intel x86_64, Intel Itanium, etc. */
-        #include "wordaccess_64_le.h"
-
-    #elif (BITS_PER_LONG == 32)
-        /* Intel x86_32 (80386, 80486, Pentium), etc. */
-        #include "wordaccess_generic.h"
-
-    #else
-        /* Extremely rare case.
-           If long is neither 32 nor 64 bits, (say, 128) it comes here.
-        */
-        #define WORDACCESS_GENERIC
-        #include "wordaccess_generic.h"
-
-    #endif
-
+#if defined(WORDACCESS_GENERIC)
+  /* User wants this, regardless of whether machine can do better */
+  #include "wordaccess_generic.h"
+#elif BYTE_ORDER == BIG_ENDIAN
+  #if UNALIGNED_OK
+     #include "wordaccess_be_unaligned.h"
+  #else
+    /* Sparc */
+    #include "wordaccess_be_aligned.h"
+  #endif
+#elif HAVE_GCC_BITCOUNT
+  #if (BITS_PER_LONG == 64)
+    /* AMD Athlon 64, Intel x86_64, Intel Itanium, etc. */
+    #include "wordaccess_64_le.h"
+  #elif (BITS_PER_LONG == 32)
+    /* Intel x86_32 (80386, 80486, Pentium), etc. */
+      #include "wordaccess_generic.h"
+  #else
+     /* Extremely rare case.
+        If long is neither 32 nor 64 bits, (say, 128) it comes here.
+     */
+     #include "wordaccess_generic.h"
+  #endif
 #else
-    /* Non GCC, GCC prior to v.3.4 or WORDACCESS_GENERIC defined  */
-    #include "wordaccess_generic.h"
-
+  /* Non GCC or GCC prior to v.3.4; little-endian  */
+  #include "wordaccess_generic.h"
 #endif
 
 #endif
diff --git a/lib/util/wordaccess_be_aligned.h b/lib/util/wordaccess_be_aligned.h
new file mode 100644
index 00000000..f3bbb841
--- /dev/null
+++ b/lib/util/wordaccess_be_aligned.h
@@ -0,0 +1,35 @@
+/*=============================================================================
+  This file is the part of wordaccess.h for use under with big-endian
+  machines that require word accesses to be word-aligned.
+*===========================================================================*/
+
+typedef unsigned long int wordint;
+typedef unsigned char wordintBytes[sizeof(wordint)];
+
+static __inline__ wordint
+bytesToWordint(wordintBytes bytes) {
+    uint16_t const hi = *((uint16_t *) (bytes + 0));
+    uint16_t const mh = *((uint16_t *) (bytes + 2));
+    uint16_t const ml = *((uint16_t *) (bytes + 4));
+    uint16_t const lo = *((uint16_t *) (bytes + 6));
+    return
+        (((wordint) hi) << 48) |
+        (((wordint) mh) << 32) |
+        (((wordint) ml) << 24) |
+        (((wordint) lo) <<  0);
+}
+
+
+
+static __inline__ void
+wordintToBytes(wordintBytes * const bytesP,
+               wordint        const wordInt) {
+    uint16_t const hi = ((wordInt >> 48) & 0xFF);
+    uint16_t const mh = ((wordInt >> 32) & 0xFF);
+    uint16_t const ml = ((wordInt >> 24) & 0xFF);
+    uint16_t const lo = ((wordInt >>  0) & 0xFF);
+    *(uint16_t *)(bytesP + 0) = hi;
+    *(uint16_t *)(bytesP + 2) = mh;
+    *(uint16_t *)(bytesP + 4) = ml;
+    *(uint16_t *)(bytesP + 6) = lo;
+}
diff --git a/lib/util/wordaccess_gcc3_be.h b/lib/util/wordaccess_be_unaligned.h
index 5aa63521..95b68ac7 100644
--- a/lib/util/wordaccess_gcc3_be.h
+++ b/lib/util/wordaccess_be_unaligned.h
@@ -1,9 +1,6 @@
 /*=============================================================================
-  This file is the part of wordaccess.h for use under these
-  conditions:
-
-  * GCC (>=3.4), GLIBC
-  * Big-Endian machines
+  This file is the part of wordaccess.h for use on a big-endian machine
+  that does not require word accesses to be word-aligned.
 *===========================================================================*/
 
 typedef unsigned long int wordint;
diff --git a/lib/util/wordaccess_generic.h b/lib/util/wordaccess_generic.h
index 94cc8124..6e0a20ef 100644
--- a/lib/util/wordaccess_generic.h
+++ b/lib/util/wordaccess_generic.h
@@ -1,11 +1,11 @@
 /*=============================================================================
 
-  This file is the part of wordaccess.h for use under these
+  This file is the part of wordaccess.h for use under any of these
   conditions:
 
-  * Compilers other than GCC
+  * Compiler other than GCC
   * GCC before version 3.4
-  * Specified by the user with WORDACCESS_GENERIC
+  * Requested by the user with WORDACCESS_GENERIC
 =============================================================================*/
 
 #include "intcode.h"
diff --git a/netpbm.c b/netpbm.c
index 5b722158..eeb82acc 100644
--- a/netpbm.c
+++ b/netpbm.c
@@ -29,13 +29,17 @@
 int
 main(int argc, char *argv[]) {
 
-    char* cp;
+    const char * cp;
     
     if (strcmp(pm_arg0toprogname(argv[0]), "netpbm") == 0) {
         ++argv;
         --argc;
         if (argc < 1 || !*argv)	{
-            fprintf(stderr, "Usage: netpbm netpbm_program_name [args ...]\n");
+            fprintf(stderr,
+                    "When you invoke this program by the name 'netpbm', "
+                    "You must supply at least one argument: the name of "
+                    "the Netpbm program to run, e.g. "
+                    "'netpbm pamfile /tmp/myfile.ppm'\n");
             exit(1);
 		}
 	}
@@ -52,9 +56,30 @@ main(int argc, char *argv[]) {
 #include "mergetrylist"
 
     /* Add the obsolete names for some programs */
+    TRY("bmptoppm", main_bmptopnm);
     TRY("gemtopbm", main_gemtopnm);
-    TRY("pnminterp", main_pamstretch);
+    TRY("icontopbm", main_sunicontopnm);
+    TRY("pbmtoicon", main_pbmtosunicon);
+    TRY("pgmedge", main_pamedge);
+    TRY("pgmnorm", main_pnmnorm);
     TRY("pgmoil", main_pamoil);
+    TRY("pgmslice", main_pamslice);
+    TRY("pnmarith", main_pamarith);
+    TRY("pngtopnm", main_pngtopam);
+    TRY("pnmarith", main_pamarith);
+    TRY("pnmcomp", main_pamcomp);
+    TRY("pnmcut", main_pamcut);
+    TRY("pnmdepth", main_pamdepth);
+    TRY("pnmfile", main_pamfile);
+    TRY("pnminterp", main_pamstretch);
+    TRY("pnmenlarge", main_pamenlarge);
+    TRY("pnmscale", main_pamscale);
+    TRY("pnmsplit", main_pamsplit);
+    TRY("pnmtofits", main_pamtofits);
+    TRY("pnmtopnm", main_pamtopnm);
+    TRY("ppmnorm", main_pnmnorm);
+    TRY("ppmtotga", main_pamtotga);
+    TRY("ppmtouil", main_pamtouil);
     TRY("pnmtopnm", main_pamtopnm);
     TRY("ppmnorm", main_pnmnorm);
     TRY("ppmtotga", main_pamtotga);
@@ -63,18 +88,13 @@ main(int argc, char *argv[]) {
        library, there is no main_pnmtojpeg library.  The right way to do
        this is to have these TRY's generated by the subdirectory makes,
        which would know whether pnmtojpeg was built into the merged binary
-       or not.  But that's too much work.
+       or not.  But that's too much work.  Same with TIFF and PNG converters.
 
     TRY("ppmtojpeg", main_pnmtojpeg); 
+    TRY("pngtopnm", main_pngtopam); 
+    TRY("pnmtotiff", main_pamtotiff);
     TRY("pamrgbatopng", main_pamtopng);
     */
-    TRY("bmptoppm", main_bmptopnm);
-    TRY("pgmnorm", main_pnmnorm);
-    TRY("ppmnorm", main_pnmnorm);
-    TRY("ppmtotga", main_pamtotga);
-    TRY("pnmarith", main_pamarith);
-    TRY("pnmfile", main_pamfile);
-    TRY("pgmedge", main_pamedge);
 
     fprintf(stderr,"'%s' is an unknown Netpbm program name \n", cp );
     exit(1);
diff --git a/other/Makefile b/other/Makefile
index f7333c64..bd2c9dc2 100644
--- a/other/Makefile
+++ b/other/Makefile
@@ -24,17 +24,17 @@ endif
 # build.
 
 PORTBINARIES = pamarith pambayer pamchannel pamdepth \
-	pamendian pamfixtrunc pamlookup pampick pamsplit \
-	pamstack pamsummcol pnmcolormap \
+	pamendian pamexec pamfix pamlookup pampick pamsplit \
+	pamstack pamsummcol pamunlookup pamvalidate pnmcolormap \
 	ppmdcfont ppmddumpfont ppmdmkfont 
 
-BINARIES = $(PORTBINARIES)
-
 ifneq ($(LINUXSVGALIB),NONE)
-  BINARIES += ppmsvgalib
+  PORTBINARIES += ppmsvgalib
 endif
 
-SCRIPTS = ppmtomap
+BINARIES = $(PORTBINARIES)
+
+SCRIPTS = ppmtomap pamfixtrunc
 
 OBJECTS = $(BINARIES:%=%.o)
 
@@ -50,10 +50,7 @@ all: $(BINARIES) $(SUBDIRS:%=%/all)
 
 include $(SRCDIR)/common.mk
 
-ppmsvgalib: %: %.o $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $< \
-	  $(shell $(LIBOPT) $(NETPBMLIB) $(LINUXSVGALIB)) \
-	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(LADD) 
+ppmsvgalib: LDFLAGS_TARGET = $(shell $(LIBOPT) $(LINUXSVGALIB))
 
 install.bin: install.bin.local
 .PHONY: install.bin.local
diff --git a/other/pamarith.c b/other/pamarith.c
index 3d29ac93..54c18485 100644
--- a/other/pamarith.c
+++ b/other/pamarith.c
@@ -47,7 +47,7 @@ isDyadic(enum function const function) {
 
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -60,7 +60,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, const char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
@@ -102,7 +102,7 @@ parseCommandLine(int argc, const char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (addSpec + subtractSpec + multiplySpec + divideSpec + differenceSpec +
@@ -400,48 +400,66 @@ doNormalizedArith(struct pam *  const inpam1P,
                   struct pam *  const outpamP,
                   enum function const function) {
 
+    /* Some of the logic in this subroutine is designed for future
+       expansion into non-dyadic computations.  But for now, all
+       computations have exactly two operands.
+    */
     unsigned int const operandCt = 2;
 
-    tuplen * tuplerown1;
-    tuplen * tuplerown2;
+    tuplen ** tuplerown;
+        /* tuplerown[0] is the current row in the first operand image */
+
     tuplen * tuplerownOut;
     unsigned int row;
     samplen * operands;
+        /* operand[0] is the first operand in the current one-sample
+           computation
+        */
+    unsigned int * plane;
+        /* plane[0] is the plane number in the first operand image for 
+           the current one-sample computation.  plane[1] is the plane number
+           in the second operand image, etc.
+         */
 
     MALLOCARRAY_NOFAIL(operands, operandCt);
+    MALLOCARRAY_NOFAIL(plane, operandCt);
+    MALLOCARRAY_NOFAIL(tuplerown, operandCt);
 
-    tuplerown1   = pnm_allocpamrown(inpam1P);
-    tuplerown2   = pnm_allocpamrown(inpam2P);
+    tuplerown[0] = pnm_allocpamrown(inpam1P);
+    tuplerown[1] = pnm_allocpamrown(inpam2P);
     tuplerownOut = pnm_allocpamrown(outpamP);
 
     for (row = 0; row < outpamP->height; ++row) {
         unsigned int col;
-        pnm_readpamrown(inpam1P, tuplerown1);
-        pnm_readpamrown(inpam2P, tuplerown2);
+        pnm_readpamrown(inpam1P, tuplerown[0]);
+        pnm_readpamrown(inpam2P, tuplerown[1]);
         
         for (col = 0; col < outpamP->width; ++col) {
             unsigned int outplane;
             
             for (outplane = 0; outplane < outpamP->depth; ++outplane) {
-                unsigned int const plane1 = MIN(outplane, inpam1P->depth-1);
-                unsigned int const plane2 = MIN(outplane, inpam2P->depth-1);
+                unsigned int op;
 
-                operands[0] = tuplerown1[col][plane1];
-                operands[1] = tuplerown2[col][plane2];
+                plane[0] = MIN(outplane, inpam1P->depth-1);
+                plane[1] = MIN(outplane, inpam2P->depth-1);
+
+                for (op = 0; op < operandCt; ++op)
+                    operands[op] = tuplerown[op][col][plane[op]];
 
                 tuplerownOut[col][outplane] = 
                     applyNormalizedFunction(function, operands, operandCt); 
                 assert(tuplerownOut[col][outplane] >= 0.);
                 assert(tuplerownOut[col][outplane] <= 1.);
-
             }
         }
         pnm_writepamrown(outpamP, tuplerownOut);
     }
 
-    pnm_freepamrown(tuplerown1);
-    pnm_freepamrown(tuplerown2);
+    pnm_freepamrown(tuplerown[0]);
+    pnm_freepamrown(tuplerown[1]);
+    free(tuplerown);
     pnm_freepamrown(tuplerownOut);
+    free(plane);
     free(operands);
 }
 
@@ -701,14 +719,28 @@ doUnNormalizedArith(struct pam *  const inpam1P,
    maxval to do the computation without time-consuming normalization of
    sample values.
 -----------------------------------------------------------------------------*/
+    /* Some of the logic in this subroutine is designed for future
+       expansion into non-dyadic computations.  But for now, all
+       computations have exactly two operands.
+    */
     unsigned int const operandCt = 2;
+
     sample const maxval = outpamP->maxval;
 
-    tuple * tuplerow1;
-    tuple * tuplerow2;
+    tuple ** tuplerow;
+        /* tuplerow[0] is the current row in the first operand image */
+
     tuple * tuplerowOut;
     unsigned int row;
     sample * operands;
+        /* operand[0] is the first operand in the current one-sample
+           computation
+        */
+    unsigned int * plane;
+        /* plane[0] is the plane number in the first operand image for 
+           the current one-sample computation.  plane[1] is the plane number
+           in the second operand image, etc.
+         */
 
     /* Input conditions: */
     assert(inpam1P->maxval == maxval);
@@ -716,25 +748,29 @@ doUnNormalizedArith(struct pam *  const inpam1P,
     assert(outpamP->maxval == maxval);
 
     MALLOCARRAY_NOFAIL(operands, operandCt);
+    MALLOCARRAY_NOFAIL(plane, operandCt);
+    MALLOCARRAY_NOFAIL(tuplerow, operandCt);
 
-    tuplerow1   = pnm_allocpamrow(inpam1P);
-    tuplerow2   = pnm_allocpamrow(inpam2P);
+    tuplerow[0]   = pnm_allocpamrow(inpam1P);
+    tuplerow[1]   = pnm_allocpamrow(inpam2P);
     tuplerowOut = pnm_allocpamrow(outpamP);
 
     for (row = 0; row < outpamP->height; ++row) {
         unsigned int col;
-        pnm_readpamrow(inpam1P, tuplerow1);
-        pnm_readpamrow(inpam2P, tuplerow2);
+        pnm_readpamrow(inpam1P, tuplerow[0]);
+        pnm_readpamrow(inpam2P, tuplerow[1]);
         
         for (col = 0; col < outpamP->width; ++col) {
             unsigned int outplane;
             
             for (outplane = 0; outplane < outpamP->depth; ++outplane) {
-                unsigned int const plane1 = MIN(outplane, inpam1P->depth-1);
-                unsigned int const plane2 = MIN(outplane, inpam2P->depth-1);
+                unsigned int op;
+
+                plane[0] = MIN(outplane, inpam1P->depth-1);
+                plane[1] = MIN(outplane, inpam2P->depth-1);
 
-                operands[0] = tuplerow1[col][plane1];
-                operands[1] = tuplerow2[col][plane2];
+                for (op = 0; op < operandCt; ++op)
+                    operands[op] = tuplerow[op][col][plane[op]];
 
                 tuplerowOut[col][outplane] = 
                     applyUnNormalizedFunction(function, operands, operandCt,
@@ -747,10 +783,11 @@ doUnNormalizedArith(struct pam *  const inpam1P,
         pnm_writepamrow(outpamP, tuplerowOut);
     }
 
-    pnm_freepamrow(tuplerow1);
-    pnm_freepamrow(tuplerow2);
+    pnm_freepamrow(tuplerow[0]);
+    pnm_freepamrow(tuplerow[1]);
+    free(tuplerow);
     pnm_freepamrow(tuplerowOut);
-
+    free(plane);
     free(operands);
 }
 
@@ -759,7 +796,7 @@ doUnNormalizedArith(struct pam *  const inpam1P,
 int
 main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     struct pam inpam1;
     struct pam inpam2;
     struct pam outpam;
diff --git a/other/pambayer.c b/other/pambayer.c
index f8ce0db8..7fc1f809 100644
--- a/other/pambayer.c
+++ b/other/pambayer.c
@@ -42,19 +42,20 @@ enum bayerType {
 struct cmdlineInfo {
     const char * inputFilespec;
     enum bayerType bayerType;
+    unsigned int nointerpolate;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+    optEntry * option_def;
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -67,12 +68,14 @@ parseCommandLine(int argc, char ** argv,
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0, "type",     OPT_UINT, &type,
             &typeSpec, 0);
+    OPTENT3(0, "nointerpolate", OPT_FLAG, NULL,
+            &cmdlineP->nointerpolate, 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 < 1)
@@ -102,28 +105,47 @@ calc_4(const struct pam * const pamP,
        tuple **           const intuples,
        tuple **           const outtuples,
        unsigned int       const plane,
+       bool               const noInterpolation,
        unsigned int       const xoffset,
        unsigned int       const yoffset) {
 /*----------------------------------------------------------------------------
     X . X
     . . .
     X . X
+
+  For the Plane 'plane' sample values, an even pixel of outtuples[] gets the
+  same value as intuples[][].  An odd pixel of outtuples[] gets the mean of
+  the four surrounding even pixels, north, south, east, and west.  But zero if
+  Caller says 'noInterpolation'.
+
+  (even/odd is with respect to ('xoffset', 'yoffset')).
 -----------------------------------------------------------------------------*/
-    unsigned int y;
+    unsigned int row;
     
-    for (y = yoffset; y < pamP->height; y += 2) {
-        unsigned int x;
-        for (x = xoffset; x + 2 < pamP->width; x += 2) {
-            outtuples[y][x][plane] = intuples[y][x][0];
-            outtuples[y][x + 1][plane] =
-                (intuples[y][x][0] + intuples[y][x + 2][0]) / 2;
+    /* Do the even rows -- the even column pixels get copied from the input,
+       while the odd column pixels get the mean of adjacent even ones
+    */
+    for (row = yoffset; row < pamP->height; row += 2) {
+        unsigned int col;
+        for (col = xoffset; col + 2 < pamP->width; col += 2) {
+            outtuples[row][col][plane] = intuples[row][col][0];
+            outtuples[row][col + 1][plane] =
+                noInterpolation ?
+                0 :
+                (intuples[row][col][0] + intuples[row][col + 2][0]) / 2;
         }
     }
-    for (y = yoffset; y + 2 < pamP->height; y += 2) {
-        unsigned int x;
-        for (x = xoffset; x < pamP->width; ++x)
-            outtuples[y + 1][x][plane] =
-                (outtuples[y][x][plane] + outtuples[y + 2][x][plane]) / 2;
+
+    /* Do the odd rows -- every pixel is the mean of the one above and below */
+    for (row = yoffset; row + 2 < pamP->height; row += 2) {
+        unsigned int col;
+        for (col = xoffset; col < pamP->width; ++col) {
+            outtuples[row + 1][col][plane] = 
+                noInterpolation ?
+                0 :
+                (outtuples[row][col][plane] +
+                 outtuples[row + 2][col][plane]) / 2;
+        }
     }
 }
 
@@ -134,25 +156,37 @@ calc_5(const struct pam * const pamP,
        tuple **           const intuples,
        tuple **           const outtuples,
        unsigned int       const plane,
+       bool               const noInterpolation,
        unsigned int       const xoffset,
        unsigned int       const yoffset) {
 /*----------------------------------------------------------------------------
    . X .
    X . X
    . X .
+
+  For the Plane 'plane' sample values, an pixel on an even diagonal of
+  outtuples[] gets the same value as intuples[][].  An pixel on an odd
+  diagonal gets the mean of the four surrounding even pixels, north,
+  south, east, and west.  But zero if Caller says 'noInterpolation'.
+
+  (even/odd is with respect to ('xoffset', 'yoffset')).
 -----------------------------------------------------------------------------*/
-    unsigned int y;
+    unsigned int row;
     unsigned int j;
 
     j = 0;  /* initial value */
 
-    for (y = yoffset; y + 2 < pamP->height; ++y) {
-        unsigned int x;
-        for (x = xoffset + j; x + 2 < pamP->width; x += 2) {
-            outtuples[y][x + 1][plane] = intuples[y][x + 1][0];
-            outtuples[y + 1][x + 1][plane] = 
-                (intuples[y][x + 1][0] + intuples[y + 1][x][0] +
-                 intuples[y + 2][x + 1][0] + intuples[y + 1][x + 2][0]) / 4;
+    for (row = yoffset; row + 2 < pamP->height; ++row) {
+        unsigned int col;
+        for (col = xoffset + j; col + 2 < pamP->width; col += 2) {
+            outtuples[row][col + 1][plane] = intuples[row][col + 1][0];
+            outtuples[row + 1][col + 1][plane] =
+                noInterpolation ?
+                0 :
+                (intuples[row][col + 1][0] +
+                 intuples[row + 1][col][0] +
+                 intuples[row + 2][col + 1][0] +
+                 intuples[row + 1][col + 2][0]) / 4;
         }
         j = 1 - j;
     }
@@ -167,6 +201,7 @@ struct compAction {
                  tuple **           const intuples,
                  tuple **           const outtuples,
                  unsigned int       const plane,
+                 bool               const noInterpolation,
                  unsigned int       const xoffset,
                  unsigned int       const yoffset);
 };
@@ -260,7 +295,7 @@ actionTableForType(enum bayerType const bayerType) {
 
 
 int
-main(int argc, char **argv) {
+main(int argc, const char **argv) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
@@ -271,8 +306,8 @@ main(int argc, char **argv) {
     const struct compAction * compActionTable;
     unsigned int plane;
 
-    pnm_init(&argc, argv);
-
+    pm_proginit(&argc, argv);
+    
     parseCommandLine(argc, argv, &cmdline);
     
     ifP = pm_openr(cmdline.inputFilespec);
@@ -289,6 +324,7 @@ main(int argc, char **argv) {
         struct compAction const compAction = compActionTable[plane];
 
         compAction.calc(&inpam, intuples, outtuples, plane,
+                        cmdline.nointerpolate,
                         compAction.xoffset, compAction.yoffset);
     }
     pnm_writepam(&outpam, outtuples);
diff --git a/other/pamchannel.c b/other/pamchannel.c
index 64ab728b..e89a979b 100644
--- a/other/pamchannel.c
+++ b/other/pamchannel.c
@@ -14,14 +14,14 @@
 #include <string.h>
 
 #include "pm_c_util.h"
-#include "pam.h"
-#include "shhopt.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
 
 #define MAX_CHANNELS 16
     /* The most channels we allow user to specify */
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -36,14 +36,14 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
     extern struct pam pam;  /* Just so we can look at field sizes */
@@ -63,7 +63,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!infileSpec)
@@ -74,7 +74,8 @@ parseCommandLine(int argc, char ** argv,
     else
         if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type))
             pm_error("Tuple type name specified is too long.  Maximum of "
-                     "%u characters allowed.", sizeof(pam.tuple_type));
+                     "%u characters allowed.",
+                     (unsigned)sizeof(pam.tuple_type));
 
     cmdlineP->n_channel = 0;  /* initial value */
     { 
@@ -173,13 +174,13 @@ doOneImage(FILE *       const ifP,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
-    bool eof;
+    int eof;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
     
diff --git a/other/pamdepth.c b/other/pamdepth.c
index ee59a408..71dae9d8 100644
--- a/other/pamdepth.c
+++ b/other/pamdepth.c
@@ -28,14 +28,14 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec strings we return are 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.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -51,7 +51,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 < 1)
@@ -133,16 +133,15 @@ transformRaster(struct pam * const inpamP,
 
 
 int
-main(int    argc,
-     char * argv[]) {
+main(int argc, const char * argv[]) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
     struct pam inpam;
     struct pam outpam;
-    bool eof;
+    int eof;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
diff --git a/other/pamexec.c b/other/pamexec.c
new file mode 100644
index 00000000..d14d8752
--- /dev/null
+++ b/other/pamexec.c
@@ -0,0 +1,192 @@
+/******************************************************************************
+                               pamexec
+*******************************************************************************
+  Split a Netpbm format input file into multiple Netpbm format output streams
+  with one image per output stream and pipe this into the specified command.
+
+  By Bryan Henderson, Olympia WA; June 2000
+  and Michael Pot, New Zealand, August 2011
+
+  Contributed to the public domain by its authors.
+
+******************************************************************************/
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "mallocvar.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 * command;
+    const char * inputFileName;
+    unsigned int debug;
+    unsigned int check;
+};
+
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the pointers we place into *cmdlineP are sometimes to storage
+   in 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 OPTENT3 */
+    OPTENT3(0,   "debug",   OPT_FLAG,   NULL,         &cmdlineP->debug, 0);
+    OPTENT3(0,   "check",   OPT_FLAG,   NULL,         &cmdlineP->check, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;   /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 < 1) 
+        pm_error("You must specify at least one argument: the shell command "
+                 "to execute");
+    else {
+        cmdlineP->command = argv[1];
+
+        if (argc-1 < 2)
+            cmdlineP->inputFileName = "-";
+        else {
+            cmdlineP->inputFileName = argv[2];
+
+            if (argc-1 > 2)
+                pm_error("Too many arguments.  There are at most two: "
+                         "command and input file name");
+        }
+    }
+}
+
+
+
+static void
+pipeOneImage(FILE * const infileP,
+             FILE * const outfileP) {
+
+    struct pam inpam;
+    struct pam outpam;
+    enum pm_check_code checkRetval;
+    
+    unsigned int row;
+    tuple * tuplerow;
+
+    pnm_readpaminit(infileP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    pnm_checkpam(&inpam, PM_CHECK_BASIC, &checkRetval);
+
+    outpam = inpam;
+    outpam.file = outfileP;
+
+    pnm_writepaminit(&outpam);
+
+    tuplerow = pnm_allocpamrow(&inpam);
+
+    for (row = 0; row < inpam.height; ++row) {
+        pnm_readpamrow(&inpam, tuplerow);
+        pnm_writepamrow(&outpam, tuplerow);
+    }
+
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+doOneImage(FILE *        const ifP,
+           const char *  const command,
+           bool          const check,
+           const char ** const errorP) {
+/*----------------------------------------------------------------------------
+   Run command 'command' on the next image in stream *ifP.
+
+   Return as *errorP a text explanation of how we failed, or NULL if we
+   didn't.
+-----------------------------------------------------------------------------*/
+    FILE * ofP;
+
+    ofP = popen(command, "w");
+
+    if (ofP == NULL)
+        pm_asprintf(errorP, 
+                    "Failed to start shell to run command '%s'.  "
+                    "errno = %d (%s)",
+                    command, errno, strerror(errno));
+    else {
+        int rc;
+
+        pipeOneImage(ifP, ofP);
+            
+        rc = pclose(ofP);
+
+        if (check && rc != 0)
+            pm_asprintf(errorP, "Command '%s' terminated abnormally "
+                        "or with nonzero exit status", command);
+        else
+            *errorP = NULL;
+    }
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+
+    FILE *       ifP;         /* Input file pointer */
+    int          eof;         /* No more images in input */
+    unsigned int imageSeq;
+        /* Sequence number of current image in input file.  First = 0.
+           (Useful for tracking down problems).
+        */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+    
+    ifP = pm_openr(cmdline.inputFileName);
+
+    for (eof = FALSE, imageSeq = 0; !eof; ++imageSeq) {
+        const char * error;
+        
+        doOneImage(ifP, cmdline.command, cmdline.check, &error);
+
+        if (error) {
+            pm_error("Failed on image %u: %s", imageSeq, error);
+            pm_strfree(error);
+        }
+
+        pnm_nextimage(ifP, &eof);
+    }
+    pm_close(ifP);
+    
+    return 0;
+}
+
+
+
diff --git a/other/pamfix.c b/other/pamfix.c
new file mode 100644
index 00000000..8db67e08
--- /dev/null
+++ b/other/pamfix.c
@@ -0,0 +1,291 @@
+/*============================================================================
+                                 pamfix
+==============================================================================
+  Salvage a Netpbm image that is corrupted in certain ways.
+
+  By Bryan Henderson, January 2007.
+
+  Contributed to the public domain by its author.
+============================================================================*/
+
+#include <setjmp.h>
+
+#include "pm_c_util.h"
+#include "pam.h"
+#include "shhopt.h"
+#include "mallocvar.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;   /* File name of input file */
+    unsigned int verbose;
+    unsigned int truncate;
+    unsigned int changemaxval;
+    unsigned int clip;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** const argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+
+    OPTENT3(0, "verbose",      OPT_FLAG, NULL, &cmdlineP->verbose,      0);
+    OPTENT3(0, "truncate",     OPT_FLAG, NULL, &cmdlineP->truncate,     0);
+    OPTENT3(0, "changemaxval", OPT_FLAG, NULL, &cmdlineP->changemaxval, 0);
+    OPTENT3(0, "clip",         OPT_FLAG, NULL, &cmdlineP->clip,         0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We don't parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    free(option_def);
+
+    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];
+
+    if (cmdlineP->changemaxval && cmdlineP->clip)
+        pm_error("You cannot specify both -changemaxval and -clip");
+}
+
+
+
+static unsigned int readErrRow;
+static bool readErrVerbose;
+
+static pm_usererrormsgfn handleRowErrMsg;
+
+static void
+handleRowErrMsg(const char * const msg) {
+    if (readErrVerbose)
+        pm_message("Error reading row %u: %s", readErrRow, msg);
+}
+
+
+static sample
+highestSampleInRow(const struct pam * const pamP,
+                   tuple *            const tuplerow) {
+
+    unsigned int col;
+    sample highestSoFar;
+
+    for (col = 0, highestSoFar = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane)
+            highestSoFar = MAX(highestSoFar, tuplerow[col][plane]);
+    }
+    return highestSoFar;
+}
+
+
+
+static void
+analyzeRaster(const struct pam * const pamP,
+              unsigned int *     const goodRowCountP,
+              sample *           const highestSampleP,
+              bool               const mustAbortOnReadError,
+              bool               const verbose) {
+/*----------------------------------------------------------------------------
+   Go through the raster at which the stream described by *tweakedPamP is
+   presently positioned and count how many rows can be successfully read
+   (including validating the samples against pamP->maxval) and determine the
+   highest sample value in those rows.
+
+   Leave the stream positioned arbitrarily.
+-----------------------------------------------------------------------------*/
+    tuple * tuplerow;
+    unsigned int row;
+    jmp_buf jmpbuf;
+    int rc;
+    
+    tuplerow = pnm_allocpamrow(pamP);
+
+    pm_setusererrormsgfn(handleRowErrMsg);
+
+    rc = setjmp(jmpbuf);
+    if (rc == 0) {
+        pm_setjmpbuf(&jmpbuf);
+
+        readErrVerbose  = mustAbortOnReadError || verbose;
+        *goodRowCountP  = 0;  /* initial value */
+        *highestSampleP = 0;  /* initial value */
+
+        for (row = 0; row < pamP->height; ++row) {
+            readErrRow = row;
+            pnm_readpamrow(pamP, tuplerow);
+            /* The above does not return if it can't read the next row from
+               the file.  Instead, it longjmps out of this loop.
+
+               Update return stats now in case the next iteration longjmps out.
+            */
+            *highestSampleP =
+                MAX(*highestSampleP,
+                    highestSampleInRow(pamP, tuplerow));
+            ++*goodRowCountP;
+        }
+    } else {
+        /* pnm_readpamrow() encountered an error and longjmped */
+        if (mustAbortOnReadError) {
+            /* handleRowErrMsg() has issued the error message */
+            exit(1);
+        }
+    }
+    pm_setjmpbuf(NULL);
+
+    pm_setusererrormsgfn(NULL);
+
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+clipPamRow(const struct pam * const pamP,
+           tuple *            const tuplerow,
+           unsigned int       const row,
+           bool               const verbose) {
+/*----------------------------------------------------------------------------
+   Clip every sample value in tuplerow[] to the maxval.
+
+   If 'verbose' is true, issue messages about every clipping, indicating it
+   is in row 'row'.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+
+    for (col = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < pamP->depth; ++plane) {
+            if (tuplerow[col][plane] > pamP->maxval) {
+                if (verbose) 
+                    pm_message("Clipping: Row %u Col %u Plane %u.  "
+                               "Sample value %lu exceeds the "
+                               "image maxval of %lu",
+                               row, col, plane, tuplerow[col][plane],
+                               pamP->maxval);
+	            tuplerow[col][plane] = pamP->maxval;
+            }
+        }
+    }
+}
+
+
+
+static void
+copyGoodRows(const struct pam * const inpamP,
+             const struct pam * const outpamP,
+             bool               const verbose) {
+/*----------------------------------------------------------------------------
+  Copy the raster of the input stream described by *inpamP to the output
+  stream described by *outpamP.  Copy only as many rows as *outpamP allows;
+  assume *outpamP specifies at most the number of rows of input.
+-----------------------------------------------------------------------------*/
+    tuple * tuplerow;
+    unsigned int row;
+
+    tuplerow = pnm_allocpamrow(inpamP);
+
+    for (row = 0; row < outpamP->height; ++row) {
+        pnm_readpamrow(inpamP, tuplerow);
+        clipPamRow(outpamP, tuplerow, row, verbose);
+        pnm_writepamrow(outpamP, tuplerow);
+    }
+    
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+    struct cmdlineInfo cmdline;
+    struct pam inpam;
+    struct pam outpam;
+    struct pam tweakedPam;
+    FILE * ifP;
+    pm_filepos rasterPos;
+    unsigned int goodRowCount;
+    sample highestSample;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr_seekable(cmdline.inputFileName);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
+
+    /* Tweak maxval to circumvent out-of-bounds sample value check
+       in libpam.  See function validatePamRow() in libpamread.c .
+
+       Ideally we would like to set tweaked-maxval higher for the
+       plain (text) formats.  We make a compromise to keep things
+       simple.
+    */
+
+    tweakedPam = inpam;  /* initial value */
+
+    if ((cmdline.clip || cmdline.changemaxval)
+        && PNM_FORMAT_TYPE(inpam.format) != PBM_TYPE)
+        tweakedPam.maxval =
+            (((sample) 1) << tweakedPam.bytes_per_sample * 8) - 1;
+
+    analyzeRaster(&tweakedPam, &goodRowCount, &highestSample,
+                  !cmdline.truncate, cmdline.verbose);
+
+    if (goodRowCount == 0)
+        pm_error("Cannot read a single row from the image%s",
+                 cmdline.verbose ? "" : ".  Use -verbose to find out why");
+
+    if (goodRowCount < inpam.height)
+        pm_message("Copying %u good rows; %u bottom rows missing%s",
+                   goodRowCount, inpam.height - goodRowCount,
+                   cmdline.verbose ? "" : ".  Use -verbose to find out why");
+
+    pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
+
+    outpam = inpam;  /* initial value */
+
+    outpam.file = stdout;
+    outpam.height = goodRowCount;
+    if (cmdline.changemaxval && highestSample > outpam.maxval) {
+        pm_message("Raising maxval from %lu to %lu to legitimize "
+                   "all sample values",
+                    outpam.maxval, highestSample);
+        outpam.maxval = highestSample;
+    }
+
+    pnm_writepaminit(&outpam);
+
+    copyGoodRows(&tweakedPam, &outpam, cmdline.verbose);
+
+    pm_close(inpam.file);
+    
+    return 0;
+}
+
+
+
diff --git a/other/pamfixtrunc b/other/pamfixtrunc
new file mode 100755
index 00000000..1aa3ff94
--- /dev/null
+++ b/other/pamfixtrunc
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+##############################################################################
+# This is essentially a Perl program.  We exec the Perl interpreter specifying
+# this same file as the Perl program and use the -x option to cause the Perl
+# interpreter to skip down to the Perl code.  The reason we do this instead of
+# just making /usr/bin/perl the script interpreter (instead of /bin/sh) is
+# that the user may have multiple Perl interpreters and the one he wants to
+# use is properly located in the PATH.  The user's choice of Perl interpreter
+# may be crucial, such as when the user also has a PERL5LIB environment
+# variable and it selects modules that work with only a certain main
+# interpreter program.
+#
+# An alternative some people use is to have /usr/bin/env as the script
+# interpreter.  We don't do that because we think the existence and
+# compatibility of /bin/sh is more reliable.
+#
+# Note that we aren't concerned about efficiency because the user who needs
+# high efficiency can use directly the programs that this program invokes.
+#
+##############################################################################
+
+exec perl -w -x -S -- "$0" "$@"
+
+#!/usr/bin/perl
+##############################################################################
+#  This is nothing but a compatibility interface for Pamfixtrunc.
+#  An old program coded to call Pamfixtrunc will continue working because
+#  this interface exists.  All new (or newly modified) programs should
+#  call Pamfix instead.
+##############################################################################
+
+use strict;
+use File::Basename;
+use Cwd 'abs_path';
+
+my @pamFixOptions;
+
+@pamFixOptions = @ARGV;  # initial value
+
+push(@pamFixOptions, '-truncate');
+
+# We want to get Pamfix from the same directory we came from if
+# it's there.  Frequently, the directory containing Netpbm programs is
+# not in the PATH and we were invoked by absolute path.
+
+my $my_directory = abs_path(dirname($0));
+$ENV{"PATH"} = $my_directory . ":" . $ENV{"PATH"};
+
+exit(system('pamfix', @pamFixOptions)>>8);
diff --git a/other/pamfixtrunc.c b/other/pamfixtrunc.c
deleted file mode 100644
index 6d71406f..00000000
--- a/other/pamfixtrunc.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/*============================================================================
-                             pamfixtrunc
-==============================================================================
-  Fix a Netpbm image that has been truncated, e.g. by I/O error.
-
-  By Bryan Henderson, January 2007.
-
-  Contributed to the public domain by its author.
-
-============================================================================*/
-
-#include <setjmp.h>
-
-#include "pm_c_util.h"
-#include "pam.h"
-#include "shhopt.h"
-#include "mallocvar.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;  /* Filespec of input file */
-    unsigned int verbose;
-};
-
-
-
-static void
-parseCommandLine(int argc, char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
-/*----------------------------------------------------------------------------
-   Note that the file spec array we return is stored in the storage that
-   was passed to us as the argv array.
------------------------------------------------------------------------------*/
-    optEntry * option_def;
-
-    optStruct3 opt;
-
-    unsigned int option_def_index;
-
-    MALLOCARRAY(option_def, 100);
-
-    option_def_index = 0;   /* incremented by OPTENTRY */
-
-    OPTENT3(0, "verbose",   OPT_FLAG,    NULL, &cmdlineP->verbose,       0);
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = FALSE;  /* We don't 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->inputFilespec = "-";
-    else if (argc-1 != 1)
-        pm_error("Program takes zero or one argument (filename).  You "
-                 "specified %d", argc-1);
-    else
-        cmdlineP->inputFilespec = argv[1];
-}
-
-
-
-static unsigned int readErrRow;
-static bool readErrVerbose;
-
-static pm_usererrormsgfn discardMsg;
-
-static void
-discardMsg(const char * const msg) {
-    if (readErrVerbose)
-        pm_message("Error reading row %u: %s", readErrRow, msg);
-}
-
-
-
-static void
-countRows(const struct pam * const inpamP,
-          bool               const verbose,
-          unsigned int *     const goodRowCountP) {
-
-    tuple * tuplerow;
-    unsigned int row;
-    jmp_buf jmpbuf;
-    int rc;
-    unsigned int goodRowCount;
-    
-    tuplerow = pnm_allocpamrow(inpamP);
-
-    pm_setusererrormsgfn(discardMsg);
-
-    rc = setjmp(jmpbuf);
-    if (rc == 0) {
-        pm_setjmpbuf(&jmpbuf);
-
-        readErrVerbose = verbose;
-        goodRowCount = 0;  /* initial value */
-        for (row = 0; row < inpamP->height; ++row) {
-            readErrRow = row;
-            pnm_readpamrow(inpamP, tuplerow);
-            /* The above does not return if it can't read the next row from
-               the file.  Instead, it longjmps out of this loop.
-            */
-            ++goodRowCount;
-        }
-    }
-    *goodRowCountP = goodRowCount;
-
-    pnm_freepamrow(tuplerow);
-}
-
-
-
-static void
-copyGoodRows(const struct pam * const inpamP,
-             FILE *             const ofP,
-             unsigned int       const goodRowCount) {
-
-    struct pam outpam;
-    tuple * tuplerow;
-    unsigned int row;
-
-    outpam = *inpamP;  /* initial value */
-
-    outpam.file = ofP;
-    outpam.height = goodRowCount;
-
-    tuplerow = pnm_allocpamrow(inpamP);
-
-    pnm_writepaminit(&outpam);
-
-    for (row = 0; row < outpam.height; ++row) {
-        pnm_readpamrow(inpamP, tuplerow);
-        pnm_writepamrow(&outpam, tuplerow);
-    }
-    
-    pnm_freepamrow(tuplerow);
-}
-
-
-
-int
-main(int argc, char * argv[]) {
-    struct cmdlineInfo cmdline;
-    struct pam inpam;
-    FILE * ifP;
-    pm_filepos rasterPos;
-    unsigned int goodRowCount;
-
-    pnm_init(&argc, argv);
-
-    parseCommandLine(argc, argv, &cmdline);
-
-    ifP = pm_openr_seekable(cmdline.inputFilespec);
-
-    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
-
-    pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
-
-    countRows(&inpam, cmdline.verbose, &goodRowCount);
-
-    pm_message("Copying %u good rows; %u bottom rows missing",
-               goodRowCount, inpam.height - goodRowCount);
-    
-    pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
-
-    copyGoodRows(&inpam, stdout, goodRowCount);
-
-    pm_close(inpam.file);
-    
-    return 0;
-}
-
-
diff --git a/other/pamlookup.c b/other/pamlookup.c
index 3b7a7f59..4ceb047f 100644
--- a/other/pamlookup.c
+++ b/other/pamlookup.c
@@ -13,32 +13,36 @@
 
 ============================================================================*/
 
+#include <assert.h>
+
 #include "pm_c_util.h"
-#include "pam.h"
+#include "mallocvar.h"
 #include "shhopt.h"
 #include "pm_system.h"
 #include "nstring.h"
+#include "pam.h"
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *indexFilespec;  
-    char *lookupFilespec;
-    char *missingcolor;  /* -missingcolor value.  null if not specified */
-    unsigned int fit;  /* -fit option */
+    const char * indexFilespec;  
+    char *       lookupFilespec;
+    char *       missingcolor;  /* null if not specified */
+    unsigned int fit;
+    unsigned int byplane;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** const argv,
-                 struct cmdlineInfo * const cmdlineP) {
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
         /* Instructions to OptParseOptions2 on how to parse our options.
          */
     optStruct3 opt;
@@ -47,6 +51,8 @@ parseCommandLine(int argc, char ** const argv,
     
     unsigned int lookupfileSpec, missingcolorSpec;
 
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0, "lookupfile",     OPT_STRING, &cmdlineP->lookupFilespec,  
             &lookupfileSpec, 0);
@@ -54,12 +60,14 @@ parseCommandLine(int argc, char ** const argv,
             &cmdlineP->missingcolor,   &missingcolorSpec, 0);
     OPTENT3(0,   "fit", OPT_FLAG, 
             NULL,   &cmdlineP->fit, 0);
+    OPTENT3(0,   "byplane", OPT_FLAG, 
+            NULL,   &cmdlineP->byplane, 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!lookupfileSpec)
@@ -68,10 +76,15 @@ parseCommandLine(int argc, char ** const argv,
     if (!missingcolorSpec)
         cmdlineP->missingcolor = NULL;
 
+    if (cmdlineP->byplane && cmdlineP->missingcolor)
+        pm_error("You cannot specify -missingcolor with -byplane");
+
     if (argc-1 < 1)
         cmdlineP->indexFilespec = "-";
     else
         cmdlineP->indexFilespec = argv[1];
+
+    free(option_def);
 }        
 
 
@@ -94,7 +107,7 @@ fitLookup(tuple **     const inputLookup,
     fitLookuppamP->width = cols;
     fitLookuppamP->height = rows;
 
-    asprintfN(&pamscaleCommand, "pamscale -width=%u -height=%u", cols, rows);
+    pm_asprintf(&pamscaleCommand, "pamscale -width=%u -height=%u", cols, rows);
 
     inPamtuples.pamP = (struct pam *) &inputLookuppam;
     inPamtuples.tuplesP = (tuple ***) &inputLookup;
@@ -105,25 +118,38 @@ fitLookup(tuple **     const inputLookup,
               &pm_accept_to_pamtuples, &outPamtuples,
               pamscaleCommand);
 
-    strfree(pamscaleCommand);
+    pm_strfree(pamscaleCommand);
 }
 
 
 
 static void
-getLookup(const char * const lookupFilespec, 
+getLookup(const char * const lookupFileName, 
           unsigned int const indexDegree,
           unsigned int const indexMaxval,
           tuple ***    const lookupP,
           struct pam * const lookuppamP,
           bool         const fit) {
+/*----------------------------------------------------------------------------
+   Get the lookup image (the one that maps integers to tuples, e.g. a
+   color index / color map / palette) from the file named 
+   'lookupFileName'.
 
-    FILE*  lookupfileP;
+   Interpret the lookup image for use with indices that are ntuples of size
+   'indexDegree' (normally 1, could be 2) whose elements range from 0 through
+   'indexMaxval'
+
+   Iff 'fit' is true, stretch or compress the image in the file to fit
+   exactly the range identified by 'indexMaxval'.
+
+   Return the image as *lookupP and *lookuppamP.
+-----------------------------------------------------------------------------*/
+    FILE *  lookupfileP;
 
     struct pam inputLookuppam;
-    tuple** inputLookup;
+    tuple ** inputLookup;
 
-    lookupfileP = pm_openr(lookupFilespec);
+    lookupfileP = pm_openr(lookupFileName);
     inputLookup = pnm_readpam(lookupfileP, 
                               &inputLookuppam, PAM_STRUCT_SIZE(tuple_type));
 
@@ -153,14 +179,14 @@ getLookup(const char * const lookupFilespec,
     if (indexDegree == 2 && lookuppamP->height - 1 > indexMaxval)
         pm_message("Warning: your lookup table image is taller than "
                    "the maxval of "
-                   "your index message, so the bottom end of the lookup "
+                   "your index image, so the bottom end of the lookup "
                    "table image has no effect on the output.");
 }
 
 
 
 static void
-computeDefaultTuple(struct cmdlineInfo const cmdline, 
+computeDefaultTuple(struct CmdlineInfo const cmdline, 
                     tuple **           const lookup,
                     struct pam *       const lookuppamP, 
                     tuple *            const defaultTupleP) {
@@ -203,20 +229,94 @@ computeDefaultTuple(struct cmdlineInfo const cmdline,
 
 
 static void
-doLookup(struct pam const indexpam,
-         struct pam const outpamarg,
-         tuple      const defaultTuple,
-         tuple **   const lookup,
-         struct pam const lookuppam) {
+doLookupByPlane(struct pam const indexpam,
+                tuple **   const lookup,
+                struct pam const lookuppam,
+                FILE *     const ofP) {
+/*----------------------------------------------------------------------------
+   Write an output image to *ofP derived from the input image read per
+   'indexpam' (now positioned to its raster).
 
+   Base each tuple of the output on the tuple in the same place in the input.
+   Look up each sample of the input tuple in the lookupt image given by
+   'lookup' and 'lookuppam' to get the corresponding sample of the output
+   image.
+
+   Our output image has the same width, height, depth, image type, and tuple
+   type as the input image and the same maxval as the lookup image.
+
+   We ignore any plane or row after the first in the lookup image.  We expect
+   its width to match the maxval of the input image.
+-----------------------------------------------------------------------------*/
     struct pam outpam;
     unsigned int row;
 
     tuple* tuplerowIndex;
     tuple* tuplerowOut;
 
-    outpam = outpamarg;
+    outpam = indexpam;  /* initial value */
+    outpam.maxval = lookuppam.maxval;
+    outpam.file = ofP;
+    
+    tuplerowIndex = pnm_allocpamrow(&indexpam);
+    tuplerowOut = pnm_allocpamrow(&outpam);
+
+    pnm_writepaminit(&outpam);
+
+    assert(lookuppam.width == indexpam.maxval + 1);
+        /* Calling condition */
+
+    for (row = 0; row < indexpam.height; ++row) {
+        unsigned int col;
+        pnm_readpamrow(&indexpam, tuplerowIndex);
+        
+        for (col = 0; col < indexpam.width; ++col) {
+            unsigned int plane;
+
+            for (plane = 0; plane < indexpam.depth; ++plane) {
+                unsigned int const index = tuplerowIndex[col][plane];
+
+                if (index > lookuppam.maxval)
+                    pm_error("Sample value %u in the lookup image exceeds "
+                             "the lookup image's maxval (%u)",
+                             index, (unsigned)lookuppam.maxval);
+
+                tuplerowOut[col][plane] = lookup[0][index][0];
+            }
+        }
+        pnm_writepamrow(&outpam, tuplerowOut);
+    }
+    pnm_freepamrow(tuplerowIndex);
+    pnm_freepamrow(tuplerowOut);
+}
+
+
+
+static void
+doLookupWholeTuple(struct pam const indexpam,
+                   tuple      const defaultTuple,
+                   tuple **   const lookup,
+                   struct pam const lookuppam,
+                   FILE *     const ofP) {
+/*----------------------------------------------------------------------------
+   Write an output image to *ofP derived from the input image read per
+   'indexpam' (now positioned to its raster).
+
+   For each tuple of the output, use the corresponding tuple of the input as
+   an index into the lookup image given by 'lookup' and 'lookuppam'.  If that
+   index is not present in the lookup image, put 'defaultTuple' in the output.
+-----------------------------------------------------------------------------*/
+    struct pam outpam;
+    unsigned int row;
+
+    tuple* tuplerowIndex;
+    tuple* tuplerowOut;
 
+    outpam = lookuppam;  /* initial value */
+    outpam.height = indexpam.height;
+    outpam.width = indexpam.width;
+    outpam.file = ofP;
+    
     tuplerowIndex = pnm_allocpamrow(&indexpam);
     tuplerowOut = pnm_allocpamrow(&outpam);
 
@@ -254,18 +354,18 @@ doLookup(struct pam const indexpam,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char ** const argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     struct pam indexpam;
-    struct pam outpam;
-    FILE*  ifP;
+    FILE * ifP;
+    unsigned int indexDegree;
     struct pam lookuppam;
-    tuple** lookup;
+    tuple ** lookup;
 
     tuple defaultTuple;
     
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -273,28 +373,30 @@ main(int argc, char *argv[]) {
 
     pnm_readpaminit(ifP, &indexpam, PAM_STRUCT_SIZE(tuple_type));
 
-    if (indexpam.depth != 1 && indexpam.depth != 2)
-        pm_error("The input (index) file must have depth 1 or 2.  "
-                 "Yours has depth %d",
+    if (!cmdline.byplane && (indexpam.depth != 1 && indexpam.depth != 2))
+        pm_error("Unless you specify -byplane, "
+                 "the input (index) file must have depth 1 or 2.  "
+                 "Yours has depth %u",
                  indexpam.depth);
 
-    getLookup(cmdline.lookupFilespec, indexpam.depth, indexpam.maxval, 
-              &lookup, &lookuppam, cmdline.fit);
+    indexDegree = cmdline.byplane ? 1 : indexpam.depth;
+
+    getLookup(cmdline.lookupFilespec, indexDegree, indexpam.maxval, 
+              &lookup, &lookuppam, cmdline.fit || cmdline.byplane);
 
     computeDefaultTuple(cmdline, lookup, &lookuppam, &defaultTuple);
 
-    outpam = lookuppam;
-    outpam.height = indexpam.height;
-    outpam.width = indexpam.width;
-    outpam.file = stdout;
-    
-    doLookup(indexpam, outpam, defaultTuple, lookup, lookuppam);
+    if (cmdline.byplane)
+        doLookupByPlane(indexpam, lookup, lookuppam, stdout);
+    else
+        doLookupWholeTuple(indexpam, defaultTuple, lookup, lookuppam, stdout);
 
     pm_close(ifP);
 
     pnm_freepamtuple(defaultTuple);
     pnm_freepamarray(lookup, &lookuppam);
     
-    exit(0);
+    return 0;
 }
 
+
diff --git a/other/pampick.c b/other/pampick.c
index 63f32968..61941f06 100644
--- a/other/pampick.c
+++ b/other/pampick.c
@@ -92,7 +92,7 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the pointers we place into *cmdlineP are sometimes to storage
@@ -119,7 +119,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     initUintSet(&cmdlineP->imageSeqList, argc-1);
@@ -217,15 +217,15 @@ failIfUnpickedImages(const struct uintSet * const uintSetP,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;
 
-    bool eof;  /* No more images in input */
+    int eof;  /* No more images in input */
     unsigned int imageSeq;  
         /* Sequence of current image in input file.  First = 0 */
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
     
diff --git a/other/pamsplit.c b/other/pamsplit.c
index a03353d7..26eb0d59 100644
--- a/other/pamsplit.c
+++ b/other/pamsplit.c
@@ -16,10 +16,10 @@
 #include <stdio.h>
 
 #include "pm_c_util.h"
-#include "pam.h"
 #include "shhopt.h"
 #include "nstring.h"
 #include "mallocvar.h"
+#include "pam.h"
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -34,7 +34,7 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the pointers we place into *cmdlineP are sometimes to storage
@@ -59,7 +59,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!padnameSpec)
@@ -138,11 +138,11 @@ computeOutputName(char          const outputFilePattern[],
     afterSub = strstr(outputFilePattern, "%d") + 2;
 
     /* Make filenameFormat something like "%s%04u%s" */
-    asprintfN(&filenameFormat, "%%s%%0%ud%%s", padCount);
+    pm_asprintf(&filenameFormat, "%%s%%0%ud%%s", padCount);
 
-    asprintfN(outputNameP, filenameFormat, beforeSub, imageSeq, afterSub);
+    pm_asprintf(outputNameP, filenameFormat, beforeSub, imageSeq, afterSub);
 
-    strfree(filenameFormat);
+    pm_strfree(filenameFormat);
 
     free(beforeSub);
 }
@@ -150,16 +150,16 @@ computeOutputName(char          const outputFilePattern[],
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;
 
     FILE * ifP;
-    bool eof;  /* No more images in input */
+    int eof;  /* No more images in input */
     unsigned int imageSeq;  
         /* Sequence of current image in input file.  First = 0 */
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
     
@@ -179,7 +179,7 @@ main(int argc, char *argv[]) {
         extractOneImage(ifP, ofP);
 
         pm_close(ofP);
-        strfree(outputFileName);
+        pm_strfree(outputFileName);
 
         pnm_nextimage(ifP, &eof);
     }
@@ -187,3 +187,6 @@ main(int argc, char *argv[]) {
     
     return 0;
 }
+
+
+
diff --git a/other/pamstack.c b/other/pamstack.c
index d826cf1f..308852c8 100644
--- a/other/pamstack.c
+++ b/other/pamstack.c
@@ -44,7 +44,7 @@ parseCommandLine(int argc, char ** argv,
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
     extern struct pam pam;  /* Just so we can look at field sizes */
@@ -62,7 +62,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!tupletypeSpec)
@@ -70,7 +70,8 @@ parseCommandLine(int argc, char ** argv,
     else
         if (strlen(cmdlineP->tupletype)+1 > sizeof(pam.tuple_type))
             pm_error("Tuple type name specified is too long.  Maximum of "
-                     "%u characters allowed.", sizeof(pam.tuple_type));
+                     "%u characters allowed.",
+                     (unsigned)sizeof(pam.tuple_type));
 
     cmdlineP->nInput = 0;  /* initial value */
     { 
@@ -215,10 +216,10 @@ nextImageAllStreams(unsigned int const nInput,
     unsigned int inputSeq;
 
     for (inputSeq = 0; inputSeq < nInput; ++inputSeq) {
-        bool eof;
+        int eof;
         pnm_nextimage(ifP[inputSeq], &eof);
         if (eof)
-            *eofP = eof;
+            *eofP = true;
     }
 }
 
diff --git a/other/pamsummcol.c b/other/pamsummcol.c
index c31a9940..c84f38ad 100644
--- a/other/pamsummcol.c
+++ b/other/pamsummcol.c
@@ -54,7 +54,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (sumSpec + minSpec + maxSpec > 1)
diff --git a/other/pamunlookup.c b/other/pamunlookup.c
new file mode 100644
index 00000000..77c2807b
--- /dev/null
+++ b/other/pamunlookup.c
@@ -0,0 +1,250 @@
+/*============================================================================
+                               pamunlookup
+==============================================================================
+  Find tuple values from an input image in a lookup table and
+  produce a corresponding index image containing table indices.
+
+  The lookup table is a one-row PAM image tuple type the same as the input
+  image.  The output index image has the same width and height as the input
+  image depth 1, and maxval equal to the width of the lookup image (the
+  possible values include one for each column in the lookup image, plus one
+  for tuple values that are not in the lookup image).
+
+  By Bryan Henderson, San Jose CA 2015.08.08
+
+============================================================================*/
+
+#include <assert.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pm_system.h"
+#include "nstring.h"
+#include "pam.h"
+#include "pammap.h"
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+    char *       lookupfile;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions2 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+    
+    unsigned int lookupfileSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "lookupfile",     OPT_STRING, &cmdlineP->lookupfile,  
+            &lookupfileSpec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!lookupfileSpec)
+        pm_error("You must specify the -lookupfile option");
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else
+        cmdlineP->inputFileName = argv[1];
+
+    free(option_def);
+}        
+
+
+
+static void
+getLookup(const char * const lookupFileName, 
+          tuple ***    const lookupP,
+          struct pam * const lookuppamP) {
+/*----------------------------------------------------------------------------
+   Get the lookup image (the one that maps integers to tuples, e.g. a
+   color index / color map / palette) from the file named 
+   'lookupFileName'.
+
+   Return the image as *lookupP and *lookuppamP.
+-----------------------------------------------------------------------------*/
+    FILE *  lookupfileP;
+
+    struct pam inputLookuppam;
+    tuple ** inputLookup;
+
+    lookupfileP = pm_openr(lookupFileName);
+    inputLookup = pnm_readpam(lookupfileP, 
+                              &inputLookuppam, PAM_STRUCT_SIZE(tuple_type));
+
+    pm_close(lookupfileP);
+    
+    if (inputLookuppam.height != 1)
+        pm_error("The lookup table image must be one row.  "
+                 "Yours is %u rows.", 
+                 inputLookuppam.height);
+
+    *lookupP = inputLookup;
+    *lookuppamP = inputLookuppam;
+}
+
+
+
+static void
+makeReverseLookupHash(struct pam * const lookuppamP,
+                      tuple **     const lookup,
+                      tuplehash *  const hashP) {
+/*----------------------------------------------------------------------------
+   Create a tuple hash that maps each tuple values in the first row of
+   'lookup' to the number of the column in which it appears.
+
+   Abort the program with an error if the same tuple value occurs in two
+   columns of the first row.
+-----------------------------------------------------------------------------*/
+    tuplehash hash;
+    unsigned int col;
+
+    hash = pnm_createtuplehash();
+
+    for (col = 0; col < lookuppamP->width; ++col) {
+        tuple const thisValue = lookup[0][col];
+        
+        int found;
+        int priorValue;
+
+        pnm_lookuptuple(lookuppamP, hash, thisValue, &found, &priorValue);
+
+        if (found)
+            pm_error("Same tuple value occurs in both Column %u and "
+                     "Column %u of the lookup image", priorValue, col);
+        else {
+            int fits;
+            pnm_addtotuplehash(lookuppamP, hash, lookup[0][col], col, &fits);
+
+            if (!fits)
+                pm_error("Out of memory constructing hash of lookup table");
+        }
+    }
+
+    *hashP = hash;
+}
+
+
+
+static void
+doUnlookup(struct pam * const inpamP,
+           tuplehash    const lookupHash,
+           sample       const maxIndex,
+           FILE *       const ofP) {
+
+    struct pam outpam;
+    unsigned int row;
+    tuple * inrow;
+    tuple * outrow;
+
+    inrow = pnm_allocpamrow(inpamP);
+
+    outpam.size = sizeof(outpam);
+    outpam.len = PAM_STRUCT_SIZE(tuple_type);
+    outpam.file = ofP;
+    outpam.format = PAM_FORMAT;
+    outpam.height = inpamP->height;
+    outpam.width = inpamP->width;
+    outpam.depth = 1;
+    outpam.maxval = maxIndex + 1;  /* +1 for missing color */
+    strcpy(outpam.tuple_type, "INDEX");
+
+    pnm_writepaminit(&outpam);
+
+    outrow = pnm_allocpamrow(&outpam);
+
+    for (row = 0; row < inpamP->height; ++row) {
+        unsigned int col;
+        
+        pnm_readpamrow(inpamP, inrow);
+
+        for (col = 0; col < inpamP->width; ++col) {
+            int found;
+            int index;
+            pnm_lookuptuple(inpamP, lookupHash, inrow[col], &found, &index);
+
+            if (found) {
+                assert(index <= outpam.maxval);
+                outrow[col][0] = index;
+            } else
+                outrow[col][0] = maxIndex + 1;
+        }
+        pnm_writepamrow(&outpam, outrow);
+    }
+
+    pnm_freepamrow(outrow);
+    pnm_freepamrow(inrow);
+}
+
+
+
+int
+main(int argc, const char ** const argv) {
+
+    struct CmdlineInfo cmdline;
+    struct pam inpam;
+    FILE * ifP;
+    struct pam lookuppam;
+    tuple ** lookup;
+
+    tuplehash lookupHash;
+    
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    getLookup(cmdline.lookupfile, &lookup, &lookuppam);
+
+    if (inpam.depth != lookuppam.depth)
+        pm_error("The lookup image has depth %u, but the input image "
+                 "has depth %u.  They must be the same",
+                 lookuppam.depth, inpam.depth);
+    if (!streq(inpam.tuple_type, lookuppam.tuple_type))
+        pm_error("The lookup image has tupel type '%s', "
+                 "but the input image "
+                 "has tuple type '%s'.  They must be the same",
+                 lookuppam.tuple_type, inpam.tuple_type);
+
+    makeReverseLookupHash(&lookuppam, lookup, &lookupHash);
+
+    doUnlookup(&inpam, lookupHash, lookuppam.width-1, stdout);
+
+    pm_close(ifP);
+
+    pnm_destroytuplehash(lookupHash);
+    pnm_freepamarray(lookup, &lookuppam);
+    
+    return 0;
+}
+
+
diff --git a/other/pamvalidate.c b/other/pamvalidate.c
new file mode 100644
index 00000000..a7b08b8e
--- /dev/null
+++ b/other/pamvalidate.c
@@ -0,0 +1,86 @@
+/*=============================================================================
+                               pamvalidate
+===============================================================================
+  Part of the Netpbm package.
+
+  Copy PAM and PNM (i.e. PBM, PGM, or PPM) images from Standard Input
+  to Standard Output.  No output when input is invalid.
+
+  Contributed to the public domain by its author.
+=============================================================================*/
+
+#include <string.h>
+#include "pm_c_util.h"
+#include "pam.h"
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+    FILE * const tmpfile = pm_tmpfile();
+    int        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");
+
+    for (eof = FALSE; !eof; ) {
+        pnm_readpaminit(stdin, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+        outpam = inpam;  /* initial value */
+        outpam.file = tmpfile;
+
+        pnm_writepaminit(&outpam);
+
+        if (PNM_FORMAT_TYPE(inpam.format) == PBM_TYPE) {
+            /* Fast method for PBM */
+            unsigned char * const inrow = pbm_allocrow_packed(inpam.width);
+
+            unsigned int row;
+
+            for (row = 0; row < inpam.height; ++row) {
+                pbm_readpbmrow_packed(inpam.file, inrow, inpam.width,
+                                      inpam.format);
+                pbm_writepbmrow_packed(tmpfile, inrow, inpam.width, 0);
+            }
+            pbm_freerow(inrow);
+
+        } else {
+            /* General case.  Logic works for PBM */
+            tuple * const 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);
+    }
+
+    fseek(tmpfile, 0, SEEK_SET);
+
+    while (!feof(tmpfile) && !ferror(tmpfile) && !ferror(stdout)) {
+        char buffer[4096];
+        size_t bytesReadCt;
+
+        bytesReadCt = fread(buffer, 1, sizeof(buffer), tmpfile);
+
+        if (ferror(tmpfile))
+            pm_error("Error reading from temporary file.  "
+                     "Incomplete output.  "    
+                     "Errno = %s (%d)", strerror(errno), errno);
+        else
+            fwrite(buffer, 1, bytesReadCt, stdout);
+    }
+    pm_close(tmpfile);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/other/pamx/Makefile b/other/pamx/Makefile
index a40ea3a6..4e06e0fd 100644
--- a/other/pamx/Makefile
+++ b/other/pamx/Makefile
@@ -8,30 +8,37 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 include $(BUILDDIR)/config.mk
 
 EXTERN_INCLUDE =
-ifneq ($(X11LIB),NONE)
-  ifneq ($(X11HDR_DIR),)
-    EXTERN_INCLUDES += -I$(X11HDR_DIR)
+
+ifeq ($(shell pkg-config x11 --modversion --silence-errors),)
+  # Pkg-config has never heard of X11, or doesn't even exist
+
+  ifneq ($(X11LIB),NONE)
+    HAVE_X11LIB = Y
+    ifneq ($(X11HDR_DIR)x,x)
+      EXTERN_INCLUDES += -I$(X11HDR_DIR)
+    endif
   endif
+else
+  HAVE_X11LIB = Y
+  X11LIB = $(shell pkg-config x11 --libs)
+  EXTERN_INCLUDES += $(shell pkg-config x11 --cflags)
 endif
 
-ifneq ($(X11LIB),NONE)
-  BINARIES += pamx
-
-  PAMX_OBJECTS = \
-	  pamx.o \
-	  image.o \
-	  send.o \
-	  window.o \
+ifeq ($(HAVE_X11LIB),Y)
+  PORTBINARIES += pamx
 
-  MERGE_OBJECTS = \
-	  pamx.o2 \
+  EXTRA_OBJECTS = \
 	  image.o \
 	  send.o \
 	  window.o \
 
 endif
 
-OBJECTS = $(PAMX_OBJECTS)
+BINARIES = $(PORTBINARIES)
+
+OBJECTS = $(BINARIES:%=%.o) $(EXTRA_OBJECTS)
+
+MERGE_OBJECTS = $(BINARIES:%=%.o2) $(EXTRA_OBJECTS)
 
 MERGEBINARIES = $(BINARIES)
 
@@ -39,7 +46,12 @@ all: $(BINARIES)
 
 include $(SRCDIR)/common.mk
 
-pamx: $(PAMX_OBJECTS) $(NETPBMLIB) $(LIBOPT)
-	$(LD) -o $@ $(PAMX_OBJECTS) \
-	  $(shell $(LIBOPT) $(NETPBMLIB) $(X11LIB)) \
-	  $(LDFLAGS) $(LDLIBS) $(MATHLIB) $(RPATH) $(LADD)
+ifeq ($(shell pkg-config x11 --libs),)
+  X11_LIBOPTS = $(shell $(LIBOPT) $(LIBOPTR) $(X11LIB))
+else
+  X11_LIBOPTS = $(shell pkg-config x11 --libs)
+endif
+
+pamx: image.o send.o window.o
+pamx: ADDL_OBJECTS = image.o send.o window.o
+pamx: LDFLAGS_TARGET = $(X11_LIBOPTS)
diff --git a/other/pamx/pamx.c b/other/pamx/pamx.c
index 130cc64c..e22693ea 100644
--- a/other/pamx/pamx.c
+++ b/other/pamx/pamx.c
@@ -3,6 +3,7 @@
    Copyright information is in the file COPYRIGHT
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <signal.h>
 #include <unistd.h>
@@ -65,7 +66,7 @@ parseCommandLine(int argc,
    was passed to us as the argv array.  We also trash *argv.
 --------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options. */
+        /* Instructions to pm_optParseOptions3 on how to parse our options. */
     optStruct3 opt;
   
     unsigned int option_def_index;
@@ -110,7 +111,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);
+    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (geometrySpec) {
@@ -360,7 +361,7 @@ main(int     argc,
     destroyViewer(viewerP);
 
     if (title)
-        strfree(title);
+        pm_strfree(title);
 
     XCloseDisplay(dispP);
 
diff --git a/other/pamx/send.c b/other/pamx/send.c
index 5413308b..fd50d5e9 100644
--- a/other/pamx/send.c
+++ b/other/pamx/send.c
@@ -78,8 +78,8 @@ ximageToPixmap(Display *    const disp,
 
 
 
-/* find the best pixmap depth supported by the server for a particular
- * visual and return that depth.
+/* find the best pixmap depth the server can do for a particular visual and
+ * return that depth.
  *
  * this is complicated by R3's lack of XListPixmapFormats so we fake it
  * by looking at the structure ourselves.
@@ -587,7 +587,7 @@ makeXImage(XImageInfo * const ximageinfoP,
         MALLOCARRAY(data, byteCount);
         if (data == NULL)
             pm_error("Can't allocate space for %u byte image", byteCount);
-        bcopy(imageP->data, data, byteCount);
+        memcpy(data, imageP->data, byteCount);
 
         ximageinfoP->ximageP =
             XCreateImage(disp, visualP, 1, XYBitmap,
diff --git a/other/pamx/window.c b/other/pamx/window.c
index 2eb48241..e2de1577 100644
--- a/other/pamx/window.c
+++ b/other/pamx/window.c
@@ -6,6 +6,7 @@
    See COPYRIGHT file for copyright information.
 */
 
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
 
 #include <assert.h>
@@ -112,11 +113,11 @@ getInitialViewerGeometry(const char *   const geometryString,
         *userChoseP = TRUE;
     } else if (geometryString) {
         const char * defGeom;
-        asprintfN(&defGeom, "%ux%u+0+0", defaultWidth, defaultHeight);
+        pm_asprintf(&defGeom, "%ux%u+0+0", defaultWidth, defaultHeight);
         XGeometry(dispP, scrn, geometryString, defGeom, 0, 1, 1, 0, 0,
                   (int *)xposP, (int *)yposP,
                   (int *)widthP, (int *)heightP);
-        strfree(defGeom);
+        pm_strfree(defGeom);
         *userChoseP = TRUE;
     } else {
         *widthP     = defaultWidth;
@@ -193,7 +194,7 @@ determineRepaintStrategy(viewer  *    const viewerP,
                         
     /* Decide how we're going to handle repaints.  We have three modes:
        use backing-store, use background pixmap, and use exposures.
-       If the server supports backing-store, we enable it and use it.
+       If the server allows backing-store, we enable it and use it.
        This really helps servers which are memory constrained.  If the
        server does not have backing-store, we try to send the image to
        a pixmap and use that as backing-store.  If that fails, we use
@@ -499,10 +500,10 @@ iconName(const char * const s) {
         char * t;
 
         STRSCPY(buf, s);
-        /* strip off stuff following 1st word.  This strips
+        /* chop off stuff following 1st word.  This strips
            info added by processing functions too.
         */
-        t = index(buf, ' ');
+        t = strchr(buf, ' ');
         if (t)
             *t = '\0';
     
@@ -510,15 +511,15 @@ iconName(const char * const s) {
            You might want to change this.
         */
     
-        t= rindex(buf, '/');
+        t= strrchr(buf, '/');
         if (t) {
             char * p;
             for (p = buf, ++t; *t; ++p, ++t)
                 *p = *t;
             *p = '\0';
         }
-        /* look for an extension and strip it off */
-        t = index(buf, '.');
+        /* Chop off any filename extension */
+        t = strchr(buf, '.');
         if (t)
             *t = '\0';
     }
@@ -652,9 +653,9 @@ bestVisual(Display *      const disp,
     Visual * visualP;
     Visual * default_visualP;
 
-    /* Figure out the best depth the server supports.  note that some servers
-       (such as the HP 11.3 server) actually say they support some depths but
-       have no visuals that support that depth.  Seems silly to me ...
+    /* Figure out the best depth the server allows.  note that some servers
+       (such as the HP 11.3 server) actually say they allow some depths but
+       have no visuals that allow that depth.  Seems silly to me ...
     */
 
     depth = 0;
@@ -1079,7 +1080,7 @@ retvalueFromExitReason(exitReason const exitReason) {
     switch (exitReason) {
     case EXIT_NONE:      assert(false); break;
     case EXIT_QUIT:      retval =  0;     break;
-    case EXIT_WM_KILL:   retval = 10;     break;
+    case EXIT_WM_KILL:   retval =  0;     break;
     case EXIT_DESTROYED: retval = 20;     break;
     }
     return retval;
@@ -1175,7 +1176,7 @@ displayInViewer(viewer *       const viewerP,
     {
         const char * const name = iconName(title);
         XSetIconName(viewerP->dispP, viewerP->viewportWin, name);
-        strfree(name);
+        pm_strfree(name);
     }
     setNormalSizeHints(viewerP, imageP);
 
diff --git a/other/pnmcolormap.c b/other/pnmcolormap.c
index 42b03063..57db4329 100644
--- a/other/pnmcolormap.c
+++ b/other/pnmcolormap.c
@@ -77,7 +77,7 @@ parseCommandLine (int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def;
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -110,7 +110,7 @@ parseCommandLine (int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
 
@@ -655,7 +655,7 @@ addImageColorsToHash(struct pam *   const pamP,
         pnm_readpamrow(pamP, tuplerow);
 
         for (col = 0; col < pamP->width; ++col) {
-            bool firstOccurrence;
+            int firstOccurrence;
 
             pnm_addtuplefreqoccurrence(pamP, tuplerow[col], tuplehash,
                                        &firstOccurrence);
@@ -688,7 +688,7 @@ computeHistogram(FILE *         const ifP,
     struct pam firstPam;
     tuplehash tuplehash;
     unsigned int colorCount;
-    bool eof;
+    int eof;
     
     pm_message("making histogram...");
 
diff --git a/other/ppmdcfont.c b/other/ppmdcfont.c
index 701277a9..130b6383 100644
--- a/other/ppmdcfont.c
+++ b/other/ppmdcfont.c
@@ -73,14 +73,14 @@ generateCommandTables(const struct ppmd_font * const fontP,
         if (fontP->glyphTable[relativeCodePoint].header.commandCount > 0) {
             const char * commandTableVariableName;
 
-            asprintfN(&commandTableVariableName, "%s_cmd_%u",
-                      glyphTableVariableName,
-                      fontP->header.firstCodePoint + relativeCodePoint);
+            pm_asprintf(&commandTableVariableName, "%s_cmd_%u",
+                        glyphTableVariableName,
+                        fontP->header.firstCodePoint + relativeCodePoint);
             
             generateCommandTable(fontP->glyphTable[relativeCodePoint],
                                  commandTableVariableName);
 
-            strfree(commandTableVariableName);
+            pm_strfree(commandTableVariableName);
 
             fprintf(stdout, "};\n");
             fprintf(stdout, "\n");
@@ -130,14 +130,14 @@ generateGlyphTable(const struct ppmd_font * const fontP,
 
         const char * commandTableVariableName;
 
-        asprintfN(&commandTableVariableName, "%s_cmd_%u",
-                  variableName,
-                  fontP->header.firstCodePoint + relativeCodePoint);
+        pm_asprintf(&commandTableVariableName, "%s_cmd_%u",
+                    variableName,
+                    fontP->header.firstCodePoint + relativeCodePoint);
         
         generateGlyph(fontP->glyphTable[relativeCodePoint],
                       commandTableVariableName);
 
-        strfree(commandTableVariableName);
+        pm_strfree(commandTableVariableName);
 
         if (relativeCodePoint < fontP->header.characterCount - 1)
             fprintf(stdout, "  ,\n");
@@ -184,7 +184,7 @@ main(int argc, char **argv) {
 
     fprintf(stdout, "#include \"ppmdfont.h\"\n\n");
 
-    asprintfN(&glyphTableVariableName, "%s_glyphTable", fontVariableName);
+    pm_asprintf(&glyphTableVariableName, "%s_glyphTable", fontVariableName);
 
     generateGlyphTable(fontP, glyphTableVariableName);
 
@@ -192,7 +192,7 @@ main(int argc, char **argv) {
         
     generateFont(fontP, fontVariableName, glyphTableVariableName);
 
-    strfree(glyphTableVariableName);
+    pm_strfree(glyphTableVariableName);
 
     ppmd_free_font(fontP);
     
diff --git a/other/ppmsvgalib.c b/other/ppmsvgalib.c
index 5bcabdc1..c5700992 100644
--- a/other/ppmsvgalib.c
+++ b/other/ppmsvgalib.c
@@ -46,7 +46,7 @@ parseCommandLine (int argc, char ** argv,
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
     optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-        /* Instructions to optParseOptions3 on how to parse our options.
+        /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
@@ -64,7 +64,7 @@ parseCommandLine (int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (!modeSpec)
@@ -253,10 +253,6 @@ main(int argc, char *argv[]) {
 
     ppm_init(&argc, argv);
 
-    rc = vga_init();         /* Initialize. */
-    if (rc < 0)
-        pm_error("Svgalib unable to allocate a virtual console.");
-
     parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.inputFilespec);
@@ -269,6 +265,14 @@ main(int argc, char *argv[]) {
                   &checkResult);
     }
 
+    /* We wait to initialize Svgalib to prevent it from interfering with
+       error messages the code above might issue, and leaving the console
+       in an undesirable state if the above code aborts the program.
+    */
+    rc = vga_init();         /* Initialize. */
+    if (rc < 0)
+        pm_error("Svgalib unable to allocate a virtual console.");
+
     if (vga_hasmode(cmdline.mode))
         display(ifP, cols, rows, maxval, format, 
                 cmdline.mode, cmdline.verbose);
diff --git a/pm_config.in.h b/pm_config.in.h
index 51a7b966..fe8b8008 100644
--- a/pm_config.in.h
+++ b/pm_config.in.h
@@ -15,7 +15,7 @@
 
 **************************************************************************/
 
-#if defined(USG) || defined(SVR4) || defined(VMS) || defined(__SVR4)
+#if defined(USG) || defined(SVR4) || defined(__SVR4)
 #define SYSV
 #endif
 #if !( defined(BSD) || defined(SYSV) || defined(MSDOS) || defined(__amigaos__))
@@ -54,45 +54,29 @@
 #endif
 
 
-/* CONFIGURE: If you have an X11-style rgb color names file, define its
-** path here.  This is used by PPM to parse color names into rgb values.
-** If you don't have such a file, comment this out and use the alternative
-** hex and decimal forms to specify colors (see ppm/pgmtoppm.1 for details).  */
-
-#define RGB_DB_PATH \
-"/usr/share/netpbm/rgb.txt:" \
-"/usr/lib/X11/rgb.txt:" \
-"/usr/share/X11/rgb.txt:" \
-"/usr/X11R6/lib/X11/rgb.txt"
-
 /* CONFIGURE: This is the name of an environment variable that tells
 ** where the color names database is.  If the environment variable isn't
-** set, Netpbm tries the hardcoded defaults set above.
+** set, Netpbm tries the hardcoded defaults per macro 'RGB_DB_PATH'
+** (see below).
 */
 #define RGBENV "RGBDEF"    /* name of env-var */
 
+/* CONFIGURE: There should be an environment variable telling where the color
+** names database (color dictionary) is for Netpbm to use, e.g. to determine
+** what colord name "Salmon" is.  The name of that environment variable is
+** above.  But as some people prefer hardcoded file paths to environment
+** variables, if such environment variable is not set, Netpbm looks for the
+** first existing file in the list which is the value of 'RGB_DB_PATH'.  And
+** if none of those exist (including if the list is empty), Netpbm simply
+** doesn't understand any color names.  Note that Netpbm comes with a color
+** database (lib/rgb.txt in the source tree), but you might choose to have
+** Netpbm use a different one.  See the documentation of ppm_parsecolor()
+** for the format of the color database file.
+*/
+
 #if (defined(SYSV) || defined(__amigaos__))
 
 #include <string.h>
-/* Before Netpbm 9.1, rand and srand were macros for random and
-   srandom here.  This caused a failure on a SunOS 5.6 system, which
-   is SYSV, but has both rand and random declared (with different
-   return types).  The macro caused the prototype for random to be a
-   second prototype for rand.  Before 9.1, Netpbm programs called
-   random() and on a SVID system, that was really a call to rand().
-   We assume all modern systems have rand() itself, so now Netpbm
-   always calls rand() and if we find a platform that doesn't have
-   rand(), we will add something here for that platform.  -Bryan 00.04.26
-#define random rand
-#define srandom(s) srand(s)
-extern void srand();
-extern int rand();
-*/
-/* Before Netpbm 9.15, there were macro definitions of index() and 
-   rindex() here, but there are no longer any invocations of those 
-   functions in Netpbm, except in the VMS-only code, so there's no
-   reason for them.
-*/
 
 #ifndef __SASC
 #ifndef _DCC    /* Amiga DICE Compiler */
@@ -115,15 +99,22 @@ extern int rand();
 #ifdef BSD
 #include <stdlib.h>
 #endif
-#if (defined(SYSV) && !defined(VMS))
+#if defined(SYSV)
 #include <malloc.h>
 #endif
 
-/* CONFIGURE: If your system has the setmode() function, set HAVE_SETMODE.
-** If you do, and also the O_BINARY file mode, pm_init() will set the mode
-** of stdin and stdout to binary for all Netpbm programs.
-** You need this with Cygwin (Windows).
-*/
+/* MSVCRT means we're using the Microsoft Visual C++ runtime library.
+
+   _WIN32, set by the compiler, apparently means the same thing; we see it set
+   in compiles using the Microsoft Visual C++ development environment and also
+   with Mingw, which is the Windows version of the GNU compiler (which brings
+   with it a runtime library which wraps around the Microsoft one).  We don't
+   see it set in Cygwin compiles, which use GNU libraries instead of the
+   Microsoft one.
+
+   There is also _MSC_VER, which is set by MSVC to the version number of the
+   MSVC runtime library and __MINGW32__.
+ */
 
 #ifdef _WIN32
 #define MSVCRT 1
@@ -131,18 +122,42 @@ extern int rand();
 #define MSVCRT 0
 #endif
 
+/* WIN32 is a macro that some older compilers predefine (compilers aren't
+   supposed to because it doesn't start with an underscore, hence the change.
+   Many build systems (project files, etc.) set WIN32 explicitly for
+   backward compatibility.  Netpbm doesn't use it.
+*/
+
+/* CONFIGURE: If your system has the setmode() function, set HAVE_SETMODE.
+** If you do, and also the O_BINARY file mode, pm_init() will set the mode
+** of stdin and stdout to binary for all Netpbm programs.
+** You need this with Cygwin (Windows).
+*/
 #if MSVCRT || defined(__CYGWIN__) || defined(DJGPP)
 #define HAVE_SETMODE
 #endif
 
-/* #define HAVE_SETMODE */
+#if MSVCRT || defined(__CYGWIN__) || defined(DJGPP)
+#define HAVE_IO_H 1
+#else
+#define HAVE_IO_H 0
+#endif
 
-#if (defined(__GLIBC__) || defined(__GNU_LIBRARY__) || defined(__APPLE__))
+#if (defined(__GLIBC__) || defined(__GNU_LIBRARY__) || defined(__APPLE__)) || defined(__NetBSD__)
   #define HAVE_VASPRINTF 1
 #else
   #define HAVE_VASPRINTF 0
 #endif
 
+/* On Windows, unlinking a file is deleting it, and you can't delete an open
+   file, so unlink of an open file fails.  The errno is (incorrectly) EACCES.
+*/
+#if MSVCRT || defined(__CYGWIN__) || defined(DJGPP)
+  #define CAN_UNLINK_OPEN 0
+#else
+  #define CAN_UNLINK_OPEN 1
+#endif
+
 #ifdef __amigaos__
 #include <clib/exec_protos.h>
 #define getpid() ((pid_t)FindTask(NULL))
@@ -150,7 +165,7 @@ extern int rand();
 
 #ifdef DJGPP
 #define lstat stat
-#endif /* DJGPP */
+#endif
 
 /*  CONFIGURE: Netpbm uses __inline__ to declare functions that should
     be compiled as inline code.  GNU C recognizes the __inline__ keyword.
@@ -185,38 +200,25 @@ extern int rand();
   #endif
 #endif
 
-/* CONFIGURE: GNUC extensions are used in performance critical places
+/* CONFIGURE: GNU Compiler extensions are used in performance critical places
    when available.  Test whether they exist.
 
-   Turn off by defining NO_GCC_BUILTINS.
+   Prevent the build from exploiting these extensions by defining
+   NO_GCC_UNIQUE.
 
-   Note that though these influence the code produced, the compiler
-   setting ultimately decides what operands are used.  If you
-   want a generic build, check the manual and adjust CFLAGS in
-   config.mk accordingly.
-
-   For example, if you want binaries that run on all Intel x86-32
-   family CPUs back to 80386, adding "-march=i386" to CFLAGS in
-   config.mk is much better than setting NO_GCC_BUILTINS to 1.
-   If you want to be extra sure use:
-   "-march=i386 -mno-mmx -mno-sse -DNO_GCC_BUILTINS"
+   Before Netpbm 10.65 (December 2013), Netpbm used GCC compiler extensions
+   to generate SSE code in Pamflip.  Starting in 10.65, Netpbm instead uses
+   the more standard operators defined in <emmtrins.h>.  To prevent Netpbm
+   from explicitly using any SSE instructions, set WANT_SSE to N in
+   config.mk.
 */
 
-#if defined(__GNUC__) && !defined(NO_GCC_BUILTINS)
+#if defined(__GNUC__) && !defined(NO_GCC_UNIQUE)
   #define GCCVERSION __GNUC__*100 + __GNUC_MINOR__
 #else
   #define GCCVERSION 0
 #endif
 
-#ifndef HAVE_GCC_MMXSSE
-#if GCCVERSION >=301 && defined(__MMX__) && defined(__SSE__)
-  #define HAVE_GCC_MMXSSE 1
-  /* Use GCC builtins to directly access MMX/SSE features */ 
-#else
-  #define HAVE_GCC_MMXSSE 0
-#endif
-#endif
-
 #ifndef HAVE_GCC_BITCOUNT
 #if GCCVERSION >=304
   #define HAVE_GCC_BITCOUNT 1
@@ -228,10 +230,9 @@ extern int rand();
 #endif
 
 #ifndef HAVE_GCC_BSWAP
-#if GCCVERSION >=403
+#if GCCVERSION >=403 || defined(__clang__)
   #define HAVE_GCC_BSWAP 1
   /* Use __builtin_bswap32(), __builtin_bswap64() for endian conversion.
-     Available from GCC v 4.3 onward.
      NOTE: On intel CPUs this may produce the bswap operand which is not
      available on 80386. */
 #else
@@ -239,6 +240,35 @@ extern int rand();
 #endif
 #endif
 
+#ifndef HAVE_WORKING_SSE2
+#if defined(__SSE2__) && ( GCCVERSION >=402 || defined(__clang__) )
+  #define HAVE_WORKING_SSE2 1
+  /* We can use SSE2 builtin functions to exploit SSE2 instructions.  GCC
+     version 4.2 or newer is required; older GCC ostensibly has these SSE2
+     builtins, but the compiler aborts with an error.  Note that __SSE2__
+     means not only that the compiler has the capability, but that the user
+     has not disabled it via compiler options.
+  */
+#else
+  #define HAVE_WORKING_SSE2 0
+#endif
+#endif
+
+/* UNALIGNED_OK means it's OK to do unaligned memory access, e.g.
+   loading an 8-byte word from an address that is not a multiple of 8.
+   On some systems, such an access causes a trap and a signal.
+
+   This determination is conservative - There may be cases where unaligned
+   access is OK and we say here it isn't.
+
+   We know unaligned access is _not_ OK on at least SPARC and some ARM.
+*/
+
+#if defined(__x86_64__) | defined(__i486__) | defined(__vax__)
+# define UNALIGNED_OK 1
+#else
+# define UNALIGNED_OK 0
+#endif
 
 
 /* CONFIGURE: Some systems seem to need more than standard program linkage
@@ -300,9 +330,14 @@ typedef long int pm_filepos;
 typedef int qsort_comparison_fn(const void *, const void *);
     /* A compare function to pass to <stdlib.h>'s qsort() */
 
-#if defined(WIN32) && !defined(__CYGWIN__)
+#if MSVCRT
   #define pm_mkdir(dir, perm) _mkdir(dir)
 #else
   #define pm_mkdir(dir, perm) mkdir(dir, perm) 
 #endif
 
+#if MSVCRT
+  #define pm_pipe _pipe
+#else
+  #define pm_pipe pipe
+#endif
diff --git a/test/411toppm.ok b/test/411toppm.ok
new file mode 100644
index 00000000..615d2b6d
--- /dev/null
+++ b/test/411toppm.ok
@@ -0,0 +1 @@
+240376509 9229
diff --git a/test/411toppm.test b/test/411toppm.test
new file mode 100755
index 00000000..98170c67
--- /dev/null
+++ b/test/411toppm.test
@@ -0,0 +1,13 @@
+#! /bin/bash
+# This script tests: 411toppm
+# Also requires:
+
+
+# Test 1. should produce: 240376509 9229
+# The above value is what 411toppm has been always producing.
+# 411toppm's author Steve Allen says he was not able to obtain accurate
+# specifications for the .411 format.  Technically, the above can change.
+#
+# See comment at head of source file 411toppm.c.
+
+head -c 4608 /dev/zero | 411toppm -quiet | cksum
diff --git a/test/BLOCK b/test/BLOCK
new file mode 100644
index 00000000..f928dc09
--- /dev/null
+++ b/test/BLOCK
@@ -0,0 +1,4 @@
+#! /bin/sh
+
+echo `basename $0` : command not found in test path 1>&2
+exit 88
diff --git a/test/Execute-Tests b/test/Execute-Tests
new file mode 100755
index 00000000..78091e6b
--- /dev/null
+++ b/test/Execute-Tests
@@ -0,0 +1,308 @@
+#! /bin/bash
+
+# See if PBM_TEST_PATH is set.
+# PBM_TEST_PATH is the list of directories with the Netpbm programs
+# you want to test.
+#
+# (1) check-tree: set to a long list of directories which contain
+# the relevant executables.
+#
+# (2) check-package: set to the [package]/bin directory.
+# 
+# (3) check-install: empty string.  Executables will be sought from
+# the default execution path ($PATH).
+#
+# You can set it here by de-commenting and modifying the next line:
+#export PBM_TEST_PATH="/usr/local/bin/"
+
+case ${CHECK_TYPE} in
+tree)
+    echo
+    echo "Checking programs in source tree" ;;
+package)  if [ -z $PBM_TEST_PATH ]
+  then
+    echo "Error: PBM_TEST_PATH is not set."
+    exit 1
+  elif [ ! -d $PBM_TEST_PATH ]
+    then
+    echo
+    echo "Error: No directory named $PBM_TEST_PATH."
+    echo
+    echo "You must run \"make package\" before this test."
+    echo
+    echo "If you specified the package directory for \"make package\""
+    echo "you must do the same for \"make check\"."
+    echo
+  exit 1
+  fi ;;
+install)
+  echo
+  echo "Programs in the default execution path:"
+  echo $PATH
+  echo "will be tested." ;;
+*)
+  echo "Invalid test type: ${CHECK_TYPE}"
+  exit 1 ;;
+esac
+
+# Set srcdir, which is the directory which contains Execute-Tests (this
+# script), programs that run the test, including *.test and helpers that they
+# invoke, the list of tests to run ('Test-Order'), and *.ok files that
+# indicate the expected results of tests.
+
+srcdir=$(dirname $0)
+
+# Set tmpdir, which is used in some of the test scripts.  By default
+# this is created by mktemp.  The user can override and specify tmpdir,
+# but in this case it must be an existing directory and must not be
+# either $srcdir or current work.
+
+if [ -z $tmpdir ]
+  then
+    tmpdir_created=$(mktemp -d "${TMPDIR:-/tmp}/netpbm.XXXXXXXX") || exit 1;
+  export tmpdir=${tmpdir_created}
+  else
+  tmpdir_created="";
+  if [ ! -d ${tmpdir} ]
+     then echo "Specified temporary directory $tmpdir does not exist."
+     exit 1;
+  elif [ ${tmpdir} -ef ${srcdir} ]
+     then echo "Temporary directory must not be $srcdir."
+     exit 1;
+  elif [ ${tmpdir} -ef $PWD ]
+     then echo "Temporary directory must not be current directory."
+     exit 1;
+  fi
+fi
+
+# If necessary set the RGBDEF environment variable.
+#export RGBDEF=/etc/rgb.txt
+#export RGBDEF=/usr/local/netpbm/lib/rgb.txt
+#export RGBDEF=/usr/share/emacs/*/etc/rgb.txt
+
+
+# Declare arrays used to count and report test results.
+# "UNEXPECTED SUCCESS" and "EXPECTED FAILURE" are not used now;
+# they are reserved for future expansion.
+declare -a array=(0 0 0 0 0)
+
+# Older versions of bash get confused when array elements contain
+# spaces.
+
+status[0]="SUCCESS"
+status[1]="FAILURE"
+status[2]="UNEXPECTED SUCCESS"
+status[3]="EXPECTED FAILURE"
+status[4]="NOT TESTABLE"
+
+
+
+# Copy test image files to the current work directory
+
+if [ ! -f ./testgrid.pbm ]
+  then cp -v ${srcdir}/testgrid.pbm ./testgrid.pbm
+fi
+
+if [ ! -f ./testimg.ppm ]
+  then cp -v ${srcdir}/testimg.ppm  ./testimg.ppm 
+fi
+
+# The block-bin directory
+#
+# This directory contains dummy executables with the names of the
+# Netpbm programs that are to be tested.  These dummy executables
+# exist to prevent execution of programs (typically from a previous
+# installation of Netpbm) in the default path during the tests.
+# They report error when accessed and nothing else.
+# The directory is placed in PATH between PBM_TEST_PATH and
+# default PATH.
+
+# Create block-bin.  If it already exists, erase and create anew.
+
+if [ ! -z $PBM_TEST_PATH ]
+  then
+  blockbin=$PWD/block-bin
+  if [ -d  $blockbin ]
+    then rm -rf $blockbin
+  fi
+  mkdir $blockbin
+  cp ${srcdir}/BLOCK $blockbin/BLOCK
+  chmod +x $blockbin/BLOCK
+
+# Populate the block-bin directory using all-in-place.ok and
+# legacy-names.ok which together make a complete list of programs.
+
+  sed 's/: ok$//' \
+       ${srcdir}/all-in-place.ok ${srcdir}/legacy-names.ok | \
+     tr ' ' '\n' | while read prog
+    do
+    ln -s $blockbin/BLOCK $blockbin/$prog
+    done
+
+  testpath=$PBM_TEST_PATH:$blockbin:$PATH:$BUILDDIR/test
+else
+# We don't need block-bin when testing after installation.
+  testpath=$PATH:$BUILDDIR/test
+fi
+
+
+
+# Execute the tests, as described in the "Test-Order" file.
+#
+# Each test outputs a ".out" file, which is compared against a
+# corresponding ".ok" file.  For example the output from "pbmmake.test"
+# is "pbmmake.out" and when this matches "pbmmake.ok" we declare the
+# test a success.
+# In the error case the ".out" file is retained in the current work
+# directory.
+#
+# All tests are self-contained.
+#
+# By default the tests are executed in the order described in the
+# file Test-Order.  Copy this file from the source directory
+# to the work directory.
+
+
+# Provision for running programs under valgrind.
+# Note that valgrind tests consume time.
+#
+# Output from valgrind must be redirected in some manner because some
+# tests examine standard error (fd2) output.  Here we use --log-file.
+# (See below)
+
+if [ -z $VALGRIND_TESTS ]
+  then VALGRIND_TESTS="off";
+elif [ $VALGRIND_TESTS = "on" ]
+  then
+  valdir=$PWD/valgrind;
+  mkdir $valdir
+ 
+  vg_command_base="valgrind --trace-children=yes";
+
+  for i in awk cat cksum cmp cp cut date dirname egrep fgrep file grep gs \
+    head mkdir mktemp perl rm sed seq sh tee testrandom tr uniq
+
+    # Tell valgrind not to probe execution of the above programs.
+
+    do vg_skip=$vg_skip"/*/"$i","; done;
+
+  vg_command_base=$vg_command_base" --trace-children-skip="$vg_skip;
+  #
+  # If using an older version of valgrind (< v.3.6) that does not
+  # support --trace-children-skip=... , comment out the above line
+  # and let valgrind probe execution of all programs listed above.
+  # This greatly increases execution time.
+
+fi
+
+
+# Provision for running only chosen tests.
+# The string "target" is a comma-separated list of target programs.
+# When set only tests for programs in the list will be run.
+# 
+# The --no-clobber version comes useful when the user wants a modified
+# (pared-down) version of Test-Order.
+
+if [ ! -z $target ]
+  then
+  echo $target | sed 's/,/\n/g' | \
+                 sed -e 's/^/# This script tests: .*\\</' -e 's/$/\\>/' \
+                     -e '/^$/q' | \
+                 grep -f - ${srcdir}/*.test -l | \
+                   while read i ; do echo ${i##*/} ; done | \
+                 grep -f - ${srcdir}/Test-Order > ./Test-Order ;
+  if [ ! -s ./Test-Order ]
+    then echo;
+         echo "Error: No testable program names in target: "$target;
+         echo; exit 1
+  fi
+else 
+       cp ${srcdir}/Test-Order ./Test-Order ;
+       #cp --no-clobber ${srcdir}/Test-Order ./Test-Order ;
+fi
+
+echo
+
+for tname in `grep -v "^#" ./Test-Order | fgrep ".test"`
+do
+echo == $tname ==
+
+# If running tests under valgrind, complete vg_command by prepending
+# the valgrind output file, which is test-specific.
+  
+if [ $VALGRIND_TESTS = "on" ]
+  then
+  vg_command="$vg_command_base --log-file=${valdir}/${tname%.test}.%p.vout"
+fi
+
+# Execute a single test and test its result.
+
+PATH=${testpath} $vg_command ${srcdir}/$tname > ${tname%.test}.out;
+let result=$?
+case $result in
+0)   cmp -s ${tname%.test}.out ${srcdir}/${tname%.test}.ok ;
+     if [ $? -eq 0 ]
+        then let result=0;  rm  ${tname%.test}.out ;
+        else let result=1;
+             grep "^##" ${srcdir}/$tname  # Print failure message.
+     fi ;;
+80) let result=4 ;;
+*)  let result=1 ;;
+esac
+
+# Report whether a single test succeeded or failed.
+# Increment counters.
+
+echo $tname: ${status[${result}]}; echo
+let array[${result}]=${array[${result}]}+1
+let total_scripts=${total_scripts}+1
+
+done
+
+# Erase temporary bin directory and its contents.
+
+rm -rf ${srcdir}/bin
+
+
+# Erase temporary directory and its contents, if it was created.
+
+if [ -n $tmpdir_created ]
+    then rm -rf $tmpdir_created
+fi
+
+
+# Erase test image files in the current (work) directory.
+# (Do not erase them if we are working from the source directory.)
+
+if [ ! $PWD -ef ${srcdir} ]
+    then rm ./testimg.ppm ./testgrid.pbm
+fi
+
+
+# Calculate success / failure totals and print a summary report.
+# Report date and time of completion.
+
+echo "Test summary:"
+echo ==================
+
+for s in 0 1 2 3 4
+  do
+    if [[ ${array[${s}]} -gt 0 || s -eq 1 ]]
+    then echo ${status[${s}]} ${array[${s}]}
+    fi
+  done
+
+let total_testable=${total_scripts}-${array[4]}
+echo "TOTAL TESTABLE" $total_testable
+
+echo ==================
+echo "All tests done."
+date -u +"%a, %d %b %Y %H:%M:%S %z"
+
+
+# Exit with status 0 if all possible tests succeeded, 1 otherwise.
+
+if [[ ${array[0]} -eq ${total_testable} ]]
+then exit 0
+else exit 1
+fi
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 00000000..9b7ab047
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,28 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = test
+VPATH = .:$(SRCDIR)/$(SUBDIR)
+include $(BUILDDIR)/config.mk
+
+MERGE_OBJECTS =
+
+PROGS = testrandom
+
+all: $(PROGS)
+
+testrandom.o: testrandom.c
+	$(CC_FOR_BUILD) -c -o $@ $(CFLAGS_FOR_BUILD) $<
+
+testrandom: testrandom.o
+	$(LD_FOR_BUILD) -o $@ $(LDFLAGS_FOR_BUILD) $<
+
+
+OMIT_TEST_RULE = 1
+include $(SRCDIR)/common.mk
+
+distclean clean: cleanlocal
+.PHONY: cleanlocal
+cleanlocal:
+	rm -f $(PROGS)
diff --git a/test/Test-Order b/test/Test-Order
new file mode 100644
index 00000000..92efca05
--- /dev/null
+++ b/test/Test-Order
@@ -0,0 +1,155 @@
+# General test
+
+all-in-place.test
+legacy-names.test
+
+# Generator tests
+
+pbmmake.test
+pgmmake.test
+ppmmake.test
+pamseq.test
+
+pbmpage.test
+pbmtext.test
+pbmupc.test
+pgmramp.test
+ppmgauss.test
+ppmcie.test
+ppmwheel.test
+pamcrater.test
+
+# Generators with random components
+
+pgmnoise.test
+ppmpat.test
+ppmforge.test
+ppmrough.test
+
+# Analyzer tests
+
+pamfile.test
+pgmhist.test
+ppmhist.test
+pamsumm.test
+pnmpsnr.test
+pbmminkowski.test
+
+# Basic (internal) converter tests
+
+pamtopam.test
+pgmtopgm.test
+ppmtoppm.test
+pgmtoppm.test
+ppmtopgm.test
+pnmtopnm-plain.test
+
+# Editor tests
+
+pamditherbw.test
+
+pbmclean.test
+pamcut.test
+pnmcat.test
+pamflip1.test
+pamflip2.test
+pamenlarge.test
+pnminvert.test
+pamchannel.test
+ppmchange.test
+pambackground.test
+
+pbmpscale.test
+pnmremap1.test
+pnmremap2.test
+pnmtile.test
+ppmbrighten.test
+ppmdither.test
+ppmrelief.test
+pamedge.test
+ppmdim.test
+pnmshear.test
+pgmbentley.test
+
+ppmmix.test
+
+# Symmetry test
+
+symmetry.test
+
+# Format converter tests
+
+pbmtog3.test
+411toppm.test
+eyuvtoppm.test
+
+pbm-misc-converters.test
+
+# Miscellaneous utility tests
+
+ppmdfont.test
+
+# Round-trip tests : editors
+
+pnm-plain-roundtrip.test
+pnm-pam-roundtrip.test
+pnminvert-roundtrip.test
+pamflip-roundtrip.test
+pamdepth-roundtrip.test
+pad-crop-roundtrip.test
+cut-paste-roundtrip.test
+rgb3-roundtrip.test
+ppmchange-roundtrip.test
+pamdice-roundtrip.test
+pamslice-roundtrip.test
+lookup-roundtrip.test
+
+# Round-trip tests : lossless converters
+
+ppmtoarbtxt-roundtrip.test
+atari-roundtrip.test
+atk-roundtrip.test
+avs-roundtrip.test
+bmp-roundtrip.test
+cis-roundtrip.test
+cmuw-roundtrip.test
+facesaver-roundtrip.test
+fits-roundtrip.test
+g3-roundtrip.test
+gem-roundtrip.test
+gif-roundtrip.test
+gif-quant-roundtrip.test
+hdiff-roundtrip.test
+ilbm-roundtrip.test
+jbig-roundtrip.test
+leaf-roundtrip.test
+macp-roundtrip.test
+mda-roundtrip.test
+mgr-roundtrip.test
+mrf-roundtrip.test
+pcx-roundtrip.test
+pfm-roundtrip.test
+pi3-roundtrip.test
+pict-roundtrip.test
+png-roundtrip.test
+png-roundtrip2.test
+ps-roundtrip.test
+ps-alt-roundtrip.test
+sgi-roundtrip.test
+sbig-roundtrip.test
+st4-roundtrip.test
+sunicon-roundtrip.test
+sunrast-roundtrip.test
+targa-roundtrip.test
+tiff-roundtrip.test
+utahrle-roundtrip.test
+wbmp-roundtrip.test
+winicon-roundtrip.test
+xbm-roundtrip.test
+xpm-roundtrip.test
+xv-roundtrip.test
+xwd-roundtrip.test
+
+# Round-trip tests : lossy converter
+
+yuv-roundtrip.test
diff --git a/test/all-in-place.ok b/test/all-in-place.ok
new file mode 100644
index 00000000..6ba75993
--- /dev/null
+++ b/test/all-in-place.ok
@@ -0,0 +1,329 @@
+411toppm: ok
+asciitopgm: ok
+atktopbm: ok
+avstopam: ok
+bioradtopgm: ok
+bmptopnm: ok
+brushtopbm: ok
+cameratopam: ok
+cistopbm: ok
+cmuwmtopbm: ok
+ddbugtopbm: ok
+escp2topbm: ok
+eyuvtoppm: ok
+fitstopnm: ok
+fstopgm: ok
+g3topbm: ok
+gemtopnm: ok
+giftopnm: ok
+gouldtoppm: ok
+hdifftopam: ok
+hipstopgm: ok
+ilbmtoppm: ok
+imgtoppm: ok
+infotopam: ok
+jbigtopnm: ok
+jpeg2ktopam: ok
+jpegtopnm: ok
+leaftoppm: ok
+lispmtopgm: ok
+macptopbm: ok
+mdatopbm: ok
+mgrtopbm: ok
+mrftopbm: ok
+mtvtoppm: ok
+neotoppm: ok
+palmtopnm: ok
+pamaddnoise: ok
+pamarith: ok
+pambackground: ok
+pambayer: ok
+pamchannel: ok
+pamcomp: ok
+pamcrater: ok
+pamcut: ok
+pamdeinterlace: ok
+pamdepth: ok
+pamdice: ok
+pamditherbw: ok
+pamedge: ok
+pamendian: ok
+pamenlarge: ok
+pamexec: ok
+pamfile: ok
+pamfix: ok
+pamflip: ok
+pamfunc: ok
+pamgauss: ok
+pamgradient: ok
+pamlookup: ok
+pammasksharpen: ok
+pammixinterlace: ok
+pammosaicknit: ok
+pamoil: ok
+pampaintspill: ok
+pamperspective: ok
+pampick: ok
+pampop9: ok
+pamrecolor: ok
+pamrubber: ok
+pamscale: ok
+pamseq: ok
+pamshadedrelief: ok
+pamsharpmap: ok
+pamsharpness: ok
+pamsistoaglyph: ok
+pamslice: ok
+pamsplit: ok
+pamstack: ok
+pamstereogram: ok
+pamstretch: ok
+pamsumm: ok
+pamsummcol: ok
+pamthreshold: ok
+pamtilt: ok
+pamtoavs: ok
+pamtodjvurle: ok
+pamtofits: ok
+pamtogif: ok
+pamtohdiff: ok
+pamtohtmltbl: ok
+pamtojpeg2k: ok
+pamtompfont: ok
+pamtooctaveimg: ok
+pamtopam: ok
+pamtopdbimg: ok
+pamtopfm: ok
+pamtopng: ok
+pamtopnm: ok
+pamtosrf: ok
+pamtosvg: ok
+pamtotga: ok
+pamtotiff: ok
+pamtouil: ok
+pamtowinicon: ok
+pamtoxvmini: ok
+pamundice: ok
+pamunlookup: ok
+pamvalidate: ok
+pamwipeout: ok
+pamx: ok
+pbmclean: ok
+pbmlife: ok
+pbmmake: ok
+pbmmask: ok
+pbmminkowski: ok
+pbmpage: ok
+pbmpscale: ok
+pbmreduce: ok
+pbmtext: ok
+pbmtextps: ok
+pbmto10x: ok
+pbmto4425: ok
+pbmtoascii: ok
+pbmtoatk: ok
+pbmtobbnbg: ok
+pbmtocis: ok
+pbmtocmuwm: ok
+pbmtodjvurle: ok
+pbmtoepsi: ok
+pbmtoepson: ok
+pbmtoescp2: ok
+pbmtog3: ok
+pbmtogem: ok
+pbmtogo: ok
+pbmtoibm23xx: ok
+pbmtolj: ok
+pbmtoln03: ok
+pbmtolps: ok
+pbmtomacp: ok
+pbmtomatrixorbital: ok
+pbmtomda: ok
+pbmtomgr: ok
+pbmtomrf: ok
+pbmtonokia: ok
+pbmtopgm: ok
+pbmtopi3: ok
+pbmtopk: ok
+pbmtoplot: ok
+pbmtoppa: ok
+pbmtopsg3: ok
+pbmtoptx: ok
+pbmtosunicon: ok
+pbmtowbmp: ok
+pbmtoxbm: ok
+pbmtoybm: ok
+pbmtozinc: ok
+pbmupc: ok
+pc1toppm: ok
+pcxtoppm: ok
+pdbimgtopam: ok
+pfmtopam: ok
+pgmabel: ok
+pgmbentley: ok
+pgmdeshadow: ok
+pgmenhance: ok
+pgmhist: ok
+pgmkernel: ok
+pgmmake: ok
+pgmmedian: ok
+pgmminkowski: ok
+pgmmorphconv: ok
+pgmnoise: ok
+pgmramp: ok
+pgmtexture: ok
+pgmtofs: ok
+pgmtolispm: ok
+pgmtopbm: ok
+pgmtopgm: ok
+pgmtoppm: ok
+pgmtosbig: ok
+pgmtost4: ok
+pi1toppm: ok
+pi3topbm: ok
+picttoppm: ok
+pjtoppm: ok
+pktopbm: ok
+pngtopam: ok
+pnmalias: ok
+pnmcat: ok
+pnmcolormap: ok
+pnmconvol: ok
+pnmcrop: ok
+pnmgamma: ok
+pnmhisteq: ok
+pnmhistmap: ok
+pnmindex: ok
+pnminvert: ok
+pnmmercator: ok
+pnmmontage: ok
+pnmnlfilt: ok
+pnmnorm: ok
+pnmpad: ok
+pnmpaste: ok
+pnmpsnr: ok
+pnmremap: ok
+pnmrotate: ok
+pnmscalefixed: ok
+pnmshear: ok
+pnmsmooth: ok
+pnmstitch: ok
+pnmtile: ok
+pnmtoddif: ok
+pnmtofiasco: ok
+pnmtojbig: ok
+pnmtojpeg: ok
+pnmtopalm: ok
+pnmtopclxl: ok
+pnmtopng: ok
+pnmtops: ok
+pnmtorast: ok
+pnmtorle: ok
+pnmtosgi: ok
+pnmtosir: ok
+pnmtotiffcmyk: ok
+pnmtoxwd: ok
+ppm3d: ok
+ppmbrighten: ok
+ppmchange: ok
+ppmcie: ok
+ppmcolormask: ok
+ppmcolors: ok
+ppmdcfont: ok
+ppmddumpfont: ok
+ppmdim: ok
+ppmdist: ok
+ppmdither: ok
+ppmdmkfont: ok
+ppmdraw: ok
+ppmflash: ok
+ppmforge: ok
+ppmglobe: ok
+ppmhist: ok
+ppmlabel: ok
+ppmmake: ok
+ppmmix: ok
+ppmntsc: ok
+ppmpat: ok
+ppmrelief: ok
+ppmrough: ok
+ppmshift: ok
+ppmspread: ok
+ppmtoacad: ok
+ppmtoapplevol: ok
+ppmtoarbtxt: ok
+ppmtoascii: ok
+ppmtobmp: ok
+ppmtoeyuv: ok
+ppmtoicr: ok
+ppmtoilbm: ok
+ppmtoleaf: ok
+ppmtolj: ok
+ppmtomitsu: ok
+ppmtoneo: ok
+ppmtopcx: ok
+ppmtopgm: ok
+ppmtopi1: ok
+ppmtopict: ok
+ppmtopj: ok
+ppmtopjxl: ok
+ppmtoppm: ok
+ppmtopuzz: ok
+ppmtorgb3: ok
+ppmtosixel: ok
+ppmtospu: ok
+ppmtoterm: ok
+ppmtowinicon: ok
+ppmtoxpm: ok
+ppmtoyuv: ok
+ppmtoyuvsplit: ok
+ppmtv: ok
+ppmwheel: ok
+psidtopgm: ok
+pstopnm: ok
+qrttoppm: ok
+rasttopnm: ok
+rawtopgm: ok
+rawtoppm: ok
+rgb3toppm: ok
+rlatopam: ok
+rletopnm: ok
+sbigtopgm: ok
+sgitopnm: ok
+sirtopnm: ok
+sldtoppm: ok
+spctoppm: ok
+spottopgm: ok
+sputoppm: ok
+srftopam: ok
+st4topgm: ok
+sunicontopnm: ok
+svgtopam: ok
+tgatoppm: ok
+thinkjettopbm: ok
+tifftopnm: ok
+wbmptopbm: ok
+winicontopam: ok
+winicontoppm: ok
+xbmtopbm: ok
+ximtoppm: ok
+xpmtoppm: ok
+xvminitoppm: ok
+xwdtopnm: ok
+ybmtopbm: ok
+yuvsplittoppm: ok
+yuvtoppm: ok
+yuy2topam: ok
+zeisstopnm: ok
+fiascotopnm: ok
+manweb: ok
+pnmmargin: ok
+anytopnm: ok
+ppmtomap: ok
+ppmshadow: ok
+pnmquant: ok
+pnmquantall: ok
+ppmrainbow: ok
+ppmfade: ok
+pamstretch-gen: ok
+pcdovtoppm: ok
diff --git a/test/all-in-place.test b/test/all-in-place.test
new file mode 100755
index 00000000..4c8af56b
--- /dev/null
+++ b/test/all-in-place.test
@@ -0,0 +1,429 @@
+#! /bin/bash
+# Tests whether the executable files listed below are in place.
+
+# We test by actually running all the executables.
+
+# Failure message
+# See Netpbm Library Prerequisites
+# http://netpbm.sourceforge.net/prereq.html
+# if make succeeds but this test fails.
+
+function testExitStatus () {
+
+# This function takes 3 positional parameters:
+#   $1: filename
+#   $2: expected exit status   (In some cases we expect error.)
+#   $3: actual exit status
+
+    case $3 in
+      126) if [ ! -x $1 ]
+             then echo $1": NOT EXECUTABLE";
+             else echo $1": ERROR: "$3;    echo $1": error: "$3 1>&2 ;
+           fi ;;
+      127) type -p $1 > /dev/null
+           if [ $? -ne 0 ]
+             then echo $1": NO SUCH FILE";
+             else echo $1": ERROR: "$3;    echo $1": error: "$3 1>&2 ;
+           fi ;;
+      88)  echo $1": NO SUCH FILE" ;;
+      $2)  echo $1": ok" ;;
+      *)   echo $1": ERROR: "$3;    echo $1": error: "$3 1>&2 ;;
+    esac
+
+}
+
+
+# Test programs which have the --version flag.
+# See showVersion() in lib/libpm.c for the standard version announcement.
+
+ordinary_testprogs="\
+  411toppm \
+  asciitopgm \
+  atktopbm \
+  avstopam \
+  bioradtopgm \
+  bmptopnm \
+  brushtopbm \
+  cameratopam \
+  cistopbm \
+  cmuwmtopbm \
+  ddbugtopbm \
+  escp2topbm \
+  eyuvtoppm \
+  fitstopnm \
+  fstopgm \
+  g3topbm \
+  gemtopnm \
+  giftopnm \
+  gouldtoppm \
+  hdifftopam \
+  hipstopgm \
+  ilbmtoppm \
+  imgtoppm \
+  infotopam \
+  jbigtopnm \
+  jpeg2ktopam \
+  jpegtopnm \
+  leaftoppm \
+  lispmtopgm \
+  macptopbm \
+  mdatopbm \
+  mgrtopbm \
+  mrftopbm \
+  mtvtoppm \
+  neotoppm \
+  palmtopnm \
+  pamaddnoise \
+  pamarith \
+  pambackground \
+  pambayer \
+  pamchannel \
+  pamcomp \
+  pamcrater \
+  pamcut \
+  pamdeinterlace \
+  pamdepth \
+  pamdice \
+  pamditherbw \
+  pamedge \
+  pamendian \
+  pamenlarge \
+  pamexec \
+  pamfile \
+  pamfix \
+  pamflip \
+  pamfunc \
+  pamgauss \
+  pamgradient \
+  pamlookup \
+  pammasksharpen \
+  pammixinterlace \
+  pammosaicknit \
+  pamoil \
+  pampaintspill \
+  pamperspective \
+  pampick \
+  pampop9 \
+  pamrecolor \
+  pamrubber \
+  pamscale \
+  pamseq \
+  pamshadedrelief \
+  pamsharpmap \
+  pamsharpness \
+  pamsistoaglyph \
+  pamslice \
+  pamsplit \
+  pamstack \
+  pamstereogram \
+  pamstretch \
+  pamsumm \
+  pamsummcol \
+  pamthreshold \
+  pamtilt \
+  pamtoavs \
+  pamtodjvurle \
+  pamtofits \
+  pamtogif \
+  pamtohdiff \
+  pamtohtmltbl \
+  pamtojpeg2k \
+  pamtompfont \
+  pamtooctaveimg \
+  pamtopam \
+  pamtopdbimg \
+  pamtopfm \
+  pamtopng \
+  pamtopnm \
+  pamtosrf \
+  pamtosvg \
+  pamtotga \
+  pamtotiff \
+  pamtouil \
+  pamtowinicon \
+  pamtoxvmini \
+  pamundice \
+  pamunlookup \
+  pamvalidate \
+  pamwipeout \
+  pamx \
+  pbmclean \
+  pbmlife \
+  pbmmake \
+  pbmmask \
+  pbmminkowski \
+  pbmpage \
+  pbmpscale \
+  pbmreduce \
+  pbmtext \
+  pbmtextps \
+  pbmto10x \
+  pbmto4425 \
+  pbmtoascii \
+  pbmtoatk \
+  pbmtobbnbg \
+  pbmtocis \
+  pbmtocmuwm \
+  pbmtodjvurle \
+  pbmtoepsi \
+  pbmtoepson \
+  pbmtoescp2 \
+  pbmtog3 \
+  pbmtogem \
+  pbmtogo \
+  pbmtoibm23xx \
+  pbmtolj \
+  pbmtoln03 \
+  pbmtolps \
+  pbmtomacp \
+  pbmtomatrixorbital \
+  pbmtomda \
+  pbmtomgr \
+  pbmtomrf \
+  pbmtonokia \
+  pbmtopgm \
+  pbmtopi3 \
+  pbmtopk \
+  pbmtoplot \
+  pbmtoppa \
+  pbmtopsg3 \
+  pbmtoptx \
+  pbmtosunicon \
+  pbmtowbmp \
+  pbmtoxbm \
+  pbmtoybm \
+  pbmtozinc \
+  pbmupc \
+  pc1toppm \
+  pcxtoppm \
+  pdbimgtopam \
+  pfmtopam \
+  pgmabel \
+  pgmbentley \
+  pgmdeshadow \
+  pgmenhance \
+  pgmhist \
+  pgmkernel \
+  pgmmake \
+  pgmmedian \
+  pgmminkowski \
+  pgmmorphconv \
+  pgmnoise \
+  pgmramp \
+  pgmtexture \
+  pgmtofs \
+  pgmtolispm \
+  pgmtopbm \
+  pgmtopgm \
+  pgmtoppm \
+  pgmtosbig \
+  pgmtost4 \
+  pi1toppm \
+  pi3topbm \
+  picttoppm \
+  pjtoppm \
+  pktopbm \
+  pngtopam \
+  pnmalias \
+  pnmcat \
+  pnmcolormap \
+  pnmconvol \
+  pnmcrop \
+  pnmgamma \
+  pnmhisteq \
+  pnmhistmap \
+  pnmindex \
+  pnminvert \
+  pnmmercator \
+  pnmmontage \
+  pnmnlfilt \
+  pnmnorm \
+  pnmpad \
+  pnmpaste \
+  pnmpsnr \
+  pnmremap \
+  pnmrotate \
+  pnmscalefixed \
+  pnmshear \
+  pnmsmooth \
+  pnmstitch \
+  pnmtile \
+  pnmtoddif \
+  pnmtofiasco \
+  pnmtojbig \
+  pnmtojpeg \
+  pnmtopalm \
+  pnmtopclxl \
+  pnmtopng \
+  pnmtops \
+  pnmtorast \
+  pnmtorle \
+  pnmtosgi \
+  pnmtosir \
+  pnmtotiffcmyk \
+  pnmtoxwd \
+  ppm3d \
+  ppmbrighten \
+  ppmchange \
+  ppmcie \
+  ppmcolormask \
+  ppmcolors \
+  ppmdcfont \
+  ppmddumpfont \
+  ppmdim \
+  ppmdist \
+  ppmdither \
+  ppmdmkfont \
+  ppmdraw \
+  ppmflash \
+  ppmforge \
+  ppmglobe \
+  ppmhist \
+  ppmlabel \
+  ppmmake \
+  ppmmix \
+  ppmntsc \
+  ppmpat \
+  ppmrelief \
+  ppmrough \
+  ppmshift \
+  ppmspread \
+  ppmtoacad \
+  ppmtoapplevol \
+  ppmtoarbtxt \
+  ppmtoascii \
+  ppmtobmp \
+  ppmtoeyuv \
+  ppmtoicr \
+  ppmtoilbm \
+  ppmtoleaf \
+  ppmtolj \
+  ppmtomitsu \
+  ppmtoneo \
+  ppmtopcx \
+  ppmtopgm \
+  ppmtopi1 \
+  ppmtopict \
+  ppmtopj \
+  ppmtopjxl \
+  ppmtoppm \
+  ppmtopuzz \
+  ppmtorgb3 \
+  ppmtosixel \
+  ppmtospu \
+  ppmtoterm \
+  ppmtowinicon \
+  ppmtoxpm \
+  ppmtoyuv \
+  ppmtoyuvsplit \
+  ppmtv \
+  ppmwheel \
+  psidtopgm \
+  pstopnm \
+  qrttoppm \
+  rasttopnm \
+  rawtopgm \
+  rawtoppm \
+  rgb3toppm \
+  rlatopam \
+  rletopnm \
+  sbigtopgm \
+  sgitopnm \
+  sirtopnm \
+  sldtoppm \
+  spctoppm \
+  spottopgm \
+  sputoppm \
+  srftopam \
+  st4topgm \
+  sunicontopnm \
+  svgtopam \
+  tgatoppm \
+  thinkjettopbm \
+  tifftopnm \
+  wbmptopbm \
+  winicontopam \
+  winicontoppm \
+  xbmtopbm \
+  ximtoppm \
+  xpmtoppm \
+  xvminitoppm \
+  xwdtopnm \
+  ybmtopbm \
+  yuvsplittoppm \
+  yuvtoppm \
+  yuy2topam \
+  zeisstopnm \
+"
+
+for i in $ordinary_testprogs
+  do
+    $i --version  2>&1 | \
+    egrep -v \
+    "(Using libnetpbm|Compiled|(BSD|SYSV|MSDOS|AMIGA) defined|RGB_?ENV=)" \
+      1>&2;
+    testExitStatus $i 0 ${PIPESTATUS[0]}
+  done
+
+
+# Test fiascotopnm, which has a unique -v flag.
+fiascotopnm -v 2> /dev/null
+    testExitStatus fiascotopnm 2 $?
+
+
+# Test manweb and pnmmargin, which have --help.
+manweb --help > /dev/null
+    testExitStatus manweb 0 $?
+
+pnmmargin --help 2> /dev/null
+    testExitStatus pnmmargin 1 $?
+
+
+# Test anytopnm, pnmnoraw, pnmquant, pnmquantall
+# ppmrainbow, ppmshadow, ppmtomap
+# with trivial input.
+
+$i ${tmpdir}/test.pbm > /dev/null 2> /dev/null;
+tmpdir=${tmpdir:-/tmp}
+test_pbm=${tmpdir}/test.pbm
+
+cat > ${test_pbm} <<EOF
+P1
+1 1
+1
+EOF
+
+for i in anytopnm ppmtomap ppmshadow
+  do
+    $i ${tmpdir}/test.pbm > /dev/null 2> /dev/null;
+    testExitStatus $i 0 $?
+  done
+
+for i in pnmquant pnmquantall
+  do
+    $i 2 ${tmpdir}/test.pbm > /dev/null 2> /dev/null;
+    testExitStatus $i 0 $?
+  done
+
+rm ${test_pbm}
+    ppmrainbow rgb:00/00/00 rgb:ff/ff/ff \
+    -tmpdir=${tmpdir} -width=2 -height=2 > /dev/null
+    testExitStatus ppmrainbow 0 $?
+
+
+# Test ppmfade with corrupt input.
+# Prevent the creation of output files by setting base to /dev/null.
+# Exit status should be 50.
+    ppmfade -f /dev/zero -base /dev/null > /dev/null 2> /dev/null
+    testExitStatus ppmfade 50 $?
+
+
+# Test pamstretch-gen and pcdovtoppm with no input.
+# These two programs write a usage message on standout in this case.
+# Exit status should be 1.
+
+for i in pamstretch-gen pcdovtoppm
+  do
+     $i > /dev/null
+     testExitStatus $i 1 $?
+  done
diff --git a/test/atari-roundtrip.ok b/test/atari-roundtrip.ok
new file mode 100644
index 00000000..48b8a30a
--- /dev/null
+++ b/test/atari-roundtrip.ok
@@ -0,0 +1,3 @@
+3583332118 192013
+3583332118 192013
+3583332118 192013
diff --git a/test/atari-roundtrip.test b/test/atari-roundtrip.test
new file mode 100755
index 00000000..413438a1
--- /dev/null
+++ b/test/atari-roundtrip.test
@@ -0,0 +1,32 @@
+#! /bin/bash
+# This script tests: ppmtopi1 pi1toppm ppmtoneo neotoppm
+# Also requires: pgmramp pamscale pbmmake pamenlarge rgb3toppm pamdepth
+
+
+# Atari pi1 and neo files have the following characteristics:
+# Size: 320x200
+# Maxval: 7
+# Color palette: at most 16 entries
+
+tmpdir=${tmpdir:-/tmp}
+t_red=${tmpdir}/t.red
+t_grn=${tmpdir}/t.grn
+t_blu=${tmpdir}/t.blu
+t_ppm=${tmpdir}/t.ppm
+
+pgmramp -lr -maxval=3 4 1 | \
+  pamscale -xscale=80 -yscale=200 -nomix  > ${t_red}
+pbmmake -g 16 10 | pamenlarge 20  > ${t_grn}
+pbmmake -g  8  5 | pamenlarge 40  > ${t_blu}
+rgb3toppm ${t_red} ${t_grn} ${t_blu} | pamdepth 7 > ${t_ppm}
+
+#Test 0.  Should print: 3583332118 192013
+cksum < ${t_ppm}
+
+#Test 1.  Should print: 3583332118 192013
+ppmtopi1 ${t_ppm} | pi1toppm | cksum
+
+#Test 2.  Should print: 3583332118 192013
+ppmtoneo ${t_ppm} | neotoppm | cksum
+
+rm ${t_red} ${t_grn} ${t_blu} ${t_ppm}
diff --git a/test/atk-roundtrip.ok b/test/atk-roundtrip.ok
new file mode 100644
index 00000000..845be5fb
--- /dev/null
+++ b/test/atk-roundtrip.ok
@@ -0,0 +1 @@
+2425386270 41
diff --git a/test/atk-roundtrip.test b/test/atk-roundtrip.test
new file mode 100755
index 00000000..6db3df6d
--- /dev/null
+++ b/test/atk-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pbmtoatk atktopbm
+# Also requires:
+
+
+# Should print 2425386270 41, cksum of testgrid.pbm
+pbmtoatk testgrid.pbm | atktopbm | cksum
diff --git a/test/avs-roundtrip.ok b/test/avs-roundtrip.ok
new file mode 100644
index 00000000..82eac5a8
--- /dev/null
+++ b/test/avs-roundtrip.ok
@@ -0,0 +1 @@
+1926073387 101484
diff --git a/test/avs-roundtrip.test b/test/avs-roundtrip.test
new file mode 100755
index 00000000..042ce91e
--- /dev/null
+++ b/test/avs-roundtrip.test
@@ -0,0 +1,8 @@
+#! /bin/bash
+# This script tests: pamtoavs avstopam
+# Also requires: pamtopnm
+
+
+# Should produce 1926073387 101484, cksum of testimg.ppm
+pamtoavs testimg.ppm | \
+  avstopam | pamtopnm | cksum
diff --git a/test/bmp-roundtrip.ok b/test/bmp-roundtrip.ok
new file mode 100644
index 00000000..67f7a1fe
--- /dev/null
+++ b/test/bmp-roundtrip.ok
@@ -0,0 +1,2 @@
+1926073387 101484
+2425386270 41
diff --git a/test/bmp-roundtrip.test b/test/bmp-roundtrip.test
new file mode 100755
index 00000000..be9a4548
--- /dev/null
+++ b/test/bmp-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: bmptopnm ppmtobmp
+# Also requires:
+
+
+ppmtobmp testimg.ppm | bmptopnm | cksum
+ppmtobmp testgrid.pbm | bmptopnm | cksum
diff --git a/test/cis-roundtrip.ok b/test/cis-roundtrip.ok
new file mode 100644
index 00000000..da90078b
--- /dev/null
+++ b/test/cis-roundtrip.ok
@@ -0,0 +1,2 @@
+2631579683 1546
+2425386270 41
diff --git a/test/cis-roundtrip.test b/test/cis-roundtrip.test
new file mode 100755
index 00000000..62fcc3b9
--- /dev/null
+++ b/test/cis-roundtrip.test
@@ -0,0 +1,14 @@
+#! /bin/bash
+# This script tests: pbmtocis cistopbm
+# Also requires: pbmmake pamcut
+
+
+# Output images produced by pbmtocis are of fixed size,
+# either 128x96 or 256x192.
+# Smaller input images are padded, larger ones are cropped.
+
+# Test 1. Should print 2631579683 1546
+pbmmake -g 128 96 | pbmtocis | cistopbm | cksum
+
+# Test 2. Should print 2425386270 41
+pbmtocis testgrid.pbm | cistopbm | pamcut 0 0 14 16 | cksum
diff --git a/test/cmuw-roundtrip.ok b/test/cmuw-roundtrip.ok
new file mode 100644
index 00000000..845be5fb
--- /dev/null
+++ b/test/cmuw-roundtrip.ok
@@ -0,0 +1 @@
+2425386270 41
diff --git a/test/cmuw-roundtrip.test b/test/cmuw-roundtrip.test
new file mode 100755
index 00000000..e52adb70
--- /dev/null
+++ b/test/cmuw-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pbmtocmuwm cmuwmtopbm
+# Also requires:
+
+
+# Should print 2425386270 41, cksum of testgrid.pbm
+pbmtocmuwm testgrid.pbm | cmuwmtopbm | cksum
diff --git a/test/cut-paste-roundtrip.ok b/test/cut-paste-roundtrip.ok
new file mode 100644
index 00000000..6e0f9359
--- /dev/null
+++ b/test/cut-paste-roundtrip.ok
@@ -0,0 +1,10 @@
+2999529086 101484
+1926073387 101484
+514843768 41
+1880210648 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
diff --git a/test/cut-paste-roundtrip.test b/test/cut-paste-roundtrip.test
new file mode 100755
index 00000000..1bb3a7c2
--- /dev/null
+++ b/test/cut-paste-roundtrip.test
@@ -0,0 +1,52 @@
+#! /bin/bash
+# This script tests: pamcut pnmpaste
+# Also requires: pbmmake pnmpad
+
+base_ppm=${tmpdir}/base.ppm
+
+# Test 1. Should produce 2999529086 101484
+# (Make a modified base image to prevent false positives.)
+
+pbmmake -g 100 70 | \
+pnmpaste -replace - 50 40 testimg.ppm | tee ${base_ppm} | cksum
+
+# Test 2. Should produce 1926073387 101484
+
+pamcut 50 40 100 70 testimg.ppm | \
+pnmpaste -replace - 50 40 ${base_ppm} | cksum
+
+rm ${base_ppm}
+
+
+basewhite_pbm=${tmpdir}/basewhite.pbm
+baseblack_pbm=${tmpdir}/baseblack.pbm
+part_pbm=${tmpdir}/part.pbm
+
+# Test 3. Should produce 514843768 41
+# (Make a modified base image to prevent false positives.)
+
+pbmmake -w 5 7 |
+pnmpaste -replace - 7 9 testgrid.pbm | tee ${basewhite_pbm} | cksum
+
+# Test 4. Should produce 1880210648 41
+# (Another modified base image with target area black.)
+
+pbmmake -b 5 7 |
+pnmpaste -replace - 7 9 ${basewhite_pbm} | tee ${baseblack_pbm} | cksum
+
+
+# Test 5. Should produce 2425386270 41 six times.
+
+pamcut 7 9 5 7 testgrid.pbm > ${part_pbm}
+
+pnmpaste -replace ${part_pbm} 7 9 ${basewhite_pbm}  | cksum
+pnmpaste -and     ${part_pbm} 7 9 ${basewhite_pbm}  | cksum
+pnmpaste -or      ${part_pbm} 7 9 ${baseblack_pbm}  | cksum
+pnmpad -left 3 -white ${part_pbm} |\
+              pnmpaste -and - 4 9 ${basewhite_pbm}  | cksum
+pnmpad -left 7 -right 2 -top 1 -black ${part_pbm} |\
+              pnmpaste -or  - 0 8 ${baseblack_pbm}  | cksum
+pnmpaste -xor     ${part_pbm} 7 9 testgrid.pbm  |\
+              pnmpaste -xor ${part_pbm} 7 9 | cksum
+
+rm ${part_pbm} ${basewhite_pbm} ${baseblack_pbm}
diff --git a/test/eyuvtoppm.ok b/test/eyuvtoppm.ok
new file mode 100644
index 00000000..90830f9f
--- /dev/null
+++ b/test/eyuvtoppm.ok
@@ -0,0 +1 @@
+1719878124 253455
diff --git a/test/eyuvtoppm.test b/test/eyuvtoppm.test
new file mode 100755
index 00000000..b28b4539
--- /dev/null
+++ b/test/eyuvtoppm.test
@@ -0,0 +1,8 @@
+#! /bin/bash
+# This script tests: eyuvtoppm
+# Also requires:
+
+
+# Should produce 1719878124 253455
+
+head -c 126720 /dev/zero |  eyuvtoppm -quiet | cksum
diff --git a/test/facesaver-roundtrip.ok b/test/facesaver-roundtrip.ok
new file mode 100644
index 00000000..430f3f5f
--- /dev/null
+++ b/test/facesaver-roundtrip.ok
@@ -0,0 +1 @@
+1571496937 33838
diff --git a/test/facesaver-roundtrip.test b/test/facesaver-roundtrip.test
new file mode 100755
index 00000000..f4b8f069
--- /dev/null
+++ b/test/facesaver-roundtrip.test
@@ -0,0 +1,9 @@
+#! /bin/bash
+# This script tests: pgmtofs fstopgm
+# Also requires: pamchannel pamtopnm
+
+
+# Should produce 1571496937 33838, cksum of testimg.red
+
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  pgmtofs | fstopgm | cksum
diff --git a/test/fits-roundtrip.ok b/test/fits-roundtrip.ok
new file mode 100644
index 00000000..82eac5a8
--- /dev/null
+++ b/test/fits-roundtrip.ok
@@ -0,0 +1 @@
+1926073387 101484
diff --git a/test/fits-roundtrip.test b/test/fits-roundtrip.test
new file mode 100755
index 00000000..e793df9a
--- /dev/null
+++ b/test/fits-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pamtofits fitstopnm
+# Also requires:
+
+
+# Should produce 1926073387 101484, cksum of testimg.ppm
+pamtofits testimg.ppm | fitstopnm | cksum
diff --git a/test/g3-roundtrip.ok b/test/g3-roundtrip.ok
new file mode 100644
index 00000000..bb0b1cf6
--- /dev/null
+++ b/test/g3-roundtrip.ok
@@ -0,0 +1,3 @@
+0
+0
+0
diff --git a/test/g3-roundtrip.test b/test/g3-roundtrip.test
new file mode 100755
index 00000000..47e20e1d
--- /dev/null
+++ b/test/g3-roundtrip.test
@@ -0,0 +1,16 @@
+#! /bin/bash
+# This script tests: g3topbm pbmtog3
+# Also requires: pnmcrop
+
+
+pbmtog3 -nofixedwidth testgrid.pbm | \
+g3topbm -width=14 | cmp -s - testgrid.pbm
+echo $?
+
+pbmtog3 -nofixedwidth -reversebits testgrid.pbm | \
+g3topbm -width=14 -reversebits | cmp -s - testgrid.pbm
+echo $?
+
+pbmtog3 testgrid.pbm | \
+g3topbm  | pnmcrop -white -right -bottom | \
+ cmp -s - testgrid.pbm ; echo $?
diff --git a/test/gem-roundtrip.ok b/test/gem-roundtrip.ok
new file mode 100644
index 00000000..845be5fb
--- /dev/null
+++ b/test/gem-roundtrip.ok
@@ -0,0 +1 @@
+2425386270 41
diff --git a/test/gem-roundtrip.test b/test/gem-roundtrip.test
new file mode 100755
index 00000000..0fce59db
--- /dev/null
+++ b/test/gem-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pbmtogem gemtopnm
+# Also requires:
+
+
+# Should print 2425386270 41, cksum of testgrid.pbm
+pbmtogem testgrid.pbm | gemtopnm | cksum
diff --git a/test/gif-quant-roundtrip.ok b/test/gif-quant-roundtrip.ok
new file mode 100644
index 00000000..573541ac
--- /dev/null
+++ b/test/gif-quant-roundtrip.ok
@@ -0,0 +1 @@
+0
diff --git a/test/gif-quant-roundtrip.test b/test/gif-quant-roundtrip.test
new file mode 100755
index 00000000..8b911740
--- /dev/null
+++ b/test/gif-quant-roundtrip.test
@@ -0,0 +1,18 @@
+#! /bin/bash
+# This script tests: giftopnm pamtogif pnmquant
+# Also requires:
+
+
+# Should print 0
+
+colors=15      # any value between 2 - 256 works
+
+tmpdir=${tmpdir:-/tmp}
+quant_ppm=${tmpdir}/quant.ppm
+
+pnmquant ${colors} testimg.ppm > ${quant_ppm} &&
+pamtogif ${quant_ppm} | giftopnm | \
+   cmp -s - ${quant_ppm} > /dev/null
+echo $?
+
+rm ${quant_ppm}
diff --git a/test/gif-roundtrip.ok b/test/gif-roundtrip.ok
new file mode 100644
index 00000000..552bf90c
--- /dev/null
+++ b/test/gif-roundtrip.ok
@@ -0,0 +1,11 @@
+1926073387 101484
+1571496937 33838
+1571496937 33838
+1571496937 33838
+1571496937 33838
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+P1 4 1 0101 
\ No newline at end of file
diff --git a/test/gif-roundtrip.test b/test/gif-roundtrip.test
new file mode 100755
index 00000000..a6d0ec4c
--- /dev/null
+++ b/test/gif-roundtrip.test
@@ -0,0 +1,66 @@
+#! /bin/bash
+# This script tests: giftopnm pamtogif
+# Also requires: ppmtorgb3 rgb3toppm pbmmake pnminvert
+
+
+tmpdir=${tmpdir:-/tmp}
+
+# Test 1. Break up input image into three monochrome planes,
+# maxval 255.  Transform each plane to gif and back to pgm.
+# Reassemble the planes.  Result should be identical to input.
+# Should print 1926073387 101484
+
+test_ppm=${tmpdir}/testimg.ppm
+
+cp testimg.ppm ${tmpdir} &&
+ppmtorgb3 ${test_ppm}
+
+test_red=${tmpdir}/testimg.red
+test_grn=${tmpdir}/testimg.grn
+test_blu=${tmpdir}/testimg.blu
+out_red=${tmpdir}/out.red
+out_grn=${tmpdir}/out.grn
+#out_blu=${tmpdir}/out.blu
+
+pamtogif ${test_red} | giftopnm > ${out_red} &&
+pamtogif ${test_grn} | giftopnm > ${out_grn} &&
+pamtogif ${test_blu} | giftopnm | \
+  rgb3toppm ${out_red} ${out_grn} - | \
+  cksum
+
+rm ${test_ppm} ${test_grn} ${test_blu} ${out_red} ${out_grn}
+
+
+# Test 2. Should produce 1571496937 33838
+# which is the result of cksum testimg.red
+# four times
+
+test_gif=${tmpdir}/testimg.gif
+
+pamtogif ${test_red} | giftopnm | cksum
+pamtogif -interlace ${test_red} | giftopnm | cksum
+pamtogif -sort ${test_red} | tee ${test_gif} | \
+  giftopnm | cksum
+echo "junk" >> ${test_gif} && \
+  giftopnm -image=1 -quitearly ${test_gif} | cksum
+
+rm  ${test_gif} ${test_red}
+
+
+# Test 3. Should produce 2425386270 41 five times.
+
+pamtogif testgrid.pbm | giftopnm | cksum
+pamtogif -nolzw testgrid.pbm | giftopnm | cksum
+pamtogif -transparent=black testgrid.pbm | giftopnm | cksum
+pamtogif -alpha=testgrid.pbm testgrid.pbm | giftopnm | cksum
+pamtogif -transparent=white testgrid.pbm | giftopnm -alpha=- | \
+  pnminvert | cksum
+
+# Test 4.
+# In this gif file the code length changes after the last image data.
+# Image data: 3 bits, end code 4 bits.
+# Should produce P1 4 1 0 1 0 1
+
+pbmmake -g 4 1 | \
+  pamtogif | giftopnm -plain | \
+  tr '\n' ' '
diff --git a/test/hdiff-roundtrip.ok b/test/hdiff-roundtrip.ok
new file mode 100644
index 00000000..82eac5a8
--- /dev/null
+++ b/test/hdiff-roundtrip.ok
@@ -0,0 +1 @@
+1926073387 101484
diff --git a/test/hdiff-roundtrip.test b/test/hdiff-roundtrip.test
new file mode 100755
index 00000000..a4a4a906
--- /dev/null
+++ b/test/hdiff-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: hdifftopam pamtohdiff
+# Also requires:
+
+
+pamtohdiff testimg.ppm | \
+  hdifftopam -pnm | cksum
diff --git a/test/ilbm-roundtrip.ok b/test/ilbm-roundtrip.ok
new file mode 100644
index 00000000..54574a18
--- /dev/null
+++ b/test/ilbm-roundtrip.ok
@@ -0,0 +1,10 @@
+829921912 685
+829921912 685
+829921912 685
+829921912 685
+1926073387 101484
+1926073387 101484
+1926073387 101484
+984199586 101484
+2059976475 661
+2059976475 661
diff --git a/test/ilbm-roundtrip.test b/test/ilbm-roundtrip.test
new file mode 100755
index 00000000..f62368ff
--- /dev/null
+++ b/test/ilbm-roundtrip.test
@@ -0,0 +1,28 @@
+#! /bin/bash
+# This script tests: ppmtoilbm ilbmtoppm
+# Also requires: pamseq pamdepth pamtopnm pnmremap
+
+#Test. 1  Should produce 829921912 685 four times
+#Output is PPM raw, 14 by 16  maxval 255
+ppmtoilbm testgrid.pbm | ilbmtoppm | cksum
+ppmtoilbm -aga testgrid.pbm | ilbmtoppm | cksum
+ppmtoilbm -ham6 testgrid.pbm | ilbmtoppm | cksum
+ppmtoilbm -ham8 testgrid.pbm | ilbmtoppm | cksum
+
+
+#Test. 2  Should produce 1926073387 101484 three times
+ppmtoilbm testimg.ppm | ilbmtoppm | cksum
+ppmtoilbm -24force testimg.ppm | ilbmtoppm | cksum
+ppmtoilbm -dcbits 8 8 8 -nocompress testimg.ppm | ilbmtoppm | cksum
+
+
+#Test. 3  Should print 984199586 101484
+pamseq 3 5 -tupletype=RGB | pamdepth 255 | pamtopnm | \
+  pnmremap -mapfile=- testimg.ppm | ppmtoilbm | ilbmtoppm | cksum
+
+
+#Test. 4 Should print 2059976475 661 twice
+pamseq 3 5 -tupletype=RGB | pamtopnm | \
+  ppmtoilbm -compress | ilbmtoppm | cksum
+pamseq 3 5 -tupletype=RGB | pamtopnm | \
+  ppmtoilbm -nocompress | ilbmtoppm | cksum
diff --git a/test/jbig-roundtrip.ok b/test/jbig-roundtrip.ok
new file mode 100644
index 00000000..b98a694b
--- /dev/null
+++ b/test/jbig-roundtrip.ok
@@ -0,0 +1,2 @@
+2425386270 41
+1571496937 33838
diff --git a/test/jbig-roundtrip.test b/test/jbig-roundtrip.test
new file mode 100755
index 00000000..5e96a001
--- /dev/null
+++ b/test/jbig-roundtrip.test
@@ -0,0 +1,11 @@
+#! /bin/bash
+# This script tests: pnmtojbig jbigtopnm
+# Also requires: pamchannel pamtopnm
+
+
+# Test 1.  Should print 2425386270 41
+pnmtojbig testgrid.pbm | jbigtopnm | cksum
+
+# Test 2.  Should print 1571496937 33838
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  pnmtojbig | jbigtopnm | cksum
diff --git a/test/leaf-roundtrip.ok b/test/leaf-roundtrip.ok
new file mode 100644
index 00000000..82eac5a8
--- /dev/null
+++ b/test/leaf-roundtrip.ok
@@ -0,0 +1 @@
+1926073387 101484
diff --git a/test/leaf-roundtrip.test b/test/leaf-roundtrip.test
new file mode 100755
index 00000000..5628ed01
--- /dev/null
+++ b/test/leaf-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: ppmtoleaf leaftoppm
+# Also requires:
+
+
+# Should produce 1926073387 101484, cksum of testimg.ppm
+ppmtoleaf testimg.ppm | leaftoppm | cksum
diff --git a/test/legacy-names.ok b/test/legacy-names.ok
new file mode 100644
index 00000000..9676639b
--- /dev/null
+++ b/test/legacy-names.ok
@@ -0,0 +1,37 @@
+bmptoppm: ok
+gemtopbm: ok
+icontopbm: ok
+pamfixtrunc: ok
+pamrgbatopng: ok
+pbmtoicon: ok
+pgmedge: ok
+pgmnorm: ok
+pgmoil: ok
+pgmslice: ok
+pngtopnm: ok
+pnmarith: ok
+pnmcomp: ok
+pnmcut: ok
+pnmdepth: ok
+pnmenlarge: ok
+pnmfile: ok
+pnmflip: ok
+pnminterp: ok
+pnmscale: ok
+pnmsplit: ok
+pnmtofits: ok
+pnmtopnm: ok
+pnmtotiff: ok
+ppmnorm: ok
+ppmtogif: ok
+ppmtojpeg: ok
+ppmtompeg: ok
+ppmtotga: ok
+ppmtouil: ok
+pgmcrater: ok
+pbmtox10bm: ok
+pnmnoraw: ok
+pnmtoplainpnm: ok
+ppmquant: ok
+ppmquantall: ok
+hpcdtoppm: ok
diff --git a/test/legacy-names.test b/test/legacy-names.test
new file mode 100755
index 00000000..df40e62d
--- /dev/null
+++ b/test/legacy-names.test
@@ -0,0 +1,132 @@
+#! /bin/bash
+# Tests whether the executable files listed below are in place.
+
+# We test with the methods used in all-in-place.test.
+# The sole exception is hpcdtoppm.  We just confirm that it exists.
+
+# Failure message
+## If all-in-place.test succeeds and this test fails, a likely cause is a
+## problem with symbolic links.
+##
+## Important: This test checks obsoleted names.
+## Programs here do not appear in other tests.
+
+
+# Skip this test if CHECK_TYPE = tree
+
+if [ ${CHECK_TYPE} = tree ]
+  then echo "Skipping." 1>&2
+  exit 80;
+fi
+
+
+function testExitStatus () {
+
+# This function takes 3 positional parameters:
+#   $1: filename
+#   $2: expected exit status   (In some cases we expect error.)
+#   $3: actual exit status
+
+    case $3 in
+      126) if [ ! -x $1 ]
+             then echo $1": NOT EXECUTABLE";
+             else echo $1": ERROR: "$3;    echo $1": error: "$3 1>&2 ;
+           fi ;;
+      127) type -p $1 > /dev/null
+           if [ $? -ne 0 ]
+             then echo $1": NO SUCH FILE";
+             else echo $1": ERROR: "$3;    echo $1": error: "$3 1>&2 ;
+           fi ;;
+      88)  echo $1": NO SUCH FILE" ;;
+      $2)  echo $1": ok" ;;
+      *)   echo $1": ERROR: "$3;    echo $1": error: "$3 1>&2 ;;
+    esac
+
+}
+
+
+# Test programs which have the --version flag.
+# See showVersion() in lib/libpm.c for the standard version announcement.
+
+ordinary_testprogs="\
+  bmptoppm \
+  gemtopbm \
+  icontopbm \
+  pamfixtrunc \
+  pamrgbatopng \
+  pbmtoicon \
+  pgmedge \
+  pgmnorm \
+  pgmoil \
+  pgmslice \
+  pngtopnm \
+  pnmarith \
+  pnmcomp \
+  pnmcut \
+  pnmdepth \
+  pnmenlarge \
+  pnmfile \
+  pnmflip \
+  pnminterp \
+  pnmscale \
+  pnmsplit \
+  pnmtofits \
+  pnmtopnm \
+  pnmtotiff \
+  ppmnorm \
+  ppmtogif \
+  ppmtojpeg \
+  ppmtompeg \
+  ppmtotga \
+  ppmtouil \
+"
+
+for i in $ordinary_testprogs
+  do
+    $i --version  2>&1 | \
+    egrep -v \
+    "(Using libnetpbm|Compiled|(BSD|SYSV|MSDOS|AMIGA) defined|RGB_?ENV=)" \
+      1>&2;
+    testExitStatus $i 0 ${PIPESTATUS[0]}
+  done
+
+
+# Test pgmcrater
+
+pgmcrater -number 1 -xsize 15 -ysize 15 -randomseed 1 > /dev/null
+testExitStatus pgmcrater 0 $?
+
+
+# Test pbmtox10bm, pnmnoraw, pnmtoplainpnm, ppmquantall, ppmrainbow
+# with trivial input.
+
+$i ${tmpdir}/test.pbm > /dev/null 2> /dev/null;
+tmpdir=${tmpdir:-/tmp}
+test_pbm=${tmpdir}/test.pbm
+
+cat > ${test_pbm} <<EOF
+P1
+1 1
+1
+EOF
+
+for i in pbmtox10bm pnmnoraw pnmtoplainpnm
+  do
+    $i ${tmpdir}/test.pbm > /dev/null 2> /dev/null;
+    testExitStatus $i 0 $?
+  done
+
+for i in ppmquant ppmquantall
+  do
+    $i 2 ${tmpdir}/test.pbm > /dev/null 2> /dev/null;
+    testExitStatus $i 0 $?
+  done
+
+
+rm ${test_pbm}
+
+
+# Test hpcdtoppm.  Simply confirm its existence.
+
+type -p hpcdtoppm > /dev/null
+testExitStatus hpcdtoppm 0 $?
diff --git a/test/lookup-roundtrip.ok b/test/lookup-roundtrip.ok
new file mode 100644
index 00000000..82eac5a8
--- /dev/null
+++ b/test/lookup-roundtrip.ok
@@ -0,0 +1 @@
+1926073387 101484
diff --git a/test/lookup-roundtrip.test b/test/lookup-roundtrip.test
new file mode 100755
index 00000000..63ec0777
--- /dev/null
+++ b/test/lookup-roundtrip.test
@@ -0,0 +1,12 @@
+#! /bin/bash
+# This script tests: pamlookup pamunlookup
+# Also requires: ppmhist
+
+tmpdir=${tmpdir:-/tmp}
+mapfile=${tmpdir}/mapfile
+
+ppmhist testimg.ppm -map > ${mapfile}
+
+# Test.  Should produce 1926073387 101484
+pamunlookup -lookupfile=${mapfile} testimg.ppm |\
+  pamlookup -lookupfile=${mapfile} | cksum
diff --git a/test/macp-roundtrip.ok b/test/macp-roundtrip.ok
new file mode 100644
index 00000000..9ff9d249
--- /dev/null
+++ b/test/macp-roundtrip.ok
@@ -0,0 +1,5 @@
+2425386270 41
+2425386270 41
+2329957971 51851
+2907103393 5086
+2907103393 5086
diff --git a/test/macp-roundtrip.test b/test/macp-roundtrip.test
new file mode 100755
index 00000000..4774a2c2
--- /dev/null
+++ b/test/macp-roundtrip.test
@@ -0,0 +1,31 @@
+#! /bin/bash
+# This script tests: pbmtomacp macptopbm
+# Also requires: pnmcrop pbmpage pbmupc pnmpad
+
+tmpdir=${tmpdir:-/tmp}
+temp1_pbm=${tmpdir}/temp1.ppm
+temp2_pbm=${tmpdir}/temp2.ppm
+
+
+# Test 1. Should produce 2425386270 41 twice
+# Because Macpaint files are fixed size (576 x 720 pixels)
+# pbmtomacp automatically adds padding when input is smaller.
+
+pbmtomacp testgrid.pbm | macptopbm | tee ${temp1_pbm} | \
+    pnmcrop | cksum
+
+pbmtomacp ${temp1_pbm} | macptopbm | pnmcrop | cksum
+
+
+#Test 2. Should produce 2329957971 51851
+pbmpage 1 | pbmtomacp | macptopbm | cksum
+
+
+#Test 3. Should produce 2907103393 5086 twice
+pbmupc 0 12345 67890 | pnmpad -black -t 44 -b 20 -l 100 -r 251 | pbmtomacp | macptopbm | \
+    tee ${temp2_pbm} | \
+    pnmcrop | pnmcrop | cksum
+
+pbmtomacp ${temp2_pbm} | macptopbm | pnmcrop | pnmcrop | cksum
+
+rm ${temp1_pbm} ${temp2_pbm}
diff --git a/test/mda-roundtrip.ok b/test/mda-roundtrip.ok
new file mode 100644
index 00000000..ef27ffd0
--- /dev/null
+++ b/test/mda-roundtrip.ok
@@ -0,0 +1,2 @@
+1757803444 169
+2425386270 41
diff --git a/test/mda-roundtrip.test b/test/mda-roundtrip.test
new file mode 100755
index 00000000..58376f3f
--- /dev/null
+++ b/test/mda-roundtrip.test
@@ -0,0 +1,13 @@
+#! /bin/bash
+# This script tests: pbmtomda mdatopbm
+# Also requires: pbmmake pnmpad pamcut
+
+
+# Pbmtomda requires input width and height to be multiples of 8.
+
+# Test 1.  Should print 1757803444 169
+pbmmake -g 32 40 | pbmtomda | mdatopbm | cksum
+
+# Test 2.  Should print 2425386270 41
+pnmpad -right 2 testgrid.pbm | \
+  pbmtomda | mdatopbm | pamcut 0 0 14 16 | cksum
diff --git a/test/mgr-roundtrip.ok b/test/mgr-roundtrip.ok
new file mode 100644
index 00000000..845be5fb
--- /dev/null
+++ b/test/mgr-roundtrip.ok
@@ -0,0 +1 @@
+2425386270 41
diff --git a/test/mgr-roundtrip.test b/test/mgr-roundtrip.test
new file mode 100755
index 00000000..252ae996
--- /dev/null
+++ b/test/mgr-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pbmtomgr mgrtopbm
+# Also requires:
+
+
+# Should print 2425386270 41, cksum of testgrid.pbm
+pbmtomgr testgrid.pbm | mgrtopbm | cksum
diff --git a/test/mrf-roundtrip.ok b/test/mrf-roundtrip.ok
new file mode 100644
index 00000000..845be5fb
--- /dev/null
+++ b/test/mrf-roundtrip.ok
@@ -0,0 +1 @@
+2425386270 41
diff --git a/test/mrf-roundtrip.test b/test/mrf-roundtrip.test
new file mode 100755
index 00000000..c9d8ce3a
--- /dev/null
+++ b/test/mrf-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pbmtomrf mrftopbm
+# Also requires:
+
+
+# Should print 2425386270 41, cksum of testgrid.pbm
+pbmtomrf testgrid.pbm | mrftopbm | cksum
diff --git a/test/pad-crop-roundtrip.ok b/test/pad-crop-roundtrip.ok
new file mode 100644
index 00000000..67f7a1fe
--- /dev/null
+++ b/test/pad-crop-roundtrip.ok
@@ -0,0 +1,2 @@
+1926073387 101484
+2425386270 41
diff --git a/test/pad-crop-roundtrip.test b/test/pad-crop-roundtrip.test
new file mode 100755
index 00000000..c7780d68
--- /dev/null
+++ b/test/pad-crop-roundtrip.test
@@ -0,0 +1,9 @@
+#! /bin/bash
+# This script tests: pnmcrop pnmmargin
+# Also requires: pnmpad
+
+
+pnmmargin -white 10 testimg.ppm | \
+  pnmcrop | cksum
+pnmmargin -white 10 testgrid.pbm | \
+  pnmcrop | cksum
diff --git a/test/pambackground.ok b/test/pambackground.ok
new file mode 100644
index 00000000..acb69bfa
--- /dev/null
+++ b/test/pambackground.ok
@@ -0,0 +1,3 @@
+2155020792 451
+2514682516 33871
+2667595257 17328
diff --git a/test/pambackground.test b/test/pambackground.test
new file mode 100755
index 00000000..522f4823
--- /dev/null
+++ b/test/pambackground.test
@@ -0,0 +1,34 @@
+#! /bin/bash
+# This script tests: pambackground
+# Also requires: pamgradient pamseq pbmmake pnmmargin pnmremap pnmtile pnmpad
+
+
+tmpdir=${tmpdir:-/tmp}
+
+# Test 1.
+# Should produce: 2155020792 451
+pbmmake -g 23 11 | pnmmargin -black 2 | pambackground | cksum
+
+
+# Test 2.
+# Should produce: 2514682516 33871
+ibmttl_pam=${tmpdir}/ibmttl.pam
+
+pamseq 3 1 > ${ibmttl_pam} && \
+pnmremap -quiet -mapfile ${ibmttl_pam} testimg.ppm | pambackground | cksum
+rm ${ibmttl_pam}
+
+
+# Test 3.
+# Should produce: 2667595257 17328
+pamgradient rgb:01/01/01 rgb:ff/7f/00 rgb:00/ff/7f rgb:fe/fe/fe 10 10 | \
+pnmmargin -white 2 | pnmtile 144 120 | pambackground | cksum
+
+
+# pamgradient rgb:01/01/01 rgb:ff/7f/00  rgb:00/ff/7f rgb:fe/fe/fe 10 10 | \
+# pnmmargin -white 2 | pnmtile 144 120 | cksum
+# should produce: 3147800256 51855
+# Above input image is a "wall" with 12x10 "windows".
+
+# pnmremap -mapfile ${ibmttl_pam} testimg.ppm | cksum
+# should produce : 452672747 101517
diff --git a/test/pamchannel.ok b/test/pamchannel.ok
new file mode 100644
index 00000000..79317f03
--- /dev/null
+++ b/test/pamchannel.ok
@@ -0,0 +1,3 @@
+1571496937 33838
+394856971 33838
+3164158573 33838
diff --git a/test/pamchannel.test b/test/pamchannel.test
new file mode 100755
index 00000000..3529f4fe
--- /dev/null
+++ b/test/pamchannel.test
@@ -0,0 +1,32 @@
+#! /bin/bash
+# This script tests: pamchannel
+# Also requires: pamtopnm
+
+
+# Extract planes one by one.
+# Convert output to pgm to make it identical to ppmtorgb3 output.
+
+# $ ppmtorgb3 testimg.ppm ; cksum testimg.{red,grn,blu}
+#
+# 1571496937 33838 testimg.red
+# 394856971 33838 testimg.grn
+# 3164158573 33838 testimg.blu
+
+
+# Test 1. red channel
+# Should produce 1571496937 33838
+
+pamchannel -infile testimg.ppm 0 | \
+  pamtopnm --assume | cksum
+
+# Test 2. green channel
+# Should produce  394856971 33838
+
+pamchannel -infile testimg.ppm -tupletype="GRAYSCALE" 1 | \
+  pamtopnm | cksum
+
+# Test 3. blue channel
+# Should produce 3164158573 33838
+
+pamchannel -infile testimg.ppm 2 | \
+  pamtopnm --assume | cksum
diff --git a/test/pamcrater.ok b/test/pamcrater.ok
new file mode 100644
index 00000000..cfb186a6
--- /dev/null
+++ b/test/pamcrater.ok
@@ -0,0 +1,6 @@
+4
+4
+4
+2
+2
+2
diff --git a/test/pamcrater.test b/test/pamcrater.test
new file mode 100755
index 00000000..4d3858b9
--- /dev/null
+++ b/test/pamcrater.test
@@ -0,0 +1,53 @@
+#! /bin/bash
+# This script tests: pamcrater pamshadedrelief
+# Also requires: pamstack pamvalidate pamcut pamflip
+
+
+# We use the undocumented --test and --radius options of pamcrater.
+# pamcrater --test --radius=N
+# The above draws a single crater of radius N.
+# The resulting image should be symmetric.
+
+tmpdir=${tmpdir:-/tmp}
+
+test_pam=${tmpdir}/test.pam
+testshaded_pam=${tmpdir}/testshaded_pam
+test10_pam=${tmpdir}/test10.pam
+test50_pam=${tmpdir}/test50.pam
+test100_pam=${tmpdir}/test100.pam
+test150_pam=${tmpdir}/test150.pam
+
+# Test 1.  Should print 4 three times
+
+pamcrater --test --radius=10 > $test10_pam
+pamcrater --test --radius=50 > $test50_pam
+pamcrater --test --radius=100 > $test100_pam
+pamcrater --test --radius=150 > $test150_pam
+
+pamstack ${test10_pam} ${test50_pam} ${test100_pam} ${test150_pam} |
+  pamvalidate > ${test_pam}
+
+for i in 1 10 70
+  do
+  ( pamcut -top=$((128 + $i)) -height=1 ${test_pam} | cksum &&
+    pamcut -top=$((128 - $i)) -height=1 ${test_pam} | cksum &&
+    pamcut -left=$((128 + $i)) -width=1 ${test_pam} | pamflip -xy | cksum &&
+    pamcut -left=$((128 - $i)) -width=1 ${test_pam} | pamflip -xy | cksum
+  ) | uniq -c | awk '{print $1}'
+  done
+
+rm ${test_pam} ${test10_pam} ${test50_pam}
+
+# Test 2.  Should print 2 three times
+
+pamshadedrelief ${test100_pam} > ${testshaded_pam}
+
+( pamcut -top=$((128 + 12)) -height=1  ${testshaded_pam} | cksum &&
+  pamcut -top=$((128 - 12)) -height=1  ${testshaded_pam} | cksum &&
+  pamcut -top=$((128 + 31)) -height=1  ${testshaded_pam} | cksum &&
+  pamcut -top=$((128 - 31)) -height=1  ${testshaded_pam} | cksum &&
+  pamcut -top=$((128 + 99)) -height=1  ${testshaded_pam} | cksum &&
+  pamcut -top=$((128 - 99)) -height=1  ${testshaded_pam} | cksum
+) | uniq -c | awk '{print $1}'
+
+rm ${testshaded_pam} ${test100_pam}
diff --git a/test/pamcut.ok b/test/pamcut.ok
new file mode 100644
index 00000000..61ef99bc
--- /dev/null
+++ b/test/pamcut.ok
@@ -0,0 +1,4 @@
+2958909756 124815
+1550940962 10933
+708474423 14
+3412257956 129
diff --git a/test/pamcut.test b/test/pamcut.test
new file mode 100755
index 00000000..fd9185a3
--- /dev/null
+++ b/test/pamcut.test
@@ -0,0 +1,18 @@
+#! /bin/bash
+# This script tests: pamcut pbmmake
+# Also requires:
+
+
+# Test 1.  Should print 2958909756 124815
+pamcut -top 0 -left 0 -width 260 -height 160 \
+  -pad testimg.ppm | cksum
+
+# Test 2.  Should print 1550940962 10933
+pamcut -top 200 -left 120 -width 40 -height 40 \
+  -pad testimg.ppm | cksum
+
+# Test 3.  Should print 708474423 14
+pamcut -top 5 -left 5 -bottom 5 -right 5 testimg.ppm | cksum
+
+# Test 4.  Should print 3412257956 129
+pbmmake -g 50 50 | pamcut 5 5 30 30 | cksum
diff --git a/test/pamdepth-roundtrip.ok b/test/pamdepth-roundtrip.ok
new file mode 100644
index 00000000..89db1a57
--- /dev/null
+++ b/test/pamdepth-roundtrip.ok
@@ -0,0 +1,8 @@
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+2425386270 41
diff --git a/test/pamdepth-roundtrip.test b/test/pamdepth-roundtrip.test
new file mode 100755
index 00000000..0c7cb8b0
--- /dev/null
+++ b/test/pamdepth-roundtrip.test
@@ -0,0 +1,13 @@
+#! /bin/bash
+# This script tests: pamdepth pgmtopbm
+# Also requires:
+
+
+for i in 300 500 1023 4095 5000 16383 65535
+do
+pamdepth $i testimg.ppm | \
+  pamdepth 255 | cksum
+done
+
+pamdepth 255 testgrid.pbm | pamdepth 1 | \
+  pgmtopbm -th -val=0.5 | cksum
diff --git a/test/pamdice-roundtrip.ok b/test/pamdice-roundtrip.ok
new file mode 100644
index 00000000..82eac5a8
--- /dev/null
+++ b/test/pamdice-roundtrip.ok
@@ -0,0 +1 @@
+1926073387 101484
diff --git a/test/pamdice-roundtrip.test b/test/pamdice-roundtrip.test
new file mode 100755
index 00000000..28997742
--- /dev/null
+++ b/test/pamdice-roundtrip.test
@@ -0,0 +1,12 @@
+#! /bin/bash
+# This script tests: pamdice pamundice
+# Also requires:
+
+
+tmpdir=${tmpdir:-/tmp}
+fname_stem=${tmpdir}/a
+
+pamdice testimg.ppm -outstem=${fname_stem} -width=50 -height=40
+pamundice ${fname_stem}_%1d_%1a.ppm -down=4 -across=5 | cksum
+
+rm ${fname_stem}_?_?.ppm
diff --git a/test/pamditherbw.ok b/test/pamditherbw.ok
new file mode 100644
index 00000000..e8186c24
--- /dev/null
+++ b/test/pamditherbw.ok
@@ -0,0 +1,4 @@
+1316122660 33894
+3342429190 33894
+3325147568 33894
+4124728025 33894
diff --git a/test/pamditherbw.test b/test/pamditherbw.test
new file mode 100755
index 00000000..3f377f81
--- /dev/null
+++ b/test/pamditherbw.test
@@ -0,0 +1,32 @@
+#! /bin/bash
+# This script tests: pamditherbw
+# Also requires: pamchannel pamtopnm
+
+
+tmpdir=${tmpdir:-/tmp}
+test_red=${tmpdir}/testimg.red
+
+# Test 1.  Simple threshold
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  tee ${test_red} | \
+  pamditherbw -threshold -val=0.5 | cksum
+
+# Test 2.  Floyd-Steinberg
+#pamditherbw -floyd -val=0.5 ${test_red} | cksum
+
+# Test 3. Atkinson
+#pamditherbw -atkinson -val=0.5 ${test_red} | cksum
+
+# Test 4. Hilbert
+pamditherbw -hilbert ${test_red} | cksum
+
+# Test 5. Dither-8
+pamditherbw -dither8 ${test_red} | cksum
+
+# Test 6. Cluster4
+pamditherbw -cluster4 ${test_red} | cksum
+
+# Test 7. Atkinson
+#pamditherbw -atkinson -val=0.5 ${test_red} | cksum
+
+rm ${test_red}
diff --git a/test/pamedge.ok b/test/pamedge.ok
new file mode 100644
index 00000000..f1df7b80
--- /dev/null
+++ b/test/pamedge.ok
@@ -0,0 +1,77 @@
+P1
+60 75
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111000000000000000000000000000000000011111111111
+111111111111110000000000000000000000000000000000001111111111
+111111111111110001111111111111111111111111111110000111111111
+111111111111110011111111111111111111111111111111000111111111
+111111111111110001111111111111111111111111111111100111111111
+111111111111110000011111110000000000000000001111100111111111
+111111111111111000001111100000000000000000000111100111111111
+111111111111111110000111000011111111111111000011100111111111
+111111111111111111000111000111111111111111100011100111111111
+111111111111111111100111001111111111111111110001000111111111
+111111111111111111100111001111111111111111110000000111111111
+111111111111111111100111001111111111111111111000001111111111
+111111111111111111100111001111111111111111111111111111111111
+111111111111111111100111001111111111111111111111111111111111
+111111111111111111100111001111111111111111111111111111111111
+111111111111111111100111001111111111111100000111111111111111
+111111111111111111100111001111111111111000000011111111111111
+111111111111111111100111001111111111111000100011111111111111
+111111111111111111100111000111111111110001110011111111111111
+111111111111111111100111000011111111100001110011111111111111
+111111111111111111100111100000000000000011110011111111111111
+111111111111111111100111110000000000000111110011111111111111
+111111111111111111100111111111111111111111110011111111111111
+111111111111111111100111111111111111111111110011111111111111
+111111111111111111100111111111111111111111110011111111111111
+111111111111111111100111110000000000000111110011111111111111
+111111111111111111100111100000000000000011110011111111111111
+111111111111111111100111000011111111100001110011111111111111
+111111111111111111100111000111111111110001110011111111111111
+111111111111111111100111001111111111111000100011111111111111
+111111111111111111100111001111111111111000000011111111111111
+111111111111111111100111001111111111111100000111111111111111
+111111111111111111100111001111111111111111111111111111111111
+111111111111111111100111001111111111111111111111111111111111
+111111111111111111100111001111111111111111111111111111111111
+111111111111111111100111001111111111111111111111111111111111
+111111111111111111100111001111111111111111111111111111111111
+111111111111111111100111001111111111111111111111111111111111
+111111111111111111000111000111111111111111111111111111111111
+111111111111111110000111000011111111111111111111111111111111
+111111111111111000001111100000000001111111111111111111111111
+111111111111110000011111110000000000111111111111111111111111
+111111111111110001111111111111111000111111111111111111111111
+111111111111110011111111111111111100111111111111111111111111
+111111111111110001111111111111111000111111111111111111111111
+111111111111110000000000000000000000111111111111111111111111
+111111111111111000000000000000000001111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
+111111111111111111111111111111111111111111111111111111111111
diff --git a/test/pamedge.test b/test/pamedge.test
new file mode 100755
index 00000000..c63b30cc
--- /dev/null
+++ b/test/pamedge.test
@@ -0,0 +1,8 @@
+#! /bin/bash
+# This script tests: pamedge
+# Also requires: pbmpscale pbmtext pgmtopbm pgmtopgm ppmtopgm
+
+
+pbmtext " F " -nom | pbmpscale 5 | \
+pgmtopgm | pamedge | \
+ppmtopgm | pgmtopbm -th -val=.5 -plain
diff --git a/test/pamenlarge.ok b/test/pamenlarge.ok
new file mode 100644
index 00000000..a2408871
--- /dev/null
+++ b/test/pamenlarge.ok
@@ -0,0 +1,4 @@
+3424505894 913236
+3763267672 304422
+3342398172 297
+398497872 6806
diff --git a/test/pamenlarge.test b/test/pamenlarge.test
new file mode 100755
index 00000000..3820f47e
--- /dev/null
+++ b/test/pamenlarge.test
@@ -0,0 +1,14 @@
+#! /bin/bash
+# This script tests: pamenlarge
+# Also requires: pamchannel pamseq pamtopnm
+
+
+# Test 1.  Should print 3424505894 913236
+pamenlarge 3 testimg.ppm | cksum
+# Test 2.  Should print 3763267672 304422
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  pamenlarge 3 | cksum
+# Test 3.  Should print 3342398172 297
+pamenlarge 3 testgrid.pbm | cksum
+# Test 4.  Should print 398497872 6806
+pamseq 3 4 | pamtopnm -assume | pamenlarge 3 -plain | cksum
diff --git a/test/pamfile.ok b/test/pamfile.ok
new file mode 100644
index 00000000..57cc8cfd
--- /dev/null
+++ b/test/pamfile.ok
@@ -0,0 +1,5 @@
+testimg.ppm:	PPM raw, 227 by 149  maxval 255
+testgrid.pbm:	PBM raw, 14 by 16
+stdin:	PGM raw, 227 by 149  maxval 255
+stdin:	PAM, 227 by 149 by 1 maxval 255
+    Tuple type: GRAYSCALE
diff --git a/test/pamfile.test b/test/pamfile.test
new file mode 100755
index 00000000..ac927172
--- /dev/null
+++ b/test/pamfile.test
@@ -0,0 +1,9 @@
+#! /bin/bash
+# This script tests: pamfile
+# Also requires: pamchannel pamtopnm
+
+
+pamfile testimg.ppm
+pamfile testgrid.pbm
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | pamfile
+pamchannel -tupletype="GRAYSCALE" -infile=testimg.ppm 0 | pamfile
diff --git a/test/pamflip-roundtrip.ok b/test/pamflip-roundtrip.ok
new file mode 100644
index 00000000..653ab007
--- /dev/null
+++ b/test/pamflip-roundtrip.ok
@@ -0,0 +1,12 @@
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
diff --git a/test/pamflip-roundtrip.test b/test/pamflip-roundtrip.test
new file mode 100755
index 00000000..c5a34ad9
--- /dev/null
+++ b/test/pamflip-roundtrip.test
@@ -0,0 +1,35 @@
+#! /bin/bash
+# This script tests: pamflip
+# Also requires:
+
+
+pamflip -lr testimg.ppm | pamflip -lr | cksum
+pamflip -tb testimg.ppm | pamflip -tb | cksum
+pamflip -r180 testimg.ppm | \
+  pamflip -r180 | cksum
+pamflip -xy testimg.ppm | pamflip -xy | cksum
+pamflip -r90 testimg.ppm | \
+  pamflip -r90 | \
+  pamflip -r90 | \
+  pamflip -r90 | cksum
+pamflip -r270 testimg.ppm | \
+  pamflip -r270 | \
+  pamflip -r270 | \
+  pamflip -r270 | cksum
+
+pamflip -lr testgrid.pbm | \
+  pamflip -lr | cksum
+pamflip -tb testgrid.pbm | \
+  pamflip -tb | cksum
+pamflip -r180 testgrid.pbm | \
+  pamflip -r180 | cksum
+pamflip -xy testgrid.pbm | \
+  pamflip -xy | cksum
+pamflip -r90 testgrid.pbm | \
+  pamflip -r90 | \
+  pamflip -r90 | \
+  pamflip -r90 | cksum
+pamflip -r270 testgrid.pbm | \
+  pamflip -r270 | \
+  pamflip -r270 | \
+  pamflip -r270 | cksum
diff --git a/test/pamflip1.ok b/test/pamflip1.ok
new file mode 100644
index 00000000..64e0407e
--- /dev/null
+++ b/test/pamflip1.ok
@@ -0,0 +1,5 @@
+2116496681 101484
+217037000 101484
+2052917888 101484
+3375384165 41
+604323149 41
diff --git a/test/pamflip1.test b/test/pamflip1.test
new file mode 100755
index 00000000..840cac73
--- /dev/null
+++ b/test/pamflip1.test
@@ -0,0 +1,15 @@
+#! /bin/bash
+# This script tests: pamflip
+# Also requires:
+
+
+# Test 1.  Should print 2116496681 101484
+pamflip -lr testimg.ppm | cksum
+# Test 2.  Should print 217037000 101484
+pamflip -cw testimg.ppm | cksum
+# Test 3.  Should print 2052917888 101484
+pamflip -tb testimg.ppm | cksum
+# Test 4.  Should print 3375384165 41
+pamflip -lr testgrid.pbm | cksum
+# Test 5.  Should print 604323149 41
+pamflip -tb testgrid.pbm | cksum
diff --git a/test/pamflip2.ok b/test/pamflip2.ok
new file mode 100644
index 00000000..d2ea501e
--- /dev/null
+++ b/test/pamflip2.ok
@@ -0,0 +1,3 @@
+490797850 37
+3727293411 37
+3511745816 37
diff --git a/test/pamflip2.test b/test/pamflip2.test
new file mode 100755
index 00000000..4dddb770
--- /dev/null
+++ b/test/pamflip2.test
@@ -0,0 +1,17 @@
+#! /bin/bash
+# This script tests: pamflip
+# Also requires:
+
+
+# Failure message
+## Failure with this test indicates a problem with pamflip transpositions
+## (90 degree flips) when input is PBM.  This operation is done by an
+## optional SSE routine.  If you make a wrong choice during configure,
+## this test will fail.
+
+# Test 1.  Should print 490797850 37
+pamflip -cw testgrid.pbm | cksum
+# Test 1.  Should print 3727293411 37
+pamflip -ccw testgrid.pbm | cksum
+# Test 2.  Should print 3511745816 37
+pamflip -xy testgrid.pbm | cksum
diff --git a/test/pamseq.ok b/test/pamseq.ok
new file mode 100644
index 00000000..52bb3dd8
--- /dev/null
+++ b/test/pamseq.ok
@@ -0,0 +1 @@
+3929266994 304
diff --git a/test/pamseq.test b/test/pamseq.test
new file mode 100755
index 00000000..e7b8060d
--- /dev/null
+++ b/test/pamseq.test
@@ -0,0 +1,6 @@
+#! /bin/bash
+# This script tests: pamseq
+# Also requires:
+
+
+pamseq 1 255 | cksum
diff --git a/test/pamslice-roundtrip.ok b/test/pamslice-roundtrip.ok
new file mode 100644
index 00000000..eae64745
--- /dev/null
+++ b/test/pamslice-roundtrip.ok
@@ -0,0 +1,4 @@
+2425386270 41
+914327477 4864
+914327477 4864
+914327477 4864
diff --git a/test/pamslice-roundtrip.test b/test/pamslice-roundtrip.test
new file mode 100755
index 00000000..6cb5533f
--- /dev/null
+++ b/test/pamslice-roundtrip.test
@@ -0,0 +1,75 @@
+#! /bin/bash
+# This script tests: pamslice pamdeinterlace
+# Also requires: pamcut pamtopnm pamflip
+
+
+# Test 1.
+# Slice rows, one by one, out of testgrid.pbm.
+# Add header and reconstruct pbm image.
+# Note that in pamslice output 0 is white and 1 is black: opposite of PBM
+# Should print 2425386270 41
+
+(echo "P1"
+ echo "14 16"
+ seq 0 15 | while read i;
+     do
+     pamslice -row=$i testgrid.pbm
+     done | awk '{print $2}' | sed 'y/01/10/' ) \
+ | pamtopnm | cksum
+
+# Test 2.
+# Slice rows, one by one, out of ppm test image
+# We take a part out of testimg.ppm with pamcut for processing the
+# whole image takes much time.
+# Add header and reconstruct ppm image.
+# Should print 914327477 4864
+
+tmpdir=${tmpdir:-/tmp}
+
+test4933_ppm=${tmpdir}/test4933.ppm
+
+pamcut 50 50 49 33 testimg.ppm > ${test4933_ppm}
+
+(echo "P3"
+ echo "49 33"
+ echo "255"
+ seq 0 32 | while read i;
+     do
+     pamslice -row=$i ${test4933_ppm}
+     done | awk '{print $2, $3, $4}' ) \
+ | pamtopnm | cksum
+
+# Same as above test 2, but take cols instead of rows.
+# Should print 914327477 4864
+
+(echo "P3"
+ echo "33 49"
+ echo "255"
+ seq 0 48 | while read i;
+     do
+     pamslice -col=$i ${test4933_ppm}
+     done | awk '{print $2, $3, $4}' ) \
+ | pamflip -xy | cksum
+
+# Test 4.
+# Divide input image into two with pamdeinterlace and recombine.
+
+testeven_ppm=${tmpdir}/testeven.ppm
+testodd_ppm=${tmpdir}/testodd.ppm
+
+pamdeinterlace -takeodd ${test4933_ppm} > ${testodd_ppm}
+pamdeinterlace -takeeven ${test4933_ppm} > ${testeven_ppm}
+
+(echo "P3"
+ echo "49 33"
+ echo "255"
+ ( seq 0 15 | while read i;
+     do
+     pamslice -row=$i ${testeven_ppm}
+     pamslice -row=$i ${testodd_ppm}
+     done
+     pamslice -row=16 ${testeven_ppm};
+ )  | awk '{print $2, $3, $4}' ) \
+ | pamtopnm | cksum
+
+rm ${test4933_ppm} ${testodd_ppm} ${testeven_ppm}
diff --git a/test/pamsumm.ok b/test/pamsumm.ok
new file mode 100644
index 00000000..0643081b
--- /dev/null
+++ b/test/pamsumm.ok
@@ -0,0 +1,8 @@
+56
+0
+1
+0.250000
+10772432
+15
+255
+106.164760
diff --git a/test/pamsumm.test b/test/pamsumm.test
new file mode 100755
index 00000000..a99dea0e
--- /dev/null
+++ b/test/pamsumm.test
@@ -0,0 +1,14 @@
+#! /bin/bash
+# This script tests: pamsumm
+# Also requires:
+
+
+for type in -sum -min -max -mean
+  do
+  pamsumm -brief $type testgrid.pbm
+  done
+
+for type in -sum -min -max -mean
+  do
+  pamsumm -brief $type testimg.ppm
+  done
diff --git a/test/pamtopam.ok b/test/pamtopam.ok
new file mode 100644
index 00000000..dcb597d3
--- /dev/null
+++ b/test/pamtopam.ok
@@ -0,0 +1,16 @@
+P7
+WIDTH 227
+HEIGHT 149
+DEPTH 3
+MAXVAL 255
+TUPLTYPE RGB
+ENDHDR
+P7
+WIDTH 14
+HEIGHT 16
+DEPTH 1
+MAXVAL 1
+TUPLTYPE BLACKANDWHITE
+ENDHDR
+1873848880 101532
+2260748250 293
diff --git a/test/pamtopam.test b/test/pamtopam.test
new file mode 100755
index 00000000..394d9904
--- /dev/null
+++ b/test/pamtopam.test
@@ -0,0 +1,10 @@
+#! /bin/bash
+# This script tests: pamtopam
+# Also requires:
+
+
+pamtopam < testimg.ppm  | sed '/ENDHDR/q'
+pamtopam < testgrid.pbm | sed '/ENDHDR/q'
+
+pamtopam < testimg.ppm   | cksum
+pamtopam < testgrid.pbm  | cksum
diff --git a/test/pbm-misc-converters.ok b/test/pbm-misc-converters.ok
new file mode 100644
index 00000000..a137102f
--- /dev/null
+++ b/test/pbm-misc-converters.ok
@@ -0,0 +1,27 @@
+1638343024 43
+2141128209 77
+2542756600 120
+3102495729 32
+2414506375 47
+3241517214 145
+1454090165 46
+1436169407 46
+1454090165 46
+2912484298 46
+3576177652 52
+1478164284 52
+3213223606 141
+3213223606 141
+3213223606 141
+1463148415 108
+203901789 30
+3732005859 92
+2459345477 86
+424535246 92
+609530223 252
+4195053594 248
+2602382240 43
+129620534 361
+2256096096 80
+1349121911 149
+3955750161 284701
diff --git a/test/pbm-misc-converters.test b/test/pbm-misc-converters.test
new file mode 100755
index 00000000..aa71489b
--- /dev/null
+++ b/test/pbm-misc-converters.test
@@ -0,0 +1,40 @@
+#! /bin/bash
+# This script tests: pbmto10x pbmto4425 pbmtoascii pbmtobbnbg
+# This script tests: pbmtodjvurle pbmtoepson pbmtogo pbmtoibm23xx
+# This script tests: pbmtolj pbmtoln03 pbmtomatrixorbital pbmtonokia
+# This script tests: pbmtoplot pbmtoptx pbmtozinc
+# Also requires: pbmpage
+
+# Note: one-way test.
+# These converters do not have counterparts that work in the opposite
+# direction.  We check whether the output is unchanged from older
+# versions.
+
+pbmto10x             testgrid.pbm | cksum
+pbmto4425            testgrid.pbm | cksum
+pbmtoascii           testgrid.pbm | cksum
+pbmtoascii -2x4      testgrid.pbm | cksum
+pbmtobbnbg         < testgrid.pbm | cksum
+pbmtodjvurle         testgrid.pbm | cksum
+pbmtoepson           testgrid.pbm | cksum
+pbmtoepson -protocol=escp   testgrid.pbm | cksum
+pbmtoepson -protocol=escp9  testgrid.pbm | cksum
+pbmtoepson -nonadjacent     testgrid.pbm | cksum
+pbmtogo              testgrid.pbm | cksum
+pbmtoibm23xx -xres=60 -yres=60 testgrid.pbm | cksum
+pbmtolj              testgrid.pbm | cksum
+pbmtolj  -packbits   testgrid.pbm | cksum
+pbmtolj  -compress   testgrid.pbm | cksum
+pbmtoln03            testgrid.pbm | cksum
+pbmtomatrixorbital < testgrid.pbm | cksum
+pbmtonokia -fmt HEX_NOL testgrid.pbm | cksum
+pbmtonokia -fmt HEX_NGG testgrid.pbm | cksum
+pbmtonokia -fmt HEX_NPM testgrid.pbm | cksum
+pbmtonokia -fmt NOL  testgrid.pbm | cksum
+pbmtonokia -fmt NGG  testgrid.pbm | cksum
+pbmtonokia -fmt NPM  testgrid.pbm | cksum
+pbmtoplot            testgrid.pbm | cksum
+pbmtoptx             testgrid.pbm | cksum
+pbmtozinc            testgrid.pbm | cksum
+
+(pbmpage 1; pbmpage 2; pbmpage 3) | pbmtoppa | cksum
diff --git a/test/pbmclean.ok b/test/pbmclean.ok
new file mode 100644
index 00000000..71b622d0
--- /dev/null
+++ b/test/pbmclean.ok
@@ -0,0 +1,73 @@
+P1
+7 7
+1111111
+1111111
+1101011
+1110111
+1101011
+1111111
+1111111
+P1
+7 7
+1111111
+1111111
+1101011
+1110111
+1101011
+1111111
+1111111
+P1
+7 7
+1111111
+1111111
+1101011
+1110111
+1101011
+1111111
+1111111
+P1
+7 7
+0111110
+1111111
+1101011
+1110111
+1101011
+1111111
+0111110
+P1
+7 7
+0111110
+1111111
+1101011
+1110111
+1101011
+1111111
+0111110
+P1
+7 7
+0000000
+0111110
+0100010
+0100010
+0100010
+0111110
+0000000
+P1
+7 7
+0000000
+0110110
+0100010
+0000000
+0100010
+0110110
+0000000
+P1
+7 7
+0000000
+0000000
+0000000
+0000000
+0000000
+0000000
+0000000
+760076056 4210813
diff --git a/test/pbmclean.test b/test/pbmclean.test
new file mode 100755
index 00000000..a8e469b1
--- /dev/null
+++ b/test/pbmclean.test
@@ -0,0 +1,19 @@
+#! /bin/bash
+# This script tests: pbmclean
+# Also requires: pbmmake pbmpage pnmmargin pnmpad
+
+
+tmpdir=${tmpdir:-/tmp}
+test_pbm=${tmpdir}/test.pbm
+
+pbmmake -g 3 3 | pnmmargin -black 2 > ${test_pbm}
+
+for n in 1 2 3 4 5 6 7 8
+do
+pbmclean -min=$n -black -plain ${test_pbm}
+done
+
+rm ${test_pbm}
+
+# Should print 760076056 4210813
+pbmpage 1 | pbmclean -black | cksum
diff --git a/test/pbmmake.ok b/test/pbmmake.ok
new file mode 100644
index 00000000..754eefdf
--- /dev/null
+++ b/test/pbmmake.ok
@@ -0,0 +1,43 @@
+P11 10
+P11 11
+P11 10
+P12 20000
+P12 21111
+P12 20110
+P13 3000000000
+P13 3111111111
+P13 3010101010
+P14 40000000000000000
+P14 41111111111111111
+P14 40101101001011010
+P15 50000000000000000000000000
+P15 51111111111111111111111111
+P15 50101010101010101010101010
+P16 6000000000000000000000000000000000000
+P16 6111111111111111111111111111111111111
+P16 6010101101010010101101010010101101010
+P17 70000000000000000000000000000000000000000000000000
+P17 71111111111111111111111111111111111111111111111111
+P17 70101010101010101010101010101010101010101010101010
+P18 80000000000000000000000000000000000000000000000000000000000000000
+P18 81111111111111111111111111111111111111111111111111111111111111111
+P18 80101010110101010010101011010101001010101101010100101010110101010
+4058563256 45
+3969089344 105
+702117756 189
+1343035453 234
+2621551326 363
+1487646353 522
+3096107498 597
+2833028503 801
+4244475883 891
+294944575 1140
+244734977 1419
+2956143361 1539
+2229243214 1863
+1123259599 2217
+3622140854 2367
+194247095 2766
+1824232358 2931
+3651864954 3375
+3302595397 3849
diff --git a/test/pbmmake.test b/test/pbmmake.test
new file mode 100755
index 00000000..4b18e3ea
--- /dev/null
+++ b/test/pbmmake.test
@@ -0,0 +1,18 @@
+#! /bin/bash
+# This script tests: pbmmake
+# Also requires:
+
+
+for i in `seq 1 8`
+do
+for color in -w -b -g
+do
+pbmmake -plain $color $i $i | tr -d '\n'; echo
+done
+done
+for i in `seq 8 5 98`
+do
+ ( pbmmake -w $i $i ;
+  pbmmake -b $i $i ;
+  pbmmake  -g $i $i ) | cksum
+done
diff --git a/test/pbmminkowski.ok b/test/pbmminkowski.ok
new file mode 100644
index 00000000..71a909ce
--- /dev/null
+++ b/test/pbmminkowski.ok
@@ -0,0 +1,23 @@
+   tiles:	1
+ x-edges:	2
+ y-edges:	2
+vertices:	4
+    area:	1
+perimeter:	4
+ eulerchi:	1
+
+   tiles:	5
+ x-edges:	10
+ y-edges:	10
+vertices:	16
+    area:	5
+perimeter:	20
+ eulerchi:	1
+
+   tiles:	56
+ x-edges:	112
+ y-edges:	112
+vertices:	224
+    area:	56
+perimeter:	224
+ eulerchi:	56
diff --git a/test/pbmminkowski.test b/test/pbmminkowski.test
new file mode 100755
index 00000000..389ff44a
--- /dev/null
+++ b/test/pbmminkowski.test
@@ -0,0 +1,11 @@
+#! /bin/bash
+# This script tests: pbmminkowski
+# Also requires: pbmmake pnmmargin pnmpad
+
+
+pbmmake -w 1 1 | pnmmargin -b 1 | \
+  pbmminkowski
+echo
+pbmmake -g 3 3 | pbmminkowski
+echo
+pbmminkowski testgrid.pbm
diff --git a/test/pbmpage.ok b/test/pbmpage.ok
new file mode 100644
index 00000000..7f68da74
--- /dev/null
+++ b/test/pbmpage.ok
@@ -0,0 +1,3 @@
+550172004 4210813
+4142746975 4210813
+2347597649 4210813
diff --git a/test/pbmpage.test b/test/pbmpage.test
new file mode 100755
index 00000000..e9bfe352
--- /dev/null
+++ b/test/pbmpage.test
@@ -0,0 +1,8 @@
+#! /bin/bash
+# This script tests: pbmpage
+# Also requires:
+
+
+pbmpage 1 | cksum
+pbmpage 2 | cksum
+pbmpage 3 | cksum
diff --git a/test/pbmpscale.ok b/test/pbmpscale.ok
new file mode 100644
index 00000000..e25d4027
--- /dev/null
+++ b/test/pbmpscale.ok
@@ -0,0 +1,50 @@
+P1
+21 45
+000000000000000000000
+000000000000000000000
+000000000000000000000
+000000000000000000000
+000000000000000000000
+000000000000000000000
+000000000000000000000
+000000000000000000000
+000000000000000000000
+111111111111111111110
+111111111111111111111
+111111111111111111111
+001111100000000001111
+000111000000000000111
+000111000000000000111
+000111000000000000000
+000111000000000000000
+000111000000000000000
+000111000000000111000
+000111000000000111000
+000111100000001111000
+000111111111111111000
+000111111111111111000
+000111111111111111000
+000111100000001111000
+000111000000000111000
+000111000000000111000
+000111000000000000000
+000111000000000000000
+000111000000000000000
+000111000000000000000
+000111000000000000000
+001111100000000000000
+111111111111000000000
+111111111111000000000
+111111111111000000000
+000000000000000000000
+000000000000000000000
+000000000000000000000
+000000000000000000000
+000000000000000000000
+000000000000000000000
+000000000000000000000
+000000000000000000000
+000000000000000000000
+2150868536 457
+3761734242 1065
+3462719777 1803
diff --git a/test/pbmpscale.test b/test/pbmpscale.test
new file mode 100755
index 00000000..42046c74
--- /dev/null
+++ b/test/pbmpscale.test
@@ -0,0 +1,12 @@
+#! /bin/bash
+# This script tests: pbmpscale
+# Also requires: pamenlarge pbmtext
+
+
+pbmtext -nomargin "F" | pbmpscale 3 -plain
+
+for i in 2 3 4
+do
+pamenlarge 2 testgrid.pbm | \
+  pbmpscale $i | cksum
+done
diff --git a/test/pbmtext.ok b/test/pbmtext.ok
new file mode 100644
index 00000000..438fe7b8
--- /dev/null
+++ b/test/pbmtext.ok
@@ -0,0 +1,21 @@
+1427751145 328
+1975911793 129
+3145408696 273
+2662867350 328
+3630387453 129
+1251480181 297
+1522829573 299
+1333433014 114
+4128014259 249
+2350105502 270
+2044333794 84
+2845861861 177
+2754777566 416
+3452484090 174
+2294575230 297
+67558248 387
+1647907430 159
+2846042958 249
+307551150 328
+584153820 114
+3355717231 177
diff --git a/test/pbmtext.test b/test/pbmtext.test
new file mode 100755
index 00000000..dffb2cb4
--- /dev/null
+++ b/test/pbmtext.test
@@ -0,0 +1,12 @@
+#! /bin/bash
+# This script tests: pbmtext
+# Also requires:
+
+
+for i in 0123456789 abcdefghijk lmnopqrst uzwxyz ABCDEFGHIJK LMNOPQRST UVWXYZ
+do
+for flags in "" "-nom" "-builtin fixed"
+do
+echo $i | pbmtext $flags | cksum
+done
+done
diff --git a/test/pbmtog3.ok b/test/pbmtog3.ok
new file mode 100644
index 00000000..a0768f95
--- /dev/null
+++ b/test/pbmtog3.ok
@@ -0,0 +1,11 @@
+3697098186 144
+1248301383 122
+686713716 144
+215463240 122
+28792587 47
+277456854 32
+28792587 47
+871281767 162
+3736247115 62
+2820255307 2191856
+4159089282 2226575
diff --git a/test/pbmtog3.test b/test/pbmtog3.test
new file mode 100755
index 00000000..15c0cdf9
--- /dev/null
+++ b/test/pbmtog3.test
@@ -0,0 +1,29 @@
+#! /bin/bash
+# This script tests: pbmtog3
+# Also requires: pbmmake
+
+
+# Test 1.  Should print 3697098186 144
+pbmtog3 testgrid.pbm | cksum
+# Test 2.  Should print 1248301383 122
+pbmtog3 -nofixedwidth testgrid.pbm | cksum
+# Test 3.  Should print 686713716 144
+pbmtog3 -reverse testgrid.pbm | cksum
+# Test 4.  Should print 215463240 122
+pbmtog3 -nofixedwidth -reverse testgrid.pbm | cksum
+# Test 5.  Should print 28792587 47
+pbmmake -w 10 10 | pbmtog3 | cksum
+# Test 6.  Should print 277456854 32
+pbmmake -w 10 10 | \
+  pbmtog3 -nofixedwidth | cksum
+# Test 7.  Should print 28792587 47
+pbmmake -w 10000 10 | pbmtog3 | cksum
+# Test 8.  Should print 871281767 162
+pbmmake -w 10000 10 | \
+  pbmtog3 -nofixedwidth | cksum
+# Test 9.  Should print 3736247115 62
+pbmmake -b 10 10 | pbmtog3 | cksum
+# Test 10.  Should print 2820255307 2191856
+pbmmake -g 1700 2286 | pbmtog3 | cksum
+# Test 11.  Should print 4159089282 2226575
+pbmmake -g 1800 2286 | pbmtog3 | cksum
diff --git a/test/pbmupc.ok b/test/pbmupc.ok
new file mode 100644
index 00000000..3e58f409
--- /dev/null
+++ b/test/pbmupc.ok
@@ -0,0 +1 @@
+2619309127 10172
diff --git a/test/pbmupc.test b/test/pbmupc.test
new file mode 100755
index 00000000..275117d5
--- /dev/null
+++ b/test/pbmupc.test
@@ -0,0 +1,9 @@
+#! /bin/bash
+# This script tests: pbmupc
+# Also requires:
+
+
+for type in -s1 -s2
+do
+pbmupc $type 0 72890 00011
+done | cksum
diff --git a/test/pcx-roundtrip.ok b/test/pcx-roundtrip.ok
new file mode 100644
index 00000000..968f46b9
--- /dev/null
+++ b/test/pcx-roundtrip.ok
@@ -0,0 +1,5 @@
+1926073387 101484
+369063776 101484
+369063776 101484
+369063776 101484
+829921912 685
diff --git a/test/pcx-roundtrip.test b/test/pcx-roundtrip.test
new file mode 100755
index 00000000..3dba42b9
--- /dev/null
+++ b/test/pcx-roundtrip.test
@@ -0,0 +1,45 @@
+#! /bin/bash
+# This script tests: ppmtopcx pcxtoppm
+# Also requires: pnmremap
+
+
+tmpdir=${tmpdir:-/tmp}
+pcxstd_ppm=${tmpdir}/pcxstd_ppm
+testpcx_ppm=${tmpdir}/test-pcx.ppm
+
+cat > ${pcxstd_ppm} << EOF
+P3
+16 1
+255
+  0   0   0
+  0   0 170
+  0 170   0
+  0 170 170
+170   0   0
+170   0 170
+170 170   0
+170 170 170
+ 85  85  85
+ 85  85 255
+ 85 255  85
+ 85 255 255
+255  85  85
+255  85 255
+255 255  85
+255 255 255
+EOF
+
+# Test 1. Should print 1926073387 101484
+ppmtopcx testimg.ppm | pcxtoppm | cksum
+
+# Test 2.  Should print 369063776 101484 three times
+pnmremap testimg.ppm -mapfile=${pcxstd_ppm} | tee ${testpcx_ppm} | cksum
+ppmtopcx -stdpalette -packed ${testpcx_ppm} | pcxtoppm | cksum
+ppmtopcx -stdpalette -packed -8bit ${testpcx_ppm} | pcxtoppm | cksum
+
+rm ${testpcx_ppm} ${pcxstd_ppm}
+
+# Test 3. Should print 829921912 685 which is the
+# result of:
+# pgmtoppm < testgrid.pbm | cksum
+ppmtopcx -stdpalette -packed testgrid.pbm | pcxtoppm | cksum
diff --git a/test/pfm-roundtrip.ok b/test/pfm-roundtrip.ok
new file mode 100644
index 00000000..82eac5a8
--- /dev/null
+++ b/test/pfm-roundtrip.ok
@@ -0,0 +1 @@
+1926073387 101484
diff --git a/test/pfm-roundtrip.test b/test/pfm-roundtrip.test
new file mode 100755
index 00000000..f1b5d418
--- /dev/null
+++ b/test/pfm-roundtrip.test
@@ -0,0 +1,8 @@
+#! /bin/bash
+# This script tests: pamtopfm pfmtopam
+# Also requires: pamtopnm
+
+
+# Should print 1926073387 101484, cksum of testimg.ppm
+pamtopfm testimg.ppm | pfmtopam | \
+  pamtopnm | cksum
diff --git a/test/pgmbentley.ok b/test/pgmbentley.ok
new file mode 100644
index 00000000..2ae59958
--- /dev/null
+++ b/test/pgmbentley.ok
@@ -0,0 +1 @@
+4127286153 16399
diff --git a/test/pgmbentley.test b/test/pgmbentley.test
new file mode 100755
index 00000000..0429d54b
--- /dev/null
+++ b/test/pgmbentley.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pgmbentley
+# Also requires: pgmramp
+
+
+# Test. Should produce 4127286153 16399
+pgmramp -rect 128 128 | pgmbentley | cksum
diff --git a/test/pgmhist.ok b/test/pgmhist.ok
new file mode 100644
index 00000000..7d89bb33
--- /dev/null
+++ b/test/pgmhist.ok
@@ -0,0 +1,14 @@
+value count b% w%
+----- ----- ------ ------
+ 0 2 12.5% 100%
+ 1 2 25% 87.5%
+ 2 2 37.5% 75%
+ 3 2 50% 62.5%
+ 4 2 62.5% 50%
+ 5 2 75% 37.5%
+ 6 2 87.5% 25%
+ 8 2 100% 12.5%
+value count b% w%
+----- ----- ------ ------
+ 0 168 75% 100%
+ 255 56 100% 25%
diff --git a/test/pgmhist.test b/test/pgmhist.test
new file mode 100755
index 00000000..42666fbf
--- /dev/null
+++ b/test/pgmhist.test
@@ -0,0 +1,12 @@
+#! /bin/bash
+# This script tests: pgmhist
+# Also requires: pgmramp
+
+
+# Ignore differences in spaces.
+
+pgmramp -maxval=8 -lr 8 2 | pgmhist | \
+  sed -e 's/  */ /g' -e 's/ *$//'
+
+pgmhist testgrid.pbm | \
+  sed -e 's/  */ /g' -e 's/ *$//'
diff --git a/test/pgmmake.ok b/test/pgmmake.ok
new file mode 100644
index 00000000..b9a03af2
--- /dev/null
+++ b/test/pgmmake.ok
@@ -0,0 +1,2 @@
+3662611538 2513
+3109612402 5012
diff --git a/test/pgmmake.test b/test/pgmmake.test
new file mode 100755
index 00000000..4a3c4842
--- /dev/null
+++ b/test/pgmmake.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pgmmake
+# Also requires:
+
+
+pgmmake 1 50 50 | cksum
+pgmmake .2 50 100 -maxval=5 | cksum
diff --git a/test/pgmnoise.ok b/test/pgmnoise.ok
new file mode 100644
index 00000000..138218c2
--- /dev/null
+++ b/test/pgmnoise.ok
@@ -0,0 +1 @@
+2005134911 10015
diff --git a/test/pgmnoise.test b/test/pgmnoise.test
new file mode 100755
index 00000000..0b38553b
--- /dev/null
+++ b/test/pgmnoise.test
@@ -0,0 +1,23 @@
+#! /bin/bash
+# This script tests: pgmnoise
+# Also requires:
+
+
+# We first check whether random number generator is glibc rand().
+# If not, this test is skipped.
+
+testrandom
+
+case $? in
+   81)
+        # Should print: 1663614689 10015
+        pgmnoise --randomseed=0 100 100 | cksum ;;
+
+        # Any additional tests go here.
+
+   8[02-9] | 90)
+        echo "Skipping: random number generator is not glibc." 1>&2
+        exit 80;;
+
+   *)   exit 1;;  # testrandom failed
+esac
diff --git a/test/pgmramp.ok b/test/pgmramp.ok
new file mode 100644
index 00000000..989ef7d4
--- /dev/null
+++ b/test/pgmramp.ok
@@ -0,0 +1,40 @@
+P2
+4 4
+6
+0 2 4 6
+0 2 4 6
+0 2 4 6
+0 2 4 6
+P2
+4 4
+6
+0 0 0 0
+2 2 2 2
+4 4 4 4
+6 6 6 6
+P2
+4 4
+6
+0 1 3 1
+1 3 4 3
+3 4 6 4
+1 3 4 3
+P2
+4 4
+6
+0 0 0 0
+0 3 4 3
+0 4 6 4
+0 3 4 3
+P2
+4 4
+6
+0 1 2 3
+1 2 3 4
+2 3 4 5
+3 4 5 6
+1777787286 65551
+2046889993 65551
+1975520432 65551
+807973067 65551
+886972785 131087
diff --git a/test/pgmramp.test b/test/pgmramp.test
new file mode 100755
index 00000000..f6f770fe
--- /dev/null
+++ b/test/pgmramp.test
@@ -0,0 +1,15 @@
+#! /bin/bash
+# This script tests: pgmramp
+# Also requires:
+
+
+for type in -lr -tb -rectangle -ellipse -diagonal
+do
+pgmramp -maxval=6 $type 4 4 -plain
+done
+
+for type in -lr -tb -rectangle -ellipse
+do pgmramp $type 256 256 | cksum
+done
+
+pgmramp -diagonal -maxval=510 256 256 | cksum
diff --git a/test/pgmtopgm.ok b/test/pgmtopgm.ok
new file mode 100644
index 00000000..53c25a44
--- /dev/null
+++ b/test/pgmtopgm.ok
@@ -0,0 +1 @@
+729348909 237
diff --git a/test/pgmtopgm.test b/test/pgmtopgm.test
new file mode 100755
index 00000000..e919fdac
--- /dev/null
+++ b/test/pgmtopgm.test
@@ -0,0 +1,6 @@
+#! /bin/bash
+# This script tests: pgmtopgm
+# Also requires:
+
+
+pgmtopgm < testgrid.pbm | cksum
diff --git a/test/pgmtoppm.ok b/test/pgmtoppm.ok
new file mode 100644
index 00000000..55e5af95
--- /dev/null
+++ b/test/pgmtoppm.ok
@@ -0,0 +1,3 @@
+1767073524 779
+1676700883 779
+718580145 779
diff --git a/test/pgmtoppm.test b/test/pgmtoppm.test
new file mode 100755
index 00000000..70df0d97
--- /dev/null
+++ b/test/pgmtoppm.test
@@ -0,0 +1,22 @@
+#! /bin/bash
+# This script tests: pgmtoppm
+# Also requires: pamseq pamtopnm pgmramp
+
+
+tmpdir=${tmpdir:-/tmp}
+
+test_pgm=${tmpdir}/test.pgm
+palette=${tmpdir}/palette
+
+pgmramp -maxval=5 -lr 256 1 >${test_pgm}
+pamseq 3 5 -tupletype=RGB | pamtopnm \
+  >${palette}
+
+# Test 1.
+pgmtoppm green ${test_pgm} | cksum
+
+pgmtoppm yellow-blue ${test_pgm} | cksum
+
+pgmtoppm -map=${palette} ${test_pgm} | cksum
+
+rm ${test_pgm} ${palette}
diff --git a/test/pi3-roundtrip.ok b/test/pi3-roundtrip.ok
new file mode 100644
index 00000000..3fde31b1
--- /dev/null
+++ b/test/pi3-roundtrip.ok
@@ -0,0 +1,2 @@
+3139449799 32011
+2425386270 41
diff --git a/test/pi3-roundtrip.test b/test/pi3-roundtrip.test
new file mode 100755
index 00000000..5700337d
--- /dev/null
+++ b/test/pi3-roundtrip.test
@@ -0,0 +1,16 @@
+#! /bin/bash
+# This script tests: pbmtopi3 pi3topbm
+# Also requires: pbmmake pamcut
+
+
+# The pi3 image format specifies a fixed image size of 640x400.
+# Pbmtopi3 rejects images that do not conform.
+
+# Test
+# Should print: 3139449799 32011
+pbmmake -g 640 400 | pbmtopi3 | pi3topbm | cksum
+
+# Test 2.
+# Should print: 2425386270 41
+pamcut -pad 0 0 640 400 testgrid.pbm | \
+  pbmtopi3 | pi3topbm | pamcut 0 0 14 16 | cksum
diff --git a/test/pict-roundtrip.ok b/test/pict-roundtrip.ok
new file mode 100644
index 00000000..d50b54bc
--- /dev/null
+++ b/test/pict-roundtrip.ok
@@ -0,0 +1 @@
+984199586 101484
diff --git a/test/pict-roundtrip.test b/test/pict-roundtrip.test
new file mode 100755
index 00000000..cd207d48
--- /dev/null
+++ b/test/pict-roundtrip.test
@@ -0,0 +1,8 @@
+#! /bin/bash
+# This script tests: picttoppm ppmtopict
+# Also requires: pamseq pamdepth pamtopnm pnmremap
+
+
+#Test.  Should print 984199586 101484
+pamseq 3 5 -tupletype=RGB | pamdepth 255 | pamtopnm | \
+  pnmremap -mapfile=- testimg.ppm | ppmtopict | picttoppm | cksum
diff --git a/test/png-roundtrip.ok b/test/png-roundtrip.ok
new file mode 100644
index 00000000..28b8c057
--- /dev/null
+++ b/test/png-roundtrip.ok
@@ -0,0 +1,36 @@
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
diff --git a/test/png-roundtrip.test b/test/png-roundtrip.test
new file mode 100755
index 00000000..7e7c4a9b
--- /dev/null
+++ b/test/png-roundtrip.test
@@ -0,0 +1,51 @@
+#! /bin/bash
+# This script tests: pngtopam pnmtopng
+# Also requires:
+
+# Failure message
+## If this test fails and pnm-roundtrip2.test succeeds, it indicates
+## some problem with pnmtopng.
+
+# Test 1.  Should print 1926073387 101484 18 times
+for flags in "" -interlace \
+  -gamma=.45 \
+  -hist \
+  -nofilter \
+  -sub \
+  -up \
+  -avg \
+  -paeth \
+  -compression=9 \
+  "-compression=0 -comp_mem=1 -comp_window=8 -comp_buffer=512" \
+  "-compression=9 -comp_mem=1 -comp_window=15 -comp_buffer=512" \
+  "-compression=9 -comp_mem=1 -comp_window=8 -comp_buffer=512" \
+  "-compression=0 -comp_mem=9 -comp_window=8 -comp_buffer=512" \
+  "-compression=9 -comp_mem=9 -comp_window=15 -comp_buffer=8096" \
+  -comp_strategy=huffman_only \
+  -comp_strategy=filtered \
+  -force
+  do
+pnmtopng testimg.ppm $flags | pngtopam | cksum
+done
+
+# Test 2.  Should print 2425386270 41 18 times
+for flags in "" -interlace \
+  -gamma=.45 \
+  -hist \
+  -nofilter \
+  -sub \
+  -up \
+  -avg \
+  -paeth \
+  -compression=9 \
+  "-compression=0 -comp_mem=1 -comp_window=8 -comp_buffer=512" \
+  "-compression=9 -comp_mem=1 -comp_window=15 -comp_buffer=512" \
+  "-compression=9 -comp_mem=1 -comp_window=8 -comp_buffer=512" \
+  "-compression=0 -comp_mem=9 -comp_window=8 -comp_buffer=512" \
+  "-compression=9 -comp_mem=9 -comp_window=15 -comp_buffer=8096" \
+  -comp_strategy=huffman_only \
+  -comp_strategy=filtered \
+  -force
+  do
+  pnmtopng testgrid.pbm $flags | pngtopam | cksum
+  done
diff --git a/test/png-roundtrip2.ok b/test/png-roundtrip2.ok
new file mode 100644
index 00000000..0e712ce7
--- /dev/null
+++ b/test/png-roundtrip2.ok
@@ -0,0 +1,4 @@
+1926073387 101484
+1926073387 101484
+2425386270 41
+2425386270 41
diff --git a/test/png-roundtrip2.test b/test/png-roundtrip2.test
new file mode 100755
index 00000000..af2ad029
--- /dev/null
+++ b/test/png-roundtrip2.test
@@ -0,0 +1,22 @@
+#! /bin/bash
+# This script tests: pngtopam pamtopng
+# Also requires:
+
+# Failure message
+## If this test fails and pnm-roundtrip.test succeeds, it indicates
+## some problem with pamtopng.
+##
+## If both tests fail, the likely cause is a problem with pngtopam.
+## It is also possible that there is some problem in libpng.
+
+# Test 1.  Should print 1926073387 101484 twice
+for flags in "" -gamma=.45
+  do
+  pamtopng testimg.ppm $flags | pngtopam | cksum
+  done
+
+# Test 2.  Should print 2425386270 41 twice
+for flags in "" -gamma=.45
+  do
+  pamtopng testgrid.pbm $flags | pngtopam | cksum
+  done
diff --git a/test/pnm-pam-roundtrip.ok b/test/pnm-pam-roundtrip.ok
new file mode 100644
index 00000000..67f7a1fe
--- /dev/null
+++ b/test/pnm-pam-roundtrip.ok
@@ -0,0 +1,2 @@
+1926073387 101484
+2425386270 41
diff --git a/test/pnm-pam-roundtrip.test b/test/pnm-pam-roundtrip.test
new file mode 100755
index 00000000..b8e60c88
--- /dev/null
+++ b/test/pnm-pam-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pamtopam pamtopnm
+# Also requires:
+
+
+pamtopam < testimg.ppm | pamtopnm | cksum
+pamtopam < testgrid.pbm | pamtopnm | cksum
diff --git a/test/pnm-plain-roundtrip.ok b/test/pnm-plain-roundtrip.ok
new file mode 100644
index 00000000..67f7a1fe
--- /dev/null
+++ b/test/pnm-plain-roundtrip.ok
@@ -0,0 +1,2 @@
+1926073387 101484
+2425386270 41
diff --git a/test/pnm-plain-roundtrip.test b/test/pnm-plain-roundtrip.test
new file mode 100755
index 00000000..3779d761
--- /dev/null
+++ b/test/pnm-plain-roundtrip.test
@@ -0,0 +1,9 @@
+#! /bin/bash
+# This script tests: pamtopnm
+#
+
+
+pamtopnm -plain testimg.ppm | \
+  pamtopnm | cksum
+pamtopnm -plain testgrid.pbm | \
+  pamtopnm | cksum
diff --git a/test/pnmcat.ok b/test/pnmcat.ok
new file mode 100644
index 00000000..68052957
--- /dev/null
+++ b/test/pnmcat.ok
@@ -0,0 +1,4 @@
+1704087873 73
+4150323653 73
+1522490272 202953
+2862441566 202953
diff --git a/test/pnmcat.test b/test/pnmcat.test
new file mode 100755
index 00000000..c643320d
--- /dev/null
+++ b/test/pnmcat.test
@@ -0,0 +1,10 @@
+#! /bin/bash
+# This script tests: pnmcat
+# Also requires:
+
+
+pnmcat -lr testgrid.pbm testgrid.pbm | cksum
+pnmcat -tb testgrid.pbm testgrid.pbm | cksum
+
+pnmcat -lr testimg.ppm testimg.ppm | cksum
+pnmcat -tb testimg.ppm testimg.ppm | cksum
diff --git a/test/pnminvert-roundtrip.ok b/test/pnminvert-roundtrip.ok
new file mode 100644
index 00000000..67f7a1fe
--- /dev/null
+++ b/test/pnminvert-roundtrip.ok
@@ -0,0 +1,2 @@
+1926073387 101484
+2425386270 41
diff --git a/test/pnminvert-roundtrip.test b/test/pnminvert-roundtrip.test
new file mode 100755
index 00000000..52d15039
--- /dev/null
+++ b/test/pnminvert-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pnminvert
+# Also requires:
+
+
+pnminvert testimg.ppm | pnminvert | cksum
+pnminvert testgrid.pbm | pnminvert | cksum
diff --git a/test/pnminvert.ok b/test/pnminvert.ok
new file mode 100644
index 00000000..6cf5f011
--- /dev/null
+++ b/test/pnminvert.ok
@@ -0,0 +1,7 @@
+1240379484 41
+1416115901 101484
+1174803406 33838
+2595564405 14
+2595564405 14
+2595564405 14
+2896726098 15
diff --git a/test/pnminvert.test b/test/pnminvert.test
new file mode 100755
index 00000000..b80716ef
--- /dev/null
+++ b/test/pnminvert.test
@@ -0,0 +1,26 @@
+#! /bin/bash
+# This script tests: pnminvert
+# Also requires: pbmmake pamchannel pamtopnm
+
+
+# Test 1.  Should print 1240379484 41
+pnminvert testgrid.pbm | cksum
+
+# Test 2.  Should print 1416115901 101484
+pnminvert testimg.ppm | cksum
+
+# Test 3.  Should print 1174803406 33838
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  pnminvert | cksum
+
+# Test 4.  Should print 2595564405 14
+pbmmake -w 7 7 | pnminvert | cksum
+
+# Test 5.  Should print 2595564405 14
+pbmmake -b 7 7 | cksum
+
+# Test 6.  Should print 2595564405 14
+pbmmake -b 7 7 | pnminvert | pnminvert | cksum
+
+# Test 7.  Should print 2896726098 15
+pbmmake -g 8 8 | pnminvert | cksum
diff --git a/test/pnmpsnr.ok b/test/pnmpsnr.ok
new file mode 100644
index 00000000..b303cc9d
--- /dev/null
+++ b/test/pnmpsnr.ok
@@ -0,0 +1,2 @@
+0.00 dB
+no difference
diff --git a/test/pnmpsnr.test b/test/pnmpsnr.test
new file mode 100755
index 00000000..3e7408ec
--- /dev/null
+++ b/test/pnmpsnr.test
@@ -0,0 +1,20 @@
+#! /bin/bash
+# This script tests: pnmpsnr
+# Also requires: pbmmake
+
+
+tmpdir=${tmpdir:-/tmp}
+
+
+w_pbm=${tmpdir}/w.pbm
+b_pbm=${tmpdir}/b.pbm
+
+pbmmake -w 10 10 > ${w_pbm}
+pbmmake -b 10 10 > ${b_pbm}
+
+pnmpsnr  ${w_pbm}  ${b_pbm} 2>&1 | \
+ awk '$2=="lumina" {print $(NF-1),$NF}'
+pnmpsnr  ${w_pbm}  ${w_pbm} 2>&1 | \
+ awk '$2=="lumina" {print $(NF-1),$NF}'
+
+rm ${b_pbm} ${w_pbm}
diff --git a/test/pnmremap1.ok b/test/pnmremap1.ok
new file mode 100644
index 00000000..07954684
--- /dev/null
+++ b/test/pnmremap1.ok
@@ -0,0 +1 @@
+3602410851 101482
diff --git a/test/pnmremap1.test b/test/pnmremap1.test
new file mode 100755
index 00000000..d7c5189c
--- /dev/null
+++ b/test/pnmremap1.test
@@ -0,0 +1,26 @@
+#! /bin/bash
+# This script tests: pnmremap
+# Also requires: pamseq pamtopnm
+
+
+tmpdir=${tmpdir:-/tmp}
+palette=${tmpdir}/palette
+#palette255=${tmpdir}/palette255
+
+pamseq 3 5 -tupletype=RGB | pamtopnm > ${palette}
+#pamdepth 255 ${palette} > ${palette255}
+
+# Test 1. Floyd-Steinberg
+# This fails with older versions of Netpbm and x86-64.
+# May also fail on other non-Intel architectures.
+# v. 10.59.2
+# x86-32: 2667816854 101482
+# x86-64: 3602410851 101482
+
+pnmremap -mapfile=${palette} -floyd -norandom \
+ testimg.ppm | cksum
+
+#pnmremap -mapfile=${palette255} -floyd -norandom \
+# testimg.ppm | cksum
+
+rm ${palette} # ${palette255}
diff --git a/test/pnmremap2.ok b/test/pnmremap2.ok
new file mode 100644
index 00000000..a2d1c0dc
--- /dev/null
+++ b/test/pnmremap2.ok
@@ -0,0 +1,3 @@
+3224011488 101482
+2325696119 101484
+2325696119 101484
diff --git a/test/pnmremap2.test b/test/pnmremap2.test
new file mode 100755
index 00000000..76c2d566
--- /dev/null
+++ b/test/pnmremap2.test
@@ -0,0 +1,25 @@
+#! /bin/bash
+# This script tests: pnmremap
+# Also requires: pamdepth pamseq pamtopnm
+
+
+tmpdir=${tmpdir:-/tmp}
+palette=${tmpdir}/palette
+palette255=${tmpdir}/palette255
+
+pamseq 3 5 -tupletype=RGB | pamtopnm > ${palette}
+pamdepth 255 ${palette} > ${palette255}
+
+# Test 2. Default (unmodified quantization)
+pnmremap -mapfile=${palette} -nofloyd \
+testimg.ppm | cksum
+
+# Test 3. Use first color in palette for missing colors
+pnmremap -mapfile=${palette255} -nofloyd \
+ -firstisdefault testimg.ppm | cksum
+
+# Test 4. Use black for missing colors
+pnmremap -mapfile=${palette255} -nofloyd \
+-missingcolor=black testimg.ppm | cksum
+
+rm ${palette} ${palette255}
diff --git a/test/pnmshear.ok b/test/pnmshear.ok
new file mode 100644
index 00000000..058ec042
--- /dev/null
+++ b/test/pnmshear.ok
@@ -0,0 +1 @@
+598644601 24
diff --git a/test/pnmshear.test b/test/pnmshear.test
new file mode 100755
index 00000000..a19a9852
--- /dev/null
+++ b/test/pnmshear.test
@@ -0,0 +1,22 @@
+#! /bin/bash
+# This script tests: pnmshear
+# Also requires: pbmmake pnmpad
+
+
+# Test.  Should produce 598644601 24
+
+pbmmake -g 7 7 | pnmpad -white -top 1 | \
+   pnmshear 45 -noantialias | cksum
+
+# Output of above, in pbm plain format
+#
+# P1
+# 15 8
+# 000000000000000
+# 010101000000000
+# 010101010000000
+# 000101010000000
+# 000101010100000
+# 000001010100000
+# 000001010101000
+# 000000010101000
diff --git a/test/pnmtile.ok b/test/pnmtile.ok
new file mode 100644
index 00000000..559d0f65
--- /dev/null
+++ b/test/pnmtile.ok
@@ -0,0 +1,2 @@
+4228632379 259
+0
diff --git a/test/pnmtile.test b/test/pnmtile.test
new file mode 100755
index 00000000..409c0e50
--- /dev/null
+++ b/test/pnmtile.test
@@ -0,0 +1,22 @@
+#! /bin/bash
+# This script tests: pnmtile
+# Also requires: pnmcat
+
+
+# Test 1.  Should print 4228632379 259
+pnmtile 40 50 testgrid.pbm | cksum
+
+tmpdir=${tmpdir:-/tmp}
+
+# Test 2.  Compare 2x2 tile images produced by pnmtile and pnmcat
+# Should print 0
+testimg2_ppm=${tmpdir}/testimg2.ppm
+testimg4_ppm=${tmpdir}/testimg4.ppm
+
+pnmtile 454 298 testimg.ppm > ${testimg4_ppm} &&
+pnmcat -lr testimg.ppm testimg.ppm > ${testimg2_ppm} &&
+pnmcat -tb ${testimg2_ppm} ${testimg2_ppm} | \
+cmp -s - ${testimg4_ppm}
+echo $?
+
+rm ${testimg2_ppm} ${testimg4_ppm}
diff --git a/test/pnmtopnm-plain.ok b/test/pnmtopnm-plain.ok
new file mode 100644
index 00000000..02d99f1e
--- /dev/null
+++ b/test/pnmtopnm-plain.ok
@@ -0,0 +1,48 @@
+P1
+14 16
+10101010101010
+11111111111111
+10101010101010
+11111111111111
+10101010101010
+11111111111111
+10101010101010
+11111111111111
+10101010101010
+11111111111111
+10101010101010
+11111111111111
+10101010101010
+11111111111111
+10101010101010
+11111111111111
+P2
+14 16
+255
+0 255 0 255 0 255 0 255 0 255 0 255 0 255 
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 
+0 255 0 255 0 255 0 255 0 255 0 255 0 255 
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 
+0 255 0 255 0 255 0 255 0 255 0 255 0 255 
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 
+0 255 0 255 0 255 0 255 0 255 0 255 0 255 
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 
+0 255 0 255 0 255 0 255 0 255 0 255 0 255 
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 
+0 255 0 255 0 255 0 255 0 255 0 255 0 255 
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 
+0 255 0 255 0 255 0 255 0 255 0 255 0 255 
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 
+0 255 0 255 0 255 0 255 0 255 0 255 0 255 
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 
+P3
+14 16
+255
+0 0 0 255 255 255 0 0 0 255 255 255 0 0 0 255 255 255 0 0 0 255 255 255 
+0 0 0 255 255 255 0 0 0 255 255 255 0 0 0 255 255 255 
+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 255 255 255 0 0 0 255 255 255 0 0 0 255 255 255 0 0 0 255 255 255 
+0 0 0 255 255 255 0 0 0 255 255 255 0 0 0 255 255 255 
+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/test/pnmtopnm-plain.test b/test/pnmtopnm-plain.test
new file mode 100755
index 00000000..5b7e4b48
--- /dev/null
+++ b/test/pnmtopnm-plain.test
@@ -0,0 +1,10 @@
+#! /bin/bash
+# This script tests: pamtopnm
+# Also requires: pgmtopgm ppmtoppm
+
+pamtopnm -plain testgrid.pbm
+
+pgmtopgm < testgrid.pbm | pamtopnm -plain
+
+ppmtoppm < testgrid.pbm | pamtopnm -plain | \
+  head -n11
diff --git a/test/ppmbrighten.ok b/test/ppmbrighten.ok
new file mode 100644
index 00000000..65feb812
--- /dev/null
+++ b/test/ppmbrighten.ok
@@ -0,0 +1,3 @@
+2737989845 101484
+1059247992 101484
+32344911 101484
diff --git a/test/ppmbrighten.test b/test/ppmbrighten.test
new file mode 100755
index 00000000..fa7702d8
--- /dev/null
+++ b/test/ppmbrighten.test
@@ -0,0 +1,8 @@
+#! /bin/bash
+# This script tests: ppmbrighten
+# Also requires:
+
+
+ppmbrighten -v 100 testimg.ppm | cksum
+ppmbrighten -v 100 -normalize testimg.ppm | cksum
+ppmbrighten -s 100 -v -50 testimg.ppm | cksum
diff --git a/test/ppmchange-roundtrip.ok b/test/ppmchange-roundtrip.ok
new file mode 100644
index 00000000..a676a1f2
--- /dev/null
+++ b/test/ppmchange-roundtrip.ok
@@ -0,0 +1,2 @@
+2425386270 41
+2425386270 41
diff --git a/test/ppmchange-roundtrip.test b/test/ppmchange-roundtrip.test
new file mode 100755
index 00000000..78f5b548
--- /dev/null
+++ b/test/ppmchange-roundtrip.test
@@ -0,0 +1,12 @@
+#! /bin/bash
+# This script tests: ppmchange
+# Also requires: pgmtopbm pnminvert ppmtopgm
+
+
+ppmchange black white white black testgrid.pbm | \
+pnminvert | ppmtopgm | \
+pgmtopbm -th -val=0.5 | cksum
+
+ppmchange  black white white black testgrid.pbm | \
+ppmchange  black white white black | \
+ppmtopgm | pgmtopbm -th -val=0.5 | cksum
diff --git a/test/ppmchange.ok b/test/ppmchange.ok
new file mode 100644
index 00000000..130c3c45
--- /dev/null
+++ b/test/ppmchange.ok
@@ -0,0 +1,4 @@
+22488533 203
+1008787190 613
+3885709071 613
+2101746192 613
diff --git a/test/ppmchange.test b/test/ppmchange.test
new file mode 100755
index 00000000..d29a699f
--- /dev/null
+++ b/test/ppmchange.test
@@ -0,0 +1,59 @@
+#! /bin/bash
+# This script tests: ppmchange
+# Also requires: ppmrainbow pgmramp
+
+
+#  Failure message
+## If this test fails and ppmchange-roundtrip.test succeeds,
+## the probably cause is a problem with one of the options of
+## ppmchange: -closeness or -remainder.
+
+tmpdir=${tmpdir:-/tmp}
+rainbow_ppm=${tmpdir}/rainbow.ppm
+
+# Explicit values for intermediate colors: rgb.txt may be defining them
+# in unusual ways.
+
+brown=rgb:67/43/00
+cyan=rgb:00/ff/ff
+yellow=rgb:ff/ff/00
+gray=rgb:7f/7f/7f
+
+
+# Test 1. Should print 811868957 60
+pgmramp -lr 8 8 | ppmchange black black  white white  $gray $gray \
+  -close=10 -remainder=blue | cksum
+
+
+# Test 2. Should print 1008787190 613
+
+ppmrainbow -tmpdir=$tmpdir -width=200 -height=1 red green blue | \
+  tee ${rainbow_ppm} | \
+  ppmchange red $brown   green $brown   blue $brown | cksum
+
+
+# Test 3. Should print 3885709071 613
+
+ppmchange red $brown   green $cyan   blue $yellow \
+  -closeness=25 ${rainbow_ppm} | cksum
+
+
+# Test 4. Should print 2101746192 613
+
+ppmchange red rgb:64/00/01 rgb:00/ff/00 rgb:00/32/02 blue blue \
+  -remainder=black -closeness=25 ${rainbow_ppm} | cksum
+
+rm ${rainbow_ppm}
+
+
+# cksum ${rainbow_ppm}
+# 1983174784 613 rainbow.ppm
+
+# ppmchange red rgb:64/00/01 rgb:00/ff/00 rgb:00/32/02 blue blue \
+#   -remainder=black -closeness=25  ${rainbow_ppm} | \
+#   pphist -sort=rgb -noheader
+#
+#     0     0     0	    0	     75
+#     0     0   255	   29	     42
+#     0    50     2	   30	     42
+#   100     0     1	   30	     41
diff --git a/test/ppmcie.ok b/test/ppmcie.ok
new file mode 100644
index 00000000..79ebd086
--- /dev/null
+++ b/test/ppmcie.ok
@@ -0,0 +1,2 @@
+ok
+ok
diff --git a/test/ppmcie.test b/test/ppmcie.test
new file mode 100755
index 00000000..0ce69dea
--- /dev/null
+++ b/test/ppmcie.test
@@ -0,0 +1,51 @@
+#! /bin/bash
+# This script tests: ppmcie
+# Also requires: pamsumm pamsharpness
+
+
+# Failure message
+## Ppmcie is sensitive to system factors.  If this test fails, please
+## run the program and visually examine the output.
+
+tmpdir=${tmpdir:-/tmp}
+
+# Test 1. Should print 955840041 786447
+# Without -nolabel -noaxes -nowpoint -noblack older versions of
+# Netpbm produce slightly different charts.
+# Output from "ppmcie | cksum" :
+# v. 10.35.86: 288356530 786447
+# v. 10.59.2 : 2292601420 786447
+
+ppmcie_ppm=${tmpdir}/ppmcie.ppm
+
+ppmcie -nolabel -noaxes -nowpoint -noblack \
+ > ${ppmcie_ppm}
+
+# There is a slight difference in the output depending on whether ppmcie
+# is compiled with SSE features are turned on or off.
+# Note that Gcc turns on SSE,SSE2 on by default for x86-64.
+
+# Output from "cksum ppmcie.ppm":
+# v. 10.59.2
+# x86 32 bit: 955840041 786447
+# x86 64 bit: 4208660683 786447
+
+# Test 1.  Measure mean value
+# v. 10.59.2
+# x86 32 bit: 38.660173
+# x86 64 bit: 38.681432
+
+pamsumm --mean --brief ${ppmcie_ppm} | \
+  awk '{ if(38.65 < $1 && $1 <38.69) print "ok"; else print $1}'
+
+# Test 2.  Measure image sharpness
+# v. 10.59.2
+# x86 32 bit: 0.002476
+# x86 64 bit: 0.002478
+
+pamsharpness ${ppmcie_ppm} 2>&1 | \
+  awk 'NF==3 && $1=="Sharpness" \
+       {if (0.002475 < $3 && $3 < 0.002479) print "ok"; else print $3}
+       NF>0 && NF!=3 {print "error"}'
+
+rm ${ppmcie_ppm}
diff --git a/test/ppmdfont.ok b/test/ppmdfont.ok
new file mode 100644
index 00000000..c996a8f5
--- /dev/null
+++ b/test/ppmdfont.ok
@@ -0,0 +1,2 @@
+2726488777 48129
+2845495212 75033
diff --git a/test/ppmdfont.test b/test/ppmdfont.test
new file mode 100755
index 00000000..7d894789
--- /dev/null
+++ b/test/ppmdfont.test
@@ -0,0 +1,22 @@
+#! /bin/bash
+# This script tests: ppmdmkfont ppmddumpfont ppmdcfont
+# Also requires:
+
+
+# Test 1. Should produce: 2726488777 48129
+ppmdmkfont | ppmddumpfont 2>&1 | cksum
+
+# Test 2. Should produce: 2845495212 75033
+ppmdmkfont | ppmdcfont | cksum
+
+# There is a strange glitch in output when ppmdcfont is compiled by
+# clang version 3.2.  Optimization (-O3) seems to be a factor.
+# 
+# 3171,3173c3171,3173
+# <     0x01,
+# <     95,
+# <     32
+# ---
+# >     0x00,
+# >     0,
+# >     0
diff --git a/test/ppmdim.ok b/test/ppmdim.ok
new file mode 100644
index 00000000..9c55e064
--- /dev/null
+++ b/test/ppmdim.ok
@@ -0,0 +1,13 @@
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
diff --git a/test/ppmdim.test b/test/ppmdim.test
new file mode 100755
index 00000000..848e2e9f
--- /dev/null
+++ b/test/ppmdim.test
@@ -0,0 +1,25 @@
+#! /bin/bash
+# This script tests: ppmdim
+# Also requires: pamfunc pamarith pamsumm
+
+
+tmpdir=${tmpdir:-/tmp}
+
+# Compare ppmdim and pamfunc with various dim factors
+# Due to the difference in rounding methods, pamfunc produces slightly
+# brighter images, by about 0.5 per pixel.
+# If the mean difference is between 0 and 0.75 we consider the output
+# normal.  This works for dim values up to 0.994 .
+dim1_ppm=${tmpdir}/dim1.ppm
+dim2_ppm=${tmpdir}/dim2.ppm
+
+for i in  0.125 0.25 0.5 0.75 0.1 0.0117 0.2 0.4 0.333 0.666 0.8 0.9 0.95
+  do
+  ppmdim $i testimg.ppm > ${dim1_ppm}
+  pamfunc -mult=$i testimg.ppm > ${dim2_ppm}
+  pamarith -diff ${dim1_ppm} ${dim2_ppm} | \
+    pamsumm -mean -brief | \
+    awk '{print ($1<0.75) ? "ok" : "fail"}'
+  done
+
+rm ${dim1_ppm} ${dim2_ppm}
diff --git a/test/ppmdither.ok b/test/ppmdither.ok
new file mode 100644
index 00000000..3159f17e
--- /dev/null
+++ b/test/ppmdither.ok
@@ -0,0 +1,3 @@
+1519741676 101482
+3886084802 101482
+3737986353 101482
diff --git a/test/ppmdither.test b/test/ppmdither.test
new file mode 100755
index 00000000..7e32ef59
--- /dev/null
+++ b/test/ppmdither.test
@@ -0,0 +1,8 @@
+#! /bin/bash
+# This script tests: ppmdither
+# Also requires:
+
+
+ppmdither testimg.ppm | cksum
+ppmdither -red 2 -green 2 -blue 2 testimg.ppm | cksum
+ppmdither -dim 2 testimg.ppm | cksum
diff --git a/test/ppmforge.ok b/test/ppmforge.ok
new file mode 100644
index 00000000..e4a4c9e2
--- /dev/null
+++ b/test/ppmforge.ok
@@ -0,0 +1 @@
+3634219838 196623
diff --git a/test/ppmforge.test b/test/ppmforge.test
new file mode 100755
index 00000000..65280d14
--- /dev/null
+++ b/test/ppmforge.test
@@ -0,0 +1,20 @@
+#! /bin/bash
+# This script tests: ppmforge
+# Also requires:
+
+# Use small x y values to avoid floating point issues.
+
+
+testrandom -q
+case $? in
+   81)
+      # Test 1: Should print: 3634219838 196623
+      ppmforge -night -seed 1 | cksum
+      ;;
+
+   8[02-9] | 90)
+       echo "Skipping: random number generator is not glibc." 1>&2
+       exit 80;;
+
+   *)  exit 1;;  # testrandom failed
+esac
diff --git a/test/ppmgauss.ok b/test/ppmgauss.ok
new file mode 100644
index 00000000..cf708d64
--- /dev/null
+++ b/test/ppmgauss.ok
@@ -0,0 +1,81 @@
+3712518499 55
+3712518499 55
+1147844094 55
+731871722 55
+1259293616 55
+4234223225 55
+171732531 55
+448293386 55
+3030522957 55
+757204806 62
+757204806 62
+2890088558 62
+4276668903 62
+1462902064 62
+4161772187 62
+2741154810 62
+417660035 62
+1026188683 62
+1236982144 71
+1236982144 71
+2668915323 71
+499504068 71
+3377926681 71
+1877781725 71
+1741784255 71
+2975827721 71
+185899480 71
+1890880170 82
+1890880170 82
+2007282168 82
+2290578407 82
+3295586348 82
+3202728672 82
+3794283497 82
+1136760947 82
+2659761303 82
+3268058875 95
+3268058875 95
+4112134785 95
+3700010306 95
+909823480 95
+2336604848 95
+3042873446 95
+299128630 95
+1852505233 95
+3614879265 110
+3614879265 110
+3727088180 110
+3861671574 110
+952180714 110
+1393452065 110
+1551903756 110
+2198775655 110
+3231547603 110
+3184597171 127
+3184597171 127
+406519147 127
+1836966508 127
+1796467962 127
+1932274161 127
+1852779549 127
+1734420920 127
+2400421918 127
+3252995941 148
+3252995941 148
+2022176119 148
+473753498 148
+3178129210 148
+2278608777 148
+3169785704 148
+85136480 148
+2396646133 148
+1871459418 169
+1871459418 169
+639002491 169
+1810927285 169
+4218372313 169
+1320749835 169
+4102007360 169
+3022719594 169
+1769176609 169
diff --git a/test/ppmgauss.test b/test/ppmgauss.test
new file mode 100755
index 00000000..213810c7
--- /dev/null
+++ b/test/ppmgauss.test
@@ -0,0 +1,12 @@
+#! /bin/bash
+# This script tests: pamgauss
+# Also requires:
+
+
+for i in `seq 3 11`
+do
+for s in `seq 1 9`
+do
+pamgauss $i $i -sigma=.$s | cksum
+done
+done
diff --git a/test/ppmhist.ok b/test/ppmhist.ok
new file mode 100644
index 00000000..d7ecf07e
--- /dev/null
+++ b/test/ppmhist.ok
@@ -0,0 +1,11 @@
+   r     g     b   	 lum 	 count  
+ ----- ----- ----- 	-----	------- 
+     0     0     0	    0	      2 
+     1     1     1	    1	      2 
+     2     2     2	    2	      2 
+     3     3     3	    3	      2 
+     4     4     4	    4	      2 
+     5     5     5	    5	      2 
+     6     6     6	    6	      2 
+     8     8     8	    8	      2 
+3438989921 711087
diff --git a/test/ppmhist.test b/test/ppmhist.test
new file mode 100755
index 00000000..97fbd79d
--- /dev/null
+++ b/test/ppmhist.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: ppmhist
+# Also requires: pgmramp
+
+
+pgmramp -maxval=8 -lr 8 2 | ppmhist -sort=rgb
+ppmhist -map -sort=rgb testimg.ppm | cksum
diff --git a/test/ppmmake.ok b/test/ppmmake.ok
new file mode 100644
index 00000000..0e871732
--- /dev/null
+++ b/test/ppmmake.ok
@@ -0,0 +1,2 @@
+2477651508 15012
+2378991101 7513
diff --git a/test/ppmmake.test b/test/ppmmake.test
new file mode 100755
index 00000000..879a367e
--- /dev/null
+++ b/test/ppmmake.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: ppmmake
+# Also requires:
+
+
+ppmmake rgb:ff/80/80 50 100 -maxval=5 | cksum
+ppmmake red 50 50  | cksum
diff --git a/test/ppmmix.ok b/test/ppmmix.ok
new file mode 100644
index 00000000..88bcef4a
--- /dev/null
+++ b/test/ppmmix.ok
@@ -0,0 +1,17 @@
+P2
+8 8
+3
+3 2 3 2 1 0 1 0 
+2 3 2 3 0 1 0 1 
+3 2 3 2 1 0 1 0 
+2 3 2 3 0 1 0 1 
+1 0 1 0 3 2 3 2 
+0 1 0 1 2 3 2 3 
+1 0 1 0 3 2 3 2 
+0 1 0 1 2 3 2 3 
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+127
+127
diff --git a/test/ppmmix.test b/test/ppmmix.test
new file mode 100755
index 00000000..e1c6486b
--- /dev/null
+++ b/test/ppmmix.test
@@ -0,0 +1,33 @@
+#! /bin/bash
+# This script tests: ppmmix
+# Also requires: pamdepth pamenlarge pamsumm pbmmake
+# Also requires: pgmtopgm pnminvert ppmtopgm
+
+
+tmpdir=${tmpdir:-/tmp}
+
+# Test 1. Print a pretty checkerboard pattern
+a1_pgm=${tmpdir}/a1.pgm
+a2_pgm=${tmpdir}/a2.pgm
+
+pbmmake -g 8 8 | pgmtopgm > ${a1_pgm} &&
+pbmmake -g 2 2 | pamenlarge 4 | pgmtopgm > ${a2_pgm} &&
+ppmmix 0.75 ${a1_pgm} ${a2_pgm} | ppmtopgm | pamdepth 3 -plain &&
+rm ${a1_pgm} ${a2_pgm}
+
+# Mix image with itself.
+# Output should match input regardless of ratio.
+for i in 0 0.5 0.6 1
+do
+ppmmix $i testimg.ppm testimg.ppm | cksum
+done
+
+# Mix image with its own inverse.
+# Output should be a monotone gray sheet.
+a3_ppm=${tmpdir}/a3.ppm
+
+pnminvert testimg.ppm | ppmmix .5 \
+    testimg.ppm - | tee ${a3_ppm} | \
+  pamsumm -brief -max &&
+  pamsumm -brief -min ${a3_ppm} &&
+rm ${a3_ppm}
diff --git a/test/ppmpat.ok b/test/ppmpat.ok
new file mode 100644
index 00000000..c5b71909
--- /dev/null
+++ b/test/ppmpat.ok
@@ -0,0 +1,6 @@
+4008533639 781
+2448908863 9613
+2698433077 1549
+3705929501 781
+2219119109 36015
+3436846137 16813
diff --git a/test/ppmpat.test b/test/ppmpat.test
new file mode 100755
index 00000000..89f86f67
--- /dev/null
+++ b/test/ppmpat.test
@@ -0,0 +1,35 @@
+#! /bin/bash
+# This script tests: ppmpat
+# Also requires:
+
+# TODO: Write tests for squig and poles.  It appears that they are
+# sensitive to differences in floating point math.
+
+testrandom -q
+case $? in
+   81)
+       # Test 1. Should print: 4008533639 781
+       ppmpat --randomseed=0 -g2 16 16 | cksum
+
+       # Test 2. Should print: 2448908863 9613
+       ppmpat --randomseed=0 -g2 64 50 | cksum
+
+       # Test 3. Should print: 2698433077 1549
+       ppmpat --randomseed=0 -madras 32 16 | cksum
+
+       # Test 4. Should print: 3705929501 781
+       ppmpat --randomseed=0 -tartan 16 16 | cksum
+
+       # Test 5. Should print: 2219119109 36015
+       ppmpat --randomseed=0 -camo 100 120 | cksum
+
+       # Test 6. Should print: 3436846137 16813
+       ppmpat --randomseed=0 -anticamo 80 70 | cksum
+       ;;
+
+   8[02-9] | 90)
+       echo "Skipping: random number generator is not glibc." 1>&2
+       exit 80;;
+
+   *)  exit 1;;  # testrandom failed
+esac
diff --git a/test/ppmrelief.ok b/test/ppmrelief.ok
new file mode 100644
index 00000000..3c236775
--- /dev/null
+++ b/test/ppmrelief.ok
@@ -0,0 +1,3 @@
+3637356697 688
+3979143778 780
+2954951371 780
diff --git a/test/ppmrelief.test b/test/ppmrelief.test
new file mode 100755
index 00000000..3467dcde
--- /dev/null
+++ b/test/ppmrelief.test
@@ -0,0 +1,36 @@
+#! /bin/bash
+# This script tests: ppmrelief
+# Also requires: pbmmake pgmramp pamflip
+
+
+# Test 1.  Should print 3637356697 688
+pbmmake -w 15 15 | ppmrelief | cksum
+
+# Test 2.  Should print 3979143778 780
+pgmramp -diagonal -maxval=30 16 16 | ppmrelief | cksum
+
+# Test 3.  Should print 2954951371 780
+pgmramp -diagonal -maxval=30 16 16 | pamflip -lr | ppmrelief | cksum
+
+
+# pgmramp -diagonal -maxval=30 16 16 | ppmrelief | ppmtopgm -plain
+
+# P2
+# 16 16
+# 30
+# 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 14 14 14 14 14 14 14 14 14 14 14 14 14 14 0
+# 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/test/ppmrough.ok b/test/ppmrough.ok
new file mode 100644
index 00000000..83643849
--- /dev/null
+++ b/test/ppmrough.ok
@@ -0,0 +1 @@
+378403602 30015
diff --git a/test/ppmrough.test b/test/ppmrough.test
new file mode 100755
index 00000000..cf948f9f
--- /dev/null
+++ b/test/ppmrough.test
@@ -0,0 +1,17 @@
+#! /bin/bash
+# This script tests: ppmrough
+# Also requires:
+
+testrandom -q
+case $? in
+   81)
+      # Should print: 378403602 30015
+      ppmrough  -randomseed 1 | cksum
+      ;;
+
+   8[02-9] | 90)
+       echo "Skipping: random number generator is not glibc." 1>&2
+       exit 80;;
+
+   *)  exit 1;;  # testrandom failed
+esac
diff --git a/test/ppmtoarbtxt-roundtrip.ok b/test/ppmtoarbtxt-roundtrip.ok
new file mode 100644
index 00000000..a29d1c2b
--- /dev/null
+++ b/test/ppmtoarbtxt-roundtrip.ok
@@ -0,0 +1,2 @@
+1926073387 101484
+1926073387 101484
diff --git a/test/ppmtoarbtxt-roundtrip.test b/test/ppmtoarbtxt-roundtrip.test
new file mode 100755
index 00000000..dff78250
--- /dev/null
+++ b/test/ppmtoarbtxt-roundtrip.test
@@ -0,0 +1,34 @@
+#! /bin/bash
+# This script tests: ppmtoarbtxt
+# Also requires: pnminvert pamtopnm
+
+# Test.  Invert colors.
+# Should print 1926073387 101484
+
+tmpdir=${tmpdir:-/tmp}
+headskl=${tmpdir}/headskl
+bodyskl=${tmpdir}/bodyskl
+inv_ppm=${tmpdir}/inv.ppm
+
+cat > ${headskl} << EOF
+P3
+#(width) #(height)
+255
+
+EOF
+
+cat > ${bodyskl} << EOF
+#(ired %d 255 0) #(igreen %d 255 0) #(iblue %d 255 0)
+
+EOF
+
+ppmtoarbtxt ${bodyskl} -hd ${headskl} testimg.ppm | tee ${inv_ppm} |
+  pnminvert | cksum
+
+
+# Test 2.
+
+cat ${inv_ppm} | ppmtoarbtxt ${bodyskl} -hd ${headskl} | pamtopnm | cksum
+
+
+rm ${bodyskl} ${headskl}
diff --git a/test/ppmtopgm.ok b/test/ppmtopgm.ok
new file mode 100644
index 00000000..32a4743b
--- /dev/null
+++ b/test/ppmtopgm.ok
@@ -0,0 +1 @@
+2871603838 33838
diff --git a/test/ppmtopgm.test b/test/ppmtopgm.test
new file mode 100755
index 00000000..8503194b
--- /dev/null
+++ b/test/ppmtopgm.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: ppmtopgm
+# Also requires:
+
+
+# Test 1. Should produce 2871603838 33838
+ppmtopgm testimg.ppm | cksum
diff --git a/test/ppmtoppm.ok b/test/ppmtoppm.ok
new file mode 100644
index 00000000..764a3464
--- /dev/null
+++ b/test/ppmtoppm.ok
@@ -0,0 +1 @@
+829921912 685
diff --git a/test/ppmtoppm.test b/test/ppmtoppm.test
new file mode 100755
index 00000000..797c733d
--- /dev/null
+++ b/test/ppmtoppm.test
@@ -0,0 +1,6 @@
+#! /bin/bash
+# This script tests: ppmtoppm
+# Also requires:
+
+
+ppmtoppm < testgrid.pbm | cksum
diff --git a/test/ppmwheel.ok b/test/ppmwheel.ok
new file mode 100644
index 00000000..24756d1e
--- /dev/null
+++ b/test/ppmwheel.ok
@@ -0,0 +1,2 @@
+1537578995 59
+875938089 86
diff --git a/test/ppmwheel.test b/test/ppmwheel.test
new file mode 100755
index 00000000..f528e209
--- /dev/null
+++ b/test/ppmwheel.test
@@ -0,0 +1,110 @@
+#! /bin/bash
+# This script tests: ppmwheel
+# Also requires:
+
+
+# For values 6 and above, x86(-32) and x86-64 produce different output.
+# SSE floating-point math is the probable cause.
+
+for i in 4 5
+do
+ppmwheel $i | cksum
+done
+
+#   i    32 bit              64 bit
+#
+#   6:   343122583   119     142348877   119
+#   7:  3572780137   158    3220488357   158
+#   8:  3078449954   203    1413542034   203
+#   9:  3437862278   254     311501249   254
+#  10:  1111245327   313     582960969   313
+#  11:   548273236   376    3726921442   376
+#  12:  2741456118   445    3696607391   445
+#  13:   673636665   520    1059273944   520
+#  14:  2445730101   601    3445053713   601
+#  15:   592850912   688    1691004230   688
+#  16:  3302887123   781      86998539   781
+#  17:  1786065899   880    2476986124   880
+#  18:   186804914   985    2853442889   985
+#  19:  1453260662  1096    3182405636  1096
+#  20:  1467480434  1213    4040494227  1213
+#  21:  1062903055  1336     810283715  1336
+#  22:  2638704989  1465     338245948  1465
+#  23:  3441453532  1600    2213581565  1600
+#  24:  1836362684  1741    2056241218  1741
+#  25:  3423398348  1888    3519123827  1888
+#  26:  3262548375  2041     684292199  2041
+#  27:  3598993221  2200    1577275941  2200
+#  28:  3728319498  2365    1061803162  2365
+#  29:  3729945629  2536     133069086  2536
+#  30:  3645113424  2713    3302414477  2713
+#  31:  2873651818  2896    3660671646  2896
+#  32:  1408491813  3085    2010949125  3085
+#  33:  3781351758  3280    1548995986  3280
+#  34:  3242345057  3481    3082631326  3481
+#  35:  2550511376  3688    1038419921  3688
+#  36:  1028065649  3901    2532290776  3901
+#  37:  1140380377  4120    2428435544  4120
+#  38:  1066850059  4345    3102961014  4345
+#  39:  3620700071  4576    1898018896  4576
+#  40:   504158758  4813     272658385  4813
+#  41:  1349365217  5056     270227718  5056
+#  42:    41774986  5305     509262620  5305
+#  43:  2655322328  5560    4133072843  5560
+#  44:  2318607206  5821    3294034402  5821
+#  45:  2178510518  6088    3314361750  6088
+#  46:  3285054002  6361    2469557621  6361
+#  47:  3047461329  6640    3629344726  6640
+#  48:   137286416  6925    4222495543  6925
+#  49:  2115001463  7216    2668575184  7216
+#  50:  1677997300  7513    2978221605  7513
+#  51:  1365174489  7816    3575061958  7816
+#  52:  2863311036  8125    1791798959  8125
+#  53:  3791281707  8440    3482008958  8440
+#  54:  2687508733  8761    4163388707  8761
+#  55:  2593010606  9088    3561733531  9088
+#  56:  2976537803  9421    3234561645  9421
+#  57:  2850729115  9760    1244925692  9760
+#  58:   224433311 10105    3825098819 10105
+#  59:   547683536 10456    3988409437 10456
+#  60:   369722519 10813    2020750234 10813
+#  61:   745454697 11176    1184600938 11176
+#  62:  1380472574 11545    1044589924 11545
+#  63:  1812671001 11920     238104621 11920
+#  64:  2833858343 12301     228306790 12301
+#  65:   957265138 12688     470109697 12688
+#  66:   895413590 13081     944950937 13081
+#  67:  3724154793 13480     126668527 13480
+#  68:   741064239 13885    1930273504 13885
+#  69:   785023636 14296    3279987135 14296
+#  70:  3649222232 14713    2854788625 14713
+#  71:  1997467987 15136    1689264112 15136
+#  72:  3469507186 15565     682126673 15565
+#  73:  2831204610 16000    1826370607 16000
+#  74:  3154512856 16441    1843713601 16441
+#  75:  1469123916 16888    3700274597 16888
+#  76:  2909139778 17341    2769322155 17341
+#  77:  2945643808 17800    1176063119 17800
+#  78:  3334870474 18265    2274153840 18265
+#  79:  1788316629 18736    1170513638 18736
+#  80:  1236198778 19213     780491458 19213
+#  81:  1946474293 19696    2989417839 19696
+#  82:  1479086076 20185    1500511762 20185
+#  83:  3510147444 20680    1990564736 20680
+#  84:  4044688071 21181    2820276538 21181
+#  85:  1293455808 21688    1335142677 21688
+#  86:  3408774244 22201    2664774438 22201
+#  87:  3591290969 22720    1236374968 22720
+#  88:  3814759633 23245    2889543826 23245
+#  89:  4157749717 23776    4033323913 23776
+#  90:  3512276083 24313    1722273029 24313
+#  91:   797424413 24856    1737843448 24856
+#  92:  3480584612 25405    1304329442 25405
+#  93:  2445523895 25960    1260847516 25960
+#  94:  3157009888 26521    1120864660 26521
+#  95:   103872014 27088    2076594208 27088
+#  96:  1584398684 27661     213099822 27661
+#  97:  3586313190 28240     944151015 28240
+#  98:  4086203619 28825    1961862620 28825
+#  99:  3656777902 29416    2475351252 29416
+# 100:  2246720411 30015    4055518595 30015
diff --git a/test/ps-alt-roundtrip.ok b/test/ps-alt-roundtrip.ok
new file mode 100644
index 00000000..1cd73f75
--- /dev/null
+++ b/test/ps-alt-roundtrip.ok
@@ -0,0 +1,3 @@
+2425386270 41
+2425386270 41
+2916080186 235
diff --git a/test/ps-alt-roundtrip.test b/test/ps-alt-roundtrip.test
new file mode 100755
index 00000000..d90c4ddb
--- /dev/null
+++ b/test/ps-alt-roundtrip.test
@@ -0,0 +1,60 @@
+#! /bin/bash
+# This script tests: pbmtoepsi pbmtopsg3 pbmtolps psidtopgm pstopnm
+# Also requires: gs pnmcrop
+
+
+# This script is for testing alternative (or minor) utilities that
+# read/write Postscript and encapsulated Postscript:
+# pbmtoepsi, pbmtopsg3, pbmtolps and psidtopgm.
+#
+# We keep these tests separate from those for pnmtops and pstopnm
+# which are far more popular.
+#
+# pbmtopsg3 and pbmtolps produce output that require pstopnm for decoding.
+#
+# Failure message
+## If ps-roundtrip.test succeeds and this test fails, it is most likely
+## a problem with one of the alternate Postscipt utilities:
+## pbmtoepsi, pbmtopsg3, pbmtolps or psidtopgm.
+## If both tests fail it indicates a problem with pstopnm or gs.
+
+# pstopnm does not use libnetpbm functions for output.
+# Output is filtered through at least one Netpbm program.
+
+# Test 1. Should print: 2425386270 41
+testgrid1_ps=${tmpdir}/testgrid1.ps
+
+pbmtopsg3 -dpi=72 testgrid.pbm \
+     > ${testgrid1_ps} && \
+pstopnm -xborder=0 -yborder=0 -llx=0 -lly=-16 -urx=14 \
+    -dpi=72 -stdout -quiet -pbm ${testgrid1_ps} | \
+    pnmcrop | cksum
+
+rm ${testgrid1_ps}
+
+
+# Test 2. Should print: 2425386270 41
+testgrid2_ps=${tmpdir}/testgrid2.ps
+
+pbmtolps -dpi 72 testgrid.pbm \
+     > ${testgrid2_ps} && \
+pstopnm -xborder=0 -yborder=0 -dpi=72 -stdout \
+    -quiet ${testgrid2_ps} -pbm | \
+  pnmcrop | cksum
+
+rm ${testgrid2_ps}
+
+# Test 3. Should print: 2916080186 235
+# Output is pgm maxval=1 with black and white inverted.
+#
+testgrid_epsi=${tmpdir}/testgrid.epsi
+
+pbmtoepsi testgrid.pbm > ${testgrid_epsi} && \
+xysizebps=`awk  '/BeginPreview/ {print $2,$3,$4}' \
+    ${testgrid_epsi}` && \
+awk '/^%%BeginPreview:/ { p=1; next } /^%%EndImage/ { p=0; next } \
+  p==1 && /%[ \t0-9a-fA-F]+/ { print substr($0,2); next } \
+  p==1 {print "!"$0}' \
+    ${testgrid_epsi} | psidtopgm $xysizebps | cksum
+
+rm ${testgrid_epsi}
diff --git a/test/ps-roundtrip.ok b/test/ps-roundtrip.ok
new file mode 100644
index 00000000..0ebfb94a
--- /dev/null
+++ b/test/ps-roundtrip.ok
@@ -0,0 +1,15 @@
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+2918318199 62
+2918318199 62
+2918318199 62
+2918318199 62
+2918318199 62
+2918318199 62
+2918318199 62
+1386192571 507420
+1386192571 507420
+1386192571 507420
diff --git a/test/ps-roundtrip.test b/test/ps-roundtrip.test
new file mode 100755
index 00000000..873bbdef
--- /dev/null
+++ b/test/ps-roundtrip.test
@@ -0,0 +1,86 @@
+#! /bin/bash
+# This script tests: pnmtops pstopnm
+# Also requires: pamtopnm gs pbmmake pnmshear pnmpad pnmcat
+
+
+# Failure message
+## This test fails when:
+## (1) zlib was not linked.
+## (2) ghostscript is not available.
+
+tmpdir=${tmpdir:-/tmp}
+
+# pstopnm does not use libnetpbm functions for output.
+# Output is filtered through pamtopnm.
+
+# Test 1.  Should print: 1926073387 101484 five times
+# *NOTE* Fifth iteration fails if pnmtops was compiled without zlib
+# (flate compression) support.
+test1_ps=${tmpdir}/testimg1.ps
+
+for flag in "" "-ps" "-rle" "-ps -ascii" "-ps -flate"
+  do
+  pnmtops -nocenter -equalpixels -dpi 72 -noturn ${flag} testimg.ppm \
+    > ${test1_ps} && \
+  xysize1=`awk  '/BoundingBox/ {print "-xsize="$4,"-ysize="$5}' \
+    ${test1_ps}` && \
+  pstopnm -portrait -xborder=0 -yborder=0 $xysize1 -stdout -quiet \
+    ${test1_ps} | pamtopnm | cksum
+  done
+
+rm ${test1_ps}
+# Test 2.  Should print: 2918318199 62 seven times
+# Test image designed to detect problems with run-length compression
+#
+
+g_pbm=${tmpdir}/g.pbm
+t_pbm=${tmpdir}/t.pbm
+grid_ps=${tmpdir}/testgrid.ps
+
+pbmmake -g 2 2 > ${g_pbm}
+pbmmake -g 8 4 | \
+  pnmshear 45 -noantialias -background=black | \
+  pnmpad -right 60 | \
+  pnmcat -tb -jright - ${g_pbm} > ${t_pbm} &&
+for flag in "" "-rle" "-ps -rle -ascii" \
+            "-bitspersample=2 -rle" "-ps -bitspersample=4 -rle" \
+            "-bitspersample=8 -rle" "-ps -bitspersample=12 -rle -dict"
+  do
+  pnmtops -nocenter -equalpixels -dpi 72 -noturn  ${flag} ${t_pbm} \
+    > ${grid_ps} && \
+  xysize2=`awk  '/BoundingBox/ {print "-xsize="$4,"-ysize="$5}' \
+      ${grid_ps}` && \
+  pstopnm -portrait -xborder=0 -yborder=0 $xysize2 -stdout \
+    -quiet ${grid_ps} -pbm | pamtopnm | cksum
+  done
+
+rm ${grid_ps} ${g_pbm} ${t_pbm}
+
+#Test 3. Should print: 1386192571 507420 three times
+# *NOTE* Second iteration fails if pnmtops was compiled without zlib
+# (flate compression) support.
+#
+# Special care is needed when conducting round-trips with multiple-image
+# files as input.
+# (1) pnmtops: -setpage is mandatory
+# (2) awk: xy values are taken from the first "BoundingBox" encountered.
+#          Subsequent BoundingBox values are ignored.
+# (3) pstopnm: input must be an ordinary file.  Input from stdin
+#     (by pipe or input redirection: "< file" ) does not work.
+#
+
+test3_ps=${tmpdir}/testimg3.ps
+
+for flag in "" "-ps" \
+            "-ps -bitspersample=12 -flate -rle -vmreclaim"
+  do
+cat testimg.ppm testimg.ppm testimg.ppm testgrid.pbm testgrid.pbm | \
+pnmtops -nocenter -equalpixels -dpi 72 -noturn -setpage ${flag} \
+  > ${test3_ps} &&
+xysize3=`awk  '/BoundingBox/ {print "-xsize="$4,"-ysize="$5 ; exit}' \
+  ${test3_ps}` &&
+pstopnm -portrait -xborder=0 -yborder=0 $xysize3 -stdout  ${test3_ps} | \
+  pamtopnm | cksum
+  done
+
+rm ${test3_ps}
diff --git a/test/rgb3-roundtrip.ok b/test/rgb3-roundtrip.ok
new file mode 100644
index 00000000..64da849d
--- /dev/null
+++ b/test/rgb3-roundtrip.ok
@@ -0,0 +1,6 @@
+1926073387 101484
+3744829044 101514
+2425386270 41
+0
+0
+0
diff --git a/test/rgb3-roundtrip.test b/test/rgb3-roundtrip.test
new file mode 100755
index 00000000..cac52220
--- /dev/null
+++ b/test/rgb3-roundtrip.test
@@ -0,0 +1,46 @@
+#! /bin/bash
+# This script tests: ppmtorgb3 rgb3toppm
+# Also requires: pgmtopbm pgmtopgm ppmtopgm
+
+
+# Break an image into three monochrome planes, reassemble the
+# image from them and check whether the resulting output is
+# identical to the original input.
+
+tmpdir=${tmpdir:-/tmp}
+
+# Test 1.  PPM (color) input
+testimg_ppm=${tmpdir}/testimg.ppm
+testimg_red=${tmpdir}/testimg.red
+testimg_grn=${tmpdir}/testimg.grn
+testimg_blu=${tmpdir}/testimg.blu
+
+cp testimg.ppm ${tmpdir} &&
+ppmtorgb3 ${testimg_ppm} &&
+rgb3toppm ${testimg_red} ${testimg_grn} ${testimg_blu} | cksum
+
+cat ${testimg_red} ${testimg_grn} ${testimg_blu} | cksum
+
+rm ${testimg_ppm} ${testimg_red} ${testimg_grn} ${testimg_blu}
+
+# Test 2.  PBM (monochrome) input
+testgrid_pbm=${tmpdir}/testgrid.pbm
+testgrid_red=${tmpdir}/testgrid.red
+testgrid_grn=${tmpdir}/testgrid.grn
+testgrid_blu=${tmpdir}/testgrid.blu
+
+cp testgrid.pbm ${tmpdir} &&
+ppmtorgb3 ${testgrid_pbm} &&
+rgb3toppm ${testgrid_red} ${testgrid_grn} ${testgrid_blu} | \
+  ppmtopgm | pgmtopbm -th -val=0.5 | cksum
+
+# Test 3.
+# With PGM or PBM input, the three monochrome planes should be
+# identical.  Test for this.
+
+cmp -s ${testgrid_red} ${testgrid_grn} ; echo $?
+cmp -s ${testgrid_grn} ${testgrid_blu} ; echo $?
+pgmtopgm < testgrid.pbm | cmp -s - ${testgrid_red}
+  echo $?
+
+rm ${testgrid_pbm} ${testgrid_red} ${testgrid_grn} ${testgrid_blu}
diff --git a/test/sbig-roundtrip.ok b/test/sbig-roundtrip.ok
new file mode 100644
index 00000000..430f3f5f
--- /dev/null
+++ b/test/sbig-roundtrip.ok
@@ -0,0 +1 @@
+1571496937 33838
diff --git a/test/sbig-roundtrip.test b/test/sbig-roundtrip.test
new file mode 100755
index 00000000..1fdaccfd
--- /dev/null
+++ b/test/sbig-roundtrip.test
@@ -0,0 +1,9 @@
+#! /bin/bash
+# This script tests: pgmtosbig sbigtopgm
+# Also requires: pamchannel pamtopnm
+
+
+# Should produce 1571496937 33838, cksum of testimg.red
+
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  pgmtosbig | sbigtopgm | cksum
diff --git a/test/sgi-roundtrip.ok b/test/sgi-roundtrip.ok
new file mode 100644
index 00000000..b1bd5ba6
--- /dev/null
+++ b/test/sgi-roundtrip.ok
@@ -0,0 +1,8 @@
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+538848130 235
+538848130 235
+2394972481 463
+2394972481 463
diff --git a/test/sgi-roundtrip.test b/test/sgi-roundtrip.test
new file mode 100755
index 00000000..88efb75e
--- /dev/null
+++ b/test/sgi-roundtrip.test
@@ -0,0 +1,43 @@
+#! /bin/bash
+# This script tests: pnmtosgi sgitopnm
+# Also requires: rgb3toppm
+
+
+a_sgi=${tmpdir}/a.sgi
+a_red=${tmpdir}/a.red
+a_grn=${tmpdir}/a.grn
+a_blu=${tmpdir}/a.blu
+
+# Test 1.  Should produce 1926073387 101484 twice
+pnmtosgi -rle testimg.ppm | tee ${a_sgi} | sgitopnm  | cksum
+sgitopnm -channel=0  ${a_sgi} > ${a_red}
+sgitopnm -channel=1  ${a_sgi} > ${a_grn}
+sgitopnm -channel=2  ${a_sgi} > ${a_blu}
+rgb3toppm ${a_red} ${a_grn} ${a_blu} | cksum
+rm ${a_sgi} ${a_red} ${a_grn} ${a_blu}
+
+b_sgi=${tmpdir}/b.sgi
+b_red=${tmpdir}/b.red
+b_grn=${tmpdir}/b.grn
+b_blu=${tmpdir}/b.blu
+
+# Test 2.  Should produce 1926073387 101484 twice
+pnmtosgi -verbatim testimg.ppm | tee ${b_sgi} | sgitopnm  | cksum
+sgitopnm -channel=0  ${b_sgi} > ${b_red}
+sgitopnm -channel=1  ${b_sgi} > ${b_grn}
+sgitopnm -channel=2  ${b_sgi} > ${b_blu}
+rgb3toppm ${b_red} ${b_grn} ${b_blu} | cksum
+rm ${b_sgi} ${b_red} ${b_grn} ${b_blu}
+
+# Test 3.  Should produce 2425386270 41 twice
+pnmtosgi testgrid.pbm | sgitopnm | cksum             # Defaults to -rle
+pnmtosgi -verbatim testgrid.pbm | sgitopnm | cksum
+
+
+testgrid_pgm=${tmpdir}/testgrid.pgm
+
+# Test 4. Should produce 2394972481 463 twice
+pamdepth 65535 testgrid.pbm | pamtopnm | tee ${testgrid_pgm} | \
+  pnmtosgi -rle | sgitopnm | cksum
+pnmtosgi -verbatim ${testgrid_pgm} | sgitopnm | cksum
+rm ${testgrid_pgm}
diff --git a/test/st4-roundtrip.ok b/test/st4-roundtrip.ok
new file mode 100644
index 00000000..e43cd037
--- /dev/null
+++ b/test/st4-roundtrip.ok
@@ -0,0 +1 @@
+185194654 31695
diff --git a/test/st4-roundtrip.test b/test/st4-roundtrip.test
new file mode 100755
index 00000000..ffe8f348
--- /dev/null
+++ b/test/st4-roundtrip.test
@@ -0,0 +1,14 @@
+#! /bin/bash
+# This script tests: pgmtost4 st4topgm
+# Also requires: pamchannel pamtopnm pamcut
+
+
+# Input image of pgmtost4 must by 192x165
+
+# Should produce 185194654 31695 which is the output of:
+# pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | \
+#   pamtopnm | pamcut -pad 0 0 192 165 | cksum
+
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | \
+  pamtopnm | pamcut -pad 0 0 192 165 | \
+  pgmtost4 | st4topgm - | cksum
diff --git a/test/sunicon-roundtrip.ok b/test/sunicon-roundtrip.ok
new file mode 100644
index 00000000..845be5fb
--- /dev/null
+++ b/test/sunicon-roundtrip.ok
@@ -0,0 +1 @@
+2425386270 41
diff --git a/test/sunicon-roundtrip.test b/test/sunicon-roundtrip.test
new file mode 100755
index 00000000..a52fda68
--- /dev/null
+++ b/test/sunicon-roundtrip.test
@@ -0,0 +1,8 @@
+#! /bin/bash
+# This script tests: pbmtosunicon sunicontopnm
+# Also requires: pamcut
+
+# Width of Sun icons are multiples of 8.
+
+# Test.  Should print: 2425386270 41
+pbmtosunicon testgrid.pbm | sunicontopnm | pamcut 1 0 14 16 | cksum
diff --git a/test/sunrast-roundtrip.ok b/test/sunrast-roundtrip.ok
new file mode 100644
index 00000000..82eac5a8
--- /dev/null
+++ b/test/sunrast-roundtrip.ok
@@ -0,0 +1 @@
+1926073387 101484
diff --git a/test/sunrast-roundtrip.test b/test/sunrast-roundtrip.test
new file mode 100755
index 00000000..a5cfa8c7
--- /dev/null
+++ b/test/sunrast-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pnmtorast rasttopnm
+# Also requires:
+
+
+# Should produce 1926073387 101484, cksum of testimg.ppm
+pnmtorast testimg.ppm | rasttopnm | cksum
diff --git a/test/symmetry.ok b/test/symmetry.ok
new file mode 100644
index 00000000..23129684
--- /dev/null
+++ b/test/symmetry.ok
@@ -0,0 +1,12 @@
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
+ok
diff --git a/test/symmetry.test b/test/symmetry.test
new file mode 100755
index 00000000..e6a6b654
--- /dev/null
+++ b/test/symmetry.test
@@ -0,0 +1,97 @@
+#! /bin/bash
+# This script tests: pgmramp pamgauss pgmkernel pbmmake pbmpscale
+# Also requires: pamflip
+
+
+# All tests print "ok" upon success, cksum results otherwise.
+# The "$3>0" is a kludge for preventing false positives with empty files.
+
+# All test images are square and have the symmetries of the square (Dih4).
+# The sole exception is ell.pgm which is a rectangle (Dih2, also called
+# "Klein four-group".)
+
+# Example symmetric square PGM image:
+# P2
+# 5 5
+# 6
+# 1 2 3 2 1
+# 2 4 5 4 2
+# 3 5 6 5 3
+# 2 4 5 4 2
+# 1 2 3 2 1
+
+## Failure with this test indicates that a generator or editor which
+## should produce symmetric output images isn't doing so.
+
+tmpdir=${tmpdir:-/tmp}
+
+# Test 1.
+rect_pgm=${tmpdir}/rect.pgm
+
+pgmramp -rect 31 31 > ${rect_pgm}
+
+( for op in -null -tb -lr -r90
+    do pamflip $op ${rect_pgm} | cksum
+    done ) | uniq -c | \
+  awk '$1==4 && $3>0 { print "ok"; exit }; { print }'
+
+rm ${rect_pgm}
+
+# Test 2.
+circle_pgm=${tmpdir}/circle.pgm
+pgmramp -ell 63 63 > ${circle_pgm}
+
+( for op in -null -tb -lr -r90
+    do pamflip $op ${circle_pgm} | cksum
+    done ) | uniq -c | \
+  awk '$1==4 && $3>0 { print "ok"; exit }; { print }'
+
+rm ${circle_pgm}
+
+# Test 3.
+gauss_pgm=${tmpdir}/gauss.pgm
+pamgauss -sigma=0.1 -tupletype=GRAYSCALE 25 25 > ${gauss_pgm}
+
+( for op in -null -tb -lr -r90
+    do pamflip $op ${gauss_pgm} | cksum
+    done ) | uniq -c | \
+  awk '$1==4 && $3>0 { print "ok"; exit }; { print }'
+
+rm ${gauss_pgm}
+
+# Test 4.
+kernel_pgm=${tmpdir}/kernel.pgm
+pgmkernel 15 15 > ${kernel_pgm}
+
+( for op in -null -tb -lr -r90
+    do pamflip $op ${kernel_pgm} | cksum
+    done ) | uniq -c | \
+  awk '$1==4 && $3>0 { print "ok"; exit }; { print }'
+
+rm ${kernel_pgm}
+
+# Test 5.
+# Should print "ok" 7 times.
+pscale_pbm=${tmpdir}/pscale.pbm
+for size in `seq 1 7`
+do
+pbmmake -g 5 5 | pbmpscale $size > ${pscale_pbm}
+
+( for op in -null -tb -lr -r90
+    do pamflip $op ${pscale_pbm} | cksum
+    done ) | uniq -c | \
+  awk '$1==4 && $3>0 { print "ok"; exit }; { print }'
+
+rm ${pscale_pbm}
+done
+
+# Test 6.
+ell_pgm=${tmpdir}/ell.pgm
+pgmramp -ell 101 33 > ${ell_pgm}
+
+( for op in -null -tb -lr
+  do pamflip $op ${ell_pgm} | cksum
+  done ) | uniq -c | \
+  awk '$1==3 && $3>0 { print "ok"; exit }; { print }'
+
+rm ${ell_pgm}
diff --git a/test/targa-roundtrip.ok b/test/targa-roundtrip.ok
new file mode 100644
index 00000000..9a428195
--- /dev/null
+++ b/test/targa-roundtrip.ok
@@ -0,0 +1,3 @@
+2425386270 41
+1571496937 33838
+1926073387 101484
diff --git a/test/targa-roundtrip.test b/test/targa-roundtrip.test
new file mode 100755
index 00000000..f646b8c1
--- /dev/null
+++ b/test/targa-roundtrip.test
@@ -0,0 +1,18 @@
+#! /bin/bash
+# This script tests: pamtotga tgatoppm
+# Also requires: ppmtopgm pgmtopbm pamchannel
+
+
+#Test 1: Should print 2425386270 41, cksum of testgrid.pbm
+
+pamtotga -mono testgrid.pbm | \
+  tgatoppm | ppmtopgm | \
+  pgmtopbm -threshold -val 0.5 | cksum
+
+#Test 2:  Should produce 1571496937 33838, cksum of testimg.red
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | \
+  pamtotga -cmap | tgatoppm | ppmtopgm | cksum
+
+#Test 3: Should print 1926073387 101484, cksum of testimg.ppm
+
+pamtotga -rgb testimg.ppm | tgatoppm | cksum
diff --git a/testgrid.pbm b/test/testgrid.pbm
index cc6a50aa..cc6a50aa 100644
--- a/testgrid.pbm
+++ b/test/testgrid.pbm
diff --git a/testimg.ppm b/test/testimg.ppm
index 061d5368..061d5368 100644
--- a/testimg.ppm
+++ b/test/testimg.ppm
diff --git a/test/testrandom.c b/test/testrandom.c
new file mode 100644
index 00000000..43414926
--- /dev/null
+++ b/test/testrandom.c
@@ -0,0 +1,133 @@
+/*=============================================================================
+                                   testrandom
+===============================================================================
+  Test for the random number generator type by generating 5 values with rand()
+  and comparing them against values in a table.
+
+  Currently only recognizes ISO glibc rand().
+
+  Options:
+    -q : quiet mode
+    -v : verbose mode : Use to generate values for new table 
+
+  This is a self-contained program which does not require any libnetpbm
+  functions.
+=============================================================================*/
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Exit values */
+#define EXIT_ERROR 1
+#define EXIT_UNKNOWN 80
+#define ISO_GLIBC 81
+/* 82-90: reserved */
+
+typedef enum {QUIET=0, NORMAL=1, VERBOSE=2} VerbosityLevel;
+
+/* On some Sun systems RAND_MAX is not defined */
+#ifndef RAND_MAX
+#define RAND_MAX 0
+#endif
+
+#define SEED 3791
+
+static struct { 
+    int          const type;
+        /* Exit value for this rand() function  */
+    int          const randMax;
+        /* Usually 0x7fffffff, sometimes 0x7fff */
+        /* Other values are possible; 0 means undefined */
+    unsigned int const res[5];
+        /* Sample values returned from our tests */
+    const char * const name;
+        /* Name for this rand() function */
+} rTable[2] = {
+    { ISO_GLIBC,  /* glibc rand() */ 
+      0x7fffffff, /* 31 bits */ 
+      { 217313873, 969144303, 1757357552, 1096307597, 818311031 },
+      "ISO C glibc rand() or equivalent" },
+    
+    /* Insert additional entries here */
+    
+    { 0,   /* terminating code */
+      0,
+      {0, 0, 0, 0, 0}, NULL  /* unknown type */}
+};
+
+
+
+static void
+parseCommandLine(int              const argc,
+                 const char *     const argv[],
+                 VerbosityLevel * const verbosityP) {
+
+    *verbosityP = NORMAL; /* Initial value */
+
+    if (argc == 2) {
+        if (argv[1][0] == '-' && argv[1][2] == '\0') {
+            switch ( argv[1][1] ) {
+            case 'v' : *verbosityP = VERBOSE; break;
+            case 'q' : *verbosityP = QUIET  ; break;
+            default :  fprintf (stderr,
+                                "Error: Unrecognized argument: %s\n", argv[1]);
+                exit (EXIT_ERROR);
+            }
+        } 
+    }
+    if (argc > 2 ) {
+        fprintf (stderr, "Error: Too many arguments.\n");
+        exit (EXIT_ERROR);
+    }
+}
+
+
+
+int
+main(int const argc, const char * const argv[]) {
+
+    unsigned int i;
+    unsigned int res[5];
+    VerbosityLevel verbosity;
+
+    parseCommandLine(argc, argv, &verbosity);
+
+    if (verbosity == VERBOSE) {
+        if (RAND_MAX > 0)
+            fprintf(stderr, "RAND_MAX = 0x%x (%d)\n", RAND_MAX, RAND_MAX);
+        else
+            fprintf(stderr, "RAND_MAX is undefined\n");
+    }
+
+    /* Set seed for pseudo-random number generator */
+    if (verbosity == VERBOSE)
+        fprintf(stderr, "Generating samples. Seed=%u\n", SEED);
+
+    srand(SEED);
+
+    /*  Generate five samples and store in array res[] */
+    for (i = 0; i < 5; ++i) {
+        res[i] = rand();
+        if (verbosity == VERBOSE)
+            fprintf (stderr, "%d\n",res[i]);
+    }
+
+    /*  Look for a match in table */
+    for (i = 0; rTable[i].type != 0; ++i) {
+        if (rTable[i].randMax == RAND_MAX && rTable[i].res[0] == res[0] &&
+            rTable[i].res[1] == res[1] &&    rTable[i].res[2] == res[2] &&
+            rTable[i].res[3] == res[3] &&    rTable[i].res[4] == res[4]) {
+            if (verbosity != QUIET)
+                fprintf(stderr,
+                        "Random number generator is %s.\n", rTable[i].name);
+
+            exit(rTable[i].type);
+        }
+    }
+    /* No matches */
+    if (verbosity != QUIET)   
+        fprintf(stderr, "Random number generator is of unknown type.\n");
+    exit(EXIT_UNKNOWN);
+}
+
+
+
diff --git a/test/tiff-roundtrip.ok b/test/tiff-roundtrip.ok
new file mode 100644
index 00000000..0e712ce7
--- /dev/null
+++ b/test/tiff-roundtrip.ok
@@ -0,0 +1,4 @@
+1926073387 101484
+1926073387 101484
+2425386270 41
+2425386270 41
diff --git a/test/tiff-roundtrip.test b/test/tiff-roundtrip.test
new file mode 100755
index 00000000..a99425fa
--- /dev/null
+++ b/test/tiff-roundtrip.test
@@ -0,0 +1,23 @@
+#! /bin/bash
+# This script tests: pamtotiff tifftopnm
+# Also requires:
+
+
+# Failure message
+## Second test fails if Netpbm was built without the flate library
+
+pamtotiff testimg.ppm 1<>${tmpdir}/test1.tiff &&
+  tifftopnm ${tmpdir}/test1.tiff | cksum
+
+# test flate compression
+pamtotiff -flate testimg.ppm 1<>${tmpdir}/test2.tiff &&
+  tifftopnm ${tmpdir}/test2.tiff | cksum
+
+pamtotiff testgrid.pbm 1<>${tmpdir}/test3.tiff &&
+  tifftopnm ${tmpdir}/test3.tiff | cksum
+
+# test G4 compression
+pamtotiff -g4 testgrid.pbm 1<>${tmpdir}/test4.tiff &&
+  tifftopnm ${tmpdir}/test4.tiff | cksum
+
+rm ${tmpdir}/test[1234].tiff
diff --git a/test/utahrle-roundtrip.ok b/test/utahrle-roundtrip.ok
new file mode 100644
index 00000000..203001aa
--- /dev/null
+++ b/test/utahrle-roundtrip.ok
@@ -0,0 +1,2 @@
+1571496937 33838
+1926073387 101484
diff --git a/test/utahrle-roundtrip.test b/test/utahrle-roundtrip.test
new file mode 100755
index 00000000..982b72a9
--- /dev/null
+++ b/test/utahrle-roundtrip.test
@@ -0,0 +1,12 @@
+#! /bin/bash
+# This script tests: pnmtorle rletopnm
+# Also requires: pamchannel pamtopnm
+
+
+#Test 1.  Should produce 1571496937 33838, cksum of testimg.red
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  pnmtorle | rletopnm | cksum
+
+#Test 2.  Should print 1926073387 101484, cksum of testimg.ppm
+pnmtorle testimg.ppm | \
+  rletopnm | cksum
diff --git a/test/wbmp-roundtrip.ok b/test/wbmp-roundtrip.ok
new file mode 100644
index 00000000..845be5fb
--- /dev/null
+++ b/test/wbmp-roundtrip.ok
@@ -0,0 +1 @@
+2425386270 41
diff --git a/test/wbmp-roundtrip.test b/test/wbmp-roundtrip.test
new file mode 100755
index 00000000..b651c3d0
--- /dev/null
+++ b/test/wbmp-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pbmtowbmp wbmptopbm
+# Also requires:
+
+
+# Should print 2425386270 41, cksum of testgrid.pbm
+pbmtowbmp testgrid.pbm | wbmptopbm | cksum
diff --git a/test/winicon-roundtrip.ok b/test/winicon-roundtrip.ok
new file mode 100644
index 00000000..8334ff4e
--- /dev/null
+++ b/test/winicon-roundtrip.ok
@@ -0,0 +1,2 @@
+71846281 6925
+326197919 137
diff --git a/test/winicon-roundtrip.test b/test/winicon-roundtrip.test
new file mode 100755
index 00000000..b37a33e1
--- /dev/null
+++ b/test/winicon-roundtrip.test
@@ -0,0 +1,13 @@
+#! /bin/bash
+# This script tests: pamtowinicon winicontopam
+# Also requires: pamcut pnmtile pamtopnm ppmtopgm pgmtopbm
+
+
+pamcut --left=30 --width=48 --height=48 testimg.ppm | \
+pamtowinicon | winicontopam | \
+  pamtopnm | cksum
+
+pnmtile 32 32 testgrid.pbm | \
+pamtowinicon | winicontopam | \
+  pamtopnm | ppmtopgm | \
+  pgmtopbm  -th -val=0.5 | cksum
diff --git a/test/xbm-roundtrip.ok b/test/xbm-roundtrip.ok
new file mode 100644
index 00000000..a676a1f2
--- /dev/null
+++ b/test/xbm-roundtrip.ok
@@ -0,0 +1,2 @@
+2425386270 41
+2425386270 41
diff --git a/test/xbm-roundtrip.test b/test/xbm-roundtrip.test
new file mode 100755
index 00000000..e84d6fc3
--- /dev/null
+++ b/test/xbm-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pbmtoxbm xbmtopbm
+# Also requires:
+
+
+pbmtoxbm testgrid.pbm | xbmtopbm | cksum
+pbmtoxbm -x10 testgrid.pbm | xbmtopbm | cksum
diff --git a/test/xpm-roundtrip.ok b/test/xpm-roundtrip.ok
new file mode 100644
index 00000000..845be5fb
--- /dev/null
+++ b/test/xpm-roundtrip.ok
@@ -0,0 +1 @@
+2425386270 41
diff --git a/test/xpm-roundtrip.test b/test/xpm-roundtrip.test
new file mode 100755
index 00000000..fd0253b1
--- /dev/null
+++ b/test/xpm-roundtrip.test
@@ -0,0 +1,9 @@
+#! /bin/bash
+# This script tests: ppmtoxpm xpmtoppm
+# Also requires: pgmtopbm ppmtopgm
+
+
+#ppmtoxpm -hexonly testimg.ppm | \
+# xpmtoppm  | cksum
+ppmtoxpm testgrid.pbm | xpmtoppm | \
+  ppmtopgm | pgmtopbm -th -value=0.5 | cksum
diff --git a/test/xv-roundtrip.ok b/test/xv-roundtrip.ok
new file mode 100644
index 00000000..ac7f0d99
--- /dev/null
+++ b/test/xv-roundtrip.ok
@@ -0,0 +1 @@
+2418728144 101484
diff --git a/test/xv-roundtrip.test b/test/xv-roundtrip.test
new file mode 100755
index 00000000..d73933a1
--- /dev/null
+++ b/test/xv-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pamtoxvmini xvminitoppm
+# Also requires: pamdepth
+
+# Test.  Should print 2418728144 101484
+
+pamdepth 3 testimg.ppm | pamtoxvmini | xvminitoppm | cksum
diff --git a/test/xwd-roundtrip.ok b/test/xwd-roundtrip.ok
new file mode 100644
index 00000000..25d3d871
--- /dev/null
+++ b/test/xwd-roundtrip.ok
@@ -0,0 +1,3 @@
+1571496937 33838
+1926073387 101484
+2425386270 41
diff --git a/test/xwd-roundtrip.test b/test/xwd-roundtrip.test
new file mode 100755
index 00000000..cd0d38ad
--- /dev/null
+++ b/test/xwd-roundtrip.test
@@ -0,0 +1,16 @@
+#! /bin/bash
+# This script tests: pnmtoxwd xwdtopnm
+# Also requires: pamchannel pamtopnm pamdepth
+
+
+# Test 1.  Should produce 1571496937 33838, cksum of testimg.red
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \
+  pnmtoxwd | xwdtopnm | pamdepth 255 | cksum
+
+# Test 2.  Should produce 1926073387 101484
+pnmtoxwd --quiet  testimg.ppm | \
+  xwdtopnm --quiet | pamdepth 255 | cksum
+
+# Test 3.  Should produce 2425386270 41
+pnmtoxwd --quiet  testgrid.pbm | \
+  xwdtopnm | cksum
diff --git a/test/yuv-roundtrip.ok b/test/yuv-roundtrip.ok
new file mode 100644
index 00000000..b4050681
--- /dev/null
+++ b/test/yuv-roundtrip.ok
@@ -0,0 +1 @@
+1904478375 253455
diff --git a/test/yuv-roundtrip.test b/test/yuv-roundtrip.test
new file mode 100755
index 00000000..e339b418
--- /dev/null
+++ b/test/yuv-roundtrip.test
@@ -0,0 +1,10 @@
+#! /bin/bash
+# This script tests: ppmtoyuv yuvtoppm
+# Also requires: pamgradient
+
+
+# Should produce 1904478375 253455
+
+pamgradient rgb:00/ff/ff rgb:ff/ff/00 \
+                            rgb:ff/00/00 rgb:00/ff/00 352 240 | \
+    ppmtoyuv | yuvtoppm 352 240 | cksum
diff --git a/urt/Makefile b/urt/Makefile
index 57543b0c..0aef5290 100644
--- a/urt/Makefile
+++ b/urt/Makefile
@@ -5,6 +5,8 @@ endif
 SUBDIR = urt
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
+default: all
+
 include $(BUILDDIR)/config.mk
 
 LIBOBJECTS = Runput.o cmd_name.o \
@@ -15,6 +17,9 @@ LIBOBJECTS = Runput.o cmd_name.o \
 
 MERGE_OBJECTS =
 
+OMIT_URT_RULE = 1
+include $(SRCDIR)/common.mk
+
 all: librle.a
 
 librle.a: $(LIBOBJECTS)
@@ -24,11 +29,8 @@ librle.a: $(LIBOBJECTS)
 
 # Rule for objects.
 $(LIBOBJECTS): %.o: %.c importinc
-	$(CC) -c -I importinc -o $@ \
-	  $< $(CPPFLAGS) $(CFLAGS) $(CFLAGS_PERSONAL) $(CADD)
+	$(CC) -c $(INCLUDES) -o $@ $< $(CFLAGS_ALL)
 
 BINARIES =
 SCRIPTS =
 
-OMIT_URT_RULE = 1
-include $(SRCDIR)/common.mk
diff --git a/urt/cmd_name.c b/urt/cmd_name.c
index 1f8f0edf..31fe5f42 100644
--- a/urt/cmd_name.c
+++ b/urt/cmd_name.c
@@ -26,6 +26,9 @@
  */
 
 #include "rle.h"
+
+
+
 static char no_name[] = "(no-name)";
 
 char *
diff --git a/urt/rle.h b/urt/rle.h
index 71f15d28..0766d22a 100644
--- a/urt/rle.h
+++ b/urt/rle.h
@@ -30,14 +30,8 @@
 #ifndef RLE_H
 #define RLE_H
 
-#include "rle_config.h"     /* Configuration parameters. */
-
 #include <stdio.h>      /* Declare FILE. */
 
-#ifdef c_plusplus
-#define USE_PROTOTYPES
-#endif
-
 enum rle_dispatch {
     NO_DISPATCH = -1,
     RUN_DISPATCH = 0
diff --git a/urt/rle_addhist.c b/urt/rle_addhist.c
index 04e26206..b1651754 100644
--- a/urt/rle_addhist.c
+++ b/urt/rle_addhist.c
@@ -25,19 +25,13 @@
  * Copyright (c) 1988, Curtin University of Technology
  */
 
-#include "rle.h"
-
 #include <string.h>
 #include <stdio.h>
-
-#ifdef  USE_TIME_H
 #include <time.h>
-#else
-#include <sys/types.h>
-#include <sys/time.h>
-#endif
 
-#include "mallocvar.h"
+#include "netpbm/mallocvar.h"
+#include "rle.h"
+
 
 /*****************************************************************
  * TAG( rle_addhist )
diff --git a/urt/rle_config.h b/urt/rle_config.h
index f3fa5bbc..57126a18 100644
--- a/urt/rle_config.h
+++ b/urt/rle_config.h
@@ -1,10 +1,7 @@
-/* rle_config.h
- * 
- * Automatically generated by make-config-h script.
- * DO NOT EDIT THIS FILE.
- * Edit include/makefile.src and the configuration file instead.
- */
-#if defined(WIN32) && !defined(__CYGWIN__)
+#ifndef RLE_CONFIG_H_INCLUDED
+#define RLE_CONFIG_H_INCLUDED
+#include "pm_config.h"
+#if MSVCRT
 #define NO_OPEN_PIPES
 #endif
 
@@ -26,7 +23,6 @@
 #define NO_MAKE_MAKEFILE NO_MAKE_MAKEFILE
 #define NO_TOOLS NO_TOOLS
 #define USE_TIME_H USE_TIME_H
-#define USE_PROTOTYPES USE_PROTOTYPES
 #define USE_RANDOM USE_RANDOM
 #define USE_STDARG USE_STDARG
 #define USE_STDLIB_H USE_STDLIB_H
@@ -58,37 +54,20 @@ typedef	void *void_star;
 typedef char *void_star;
 #endif
 
-#ifdef USE_STDLIB_H
-#include <stdlib.h>
-#else
-
 /* Some programs include files from other packages that also declare
  * malloc.  Avoid double declaration by #define NO_DECLARE_MALLOC
  * before including this file.
  */
 #ifndef NO_DECLARE_MALLOC
-#ifdef USE_PROTOTYPES
 #   include <sys/types.h>	/* For size_t. */
     extern void_star malloc( size_t );
     extern void_star calloc( size_t, size_t );
     extern void_star realloc( void_star, size_t );
     extern void free( void_star );
-#else
-    extern void_star malloc();
-    extern void_star realloc();
-    extern void_star calloc();
-    extern void free();
-    extern void cfree();
-#endif /* USE_PROTOTYPES */
 #endif /* NO_DECLARE_MALLOC */
 
-#ifdef USE_PROTOTYPES
 extern char *getenv( CONST_DECL char *name );
-#else
-extern char *getenv();
-#endif
 
-#endif /* USE_STDLIB_H */
 
 #ifdef NEED_BSTRING
     /* From bstring.c. */
@@ -103,3 +82,5 @@ extern char *getenv();
 #ifdef NEED_SETLINEBUF
 #   define setlinebuf( _s )	setvbuf( (_s), NULL, _IOLBF, 0 )
 #endif
+
+#endif
diff --git a/urt/rle_error.c b/urt/rle_error.c
index acaca1a6..da314f0b 100644
--- a/urt/rle_error.c
+++ b/urt/rle_error.c
@@ -27,6 +27,7 @@
 
 #include <string.h>
 
+#include "rle_config.h"
 #include "rle.h"
 
 /*****************************************************************
diff --git a/urt/rle_getcom.c b/urt/rle_getcom.c
index 20da56a9..4226eaaf 100644
--- a/urt/rle_getcom.c
+++ b/urt/rle_getcom.c
@@ -26,6 +26,7 @@
  */
 
 #include <stdio.h>
+
 #include "rle.h"
 
 /*****************************************************************
diff --git a/urt/rle_getrow.c b/urt/rle_getrow.c
index fadf5441..679811cc 100644
--- a/urt/rle_getrow.c
+++ b/urt/rle_getrow.c
@@ -33,8 +33,8 @@
 #include <string.h>
 #include <stdio.h>
 
-#include "pm.h"
-#include "mallocvar.h"
+#include "netpbm/pm.h"
+#include "netpbm/mallocvar.h"
 
 #include "rle.h"
 #include "rle_code.h"
@@ -52,27 +52,26 @@
 
 static int     debug_f;     /* If non-zero, print debug info. */
 
-/*****************************************************************
- * TAG( rle_get_setup )
- * 
- * Read the initialization information from an RLE file.
- * Inputs:
- *  the_hdr:    Contains pointer to the input file.
- * Outputs:
- *  the_hdr:    Initialized with information from the
- *          input file.
- *  Returns 0 on success, -1 if the file is not an RLE file,
- *  -2 if malloc of the color map failed, -3 if an immediate EOF
- *  is hit (empty input file), and -4 if an EOF is encountered reading
- *  the setup information.
- * Assumptions:
- *  infile points to the "magic" number in an RLE file (usually
- * byte 0 in the file).
- * Algorithm:
- *  Read in the setup info and fill in the_hdr.
- */
 int
 rle_get_setup(rle_hdr * const the_hdr) {
+/*-----------------------------------------------------------------------------
+  Read the initialization information from an RLE file.
+  Inputs:
+    the_hdr:    Contains pointer to the input file.
+  Outputs:
+    the_hdr:    Initialized with information from the input file.
+  Returns
+     0  on success,
+     -1 if the file is not an RLE file,
+     -2 if malloc of the color map failed,
+     -3 if an immediate EOF is hit (empty input file)
+     -4 if an EOF is encountered reading the setup information.
+  Assumptions:
+    infile points to the "magic" number in an RLE file (usually  byte 0
+    in the file).
+  Algorithm:
+    Read in the setup info and fill in the_hdr.
+---------------------------------------------------------------------------- */
     struct XtndRsetup setup;
     short magic;
     FILE * infile = the_hdr->rle_file;
@@ -80,116 +79,106 @@ rle_get_setup(rle_hdr * const the_hdr) {
     char * comment_buf;
     
     /* Clear old stuff out of the header. */
-    rle_hdr_clear( the_hdr );
-    if ( the_hdr->is_init != RLE_INIT_MAGIC )
-        rle_names( the_hdr, "Urt", "some file", 0 );
-    the_hdr->img_num++;     /* Count images. */
+    rle_hdr_clear(the_hdr);
+    if (the_hdr->is_init != RLE_INIT_MAGIC)
+        rle_names(the_hdr, "Urt", "some file", 0);
+    ++the_hdr->img_num;     /* Count images. */
 
-    VAXSHORT( magic, infile );
-    if ( feof( infile ) )
+    VAXSHORT(magic, infile);
+    if (feof(infile))
         return RLE_EMPTY;
-    if ( magic != RLE_MAGIC )
+    if (magic != RLE_MAGIC)
         return RLE_NOT_RLE;
-    fread( &setup, 1, SETUPSIZE, infile );  /* assume VAX packing */
-    if ( feof( infile ) )
+    fread(&setup, 1, SETUPSIZE, infile);  /* assume VAX packing */
+    if (feof( infile))
         return RLE_EOF;
 
     /* Extract information from setup */
     the_hdr->ncolors = setup.h_ncolors;
-    for ( i = 0; i < the_hdr->ncolors; i++ )
-        RLE_SET_BIT( *the_hdr, i );
+    for (i = 0; i < the_hdr->ncolors; ++i)
+        RLE_SET_BIT(*the_hdr, i);
 
-    if ( !(setup.h_flags & H_NO_BACKGROUND) && setup.h_ncolors > 0 )
-    {
+    if (!(setup.h_flags & H_NO_BACKGROUND) && setup.h_ncolors > 0) {
         rle_pixel * bg_color;
 
         MALLOCARRAY(the_hdr->bg_color, setup.h_ncolors);
         MALLOCARRAY(bg_color, 1 + (setup.h_ncolors / 2) * 2);
-        RLE_CHECK_ALLOC( the_hdr->cmd, the_hdr->bg_color && bg_color,
-                         "background color" );
-        fread( (char *)bg_color, 1, 1 + (setup.h_ncolors / 2) * 2, infile );
-        for ( i = 0; i < setup.h_ncolors; i++ )
+        RLE_CHECK_ALLOC(the_hdr->cmd, the_hdr->bg_color && bg_color,
+                        "background color" );
+        fread((char *)bg_color, 1, 1 + (setup.h_ncolors / 2) * 2, infile);
+        for (i = 0; i < setup.h_ncolors; ++i)
             the_hdr->bg_color[i] = bg_color[i];
-        free( bg_color );
-    }
-    else
-    {
-        (void)getc( infile );   /* skip filler byte */
+        free(bg_color);
+    } else {
+        getc(infile);   /* skip filler byte */
         the_hdr->bg_color = NULL;
     }
 
-    if ( setup.h_flags & H_NO_BACKGROUND )
+    if (setup.h_flags & H_NO_BACKGROUND)
         the_hdr->background = 0;
-    else if ( setup.h_flags & H_CLEARFIRST )
+    else if (setup.h_flags & H_CLEARFIRST)
         the_hdr->background = 2;
     else
         the_hdr->background = 1;
-    if ( setup.h_flags & H_ALPHA )
-    {
+    if (setup.h_flags & H_ALPHA) {
         the_hdr->alpha = 1;
         RLE_SET_BIT( *the_hdr, RLE_ALPHA );
-    }
-    else
+    } else
         the_hdr->alpha = 0;
 
-    the_hdr->xmin = vax_gshort( setup.hc_xpos );
-    the_hdr->ymin = vax_gshort( setup.hc_ypos );
-    the_hdr->xmax = the_hdr->xmin + vax_gshort( setup.hc_xlen ) - 1;
-    the_hdr->ymax = the_hdr->ymin + vax_gshort( setup.hc_ylen ) - 1;
+    the_hdr->xmin = vax_gshort(setup.hc_xpos);
+    the_hdr->ymin = vax_gshort(setup.hc_ypos);
+    the_hdr->xmax = the_hdr->xmin + vax_gshort(setup.hc_xlen) - 1;
+    the_hdr->ymax = the_hdr->ymin + vax_gshort(setup.hc_ylen) - 1;
 
     the_hdr->ncmap = setup.h_ncmap;
     the_hdr->cmaplen = setup.h_cmaplen;
-    if ( the_hdr->ncmap > 0 )
-    {
+    if (the_hdr->ncmap > 0) {
         int const maplen = the_hdr->ncmap * (1 << the_hdr->cmaplen);
+
         int i;
         char *maptemp;
 
         MALLOCARRAY(the_hdr->cmap, maplen);
         MALLOCARRAY(maptemp, 2 * maplen);
-        if ( the_hdr->cmap == NULL || maptemp == NULL )
-        {
+        if (the_hdr->cmap == NULL || maptemp == NULL) {
             pm_error("Malloc failed for color map of size %d*%d "
                      "in rle_get_setup, reading '%s'",
                      the_hdr->ncmap, (1 << the_hdr->cmaplen),
                      the_hdr->file_name );
             return RLE_NO_SPACE;
         }
-        fread( maptemp, 2, maplen, infile );
-        for ( i = 0; i < maplen; i++ )
-            the_hdr->cmap[i] = vax_gshort( &maptemp[i * 2] );
-        free( maptemp );
+        fread(maptemp, 2, maplen, infile);
+        for (i = 0; i < maplen; ++i)
+            the_hdr->cmap[i] = vax_gshort(&maptemp[i * 2]);
+        free(maptemp);
     }
 
     /* Check for comments */
-    if ( setup.h_flags & H_COMMENT )
-    {
+    if (setup.h_flags & H_COMMENT) {
         short comlen, evenlen;
-        register char * cp;
+        char * cp;
 
-        VAXSHORT( comlen, infile ); /* get comment length */
+        VAXSHORT(comlen, infile); /* get comment length */
         evenlen = (comlen + 1) & ~1;    /* make it even */
-        if ( evenlen )
-        {
+        if (evenlen) {
             MALLOCARRAY(comment_buf, evenlen);
     
-            if ( comment_buf == NULL )
-            {
+            if (comment_buf == NULL) {
                 pm_error("Malloc failed for comment buffer of size %d "
                          "in rle_get_setup, reading '%s'",
                          comlen, the_hdr->file_name );
                 return RLE_NO_SPACE;
             }
-            fread( comment_buf, 1, evenlen, infile );
+            fread(comment_buf, 1, evenlen, infile);
             /* Count the comments */
-            for ( i = 0, cp = comment_buf; cp < comment_buf + comlen; cp++ )
-                if ( *cp == 0 )
-                    i++;
-            i++;            /* extra for NULL pointer at end */
+            for (i = 0, cp = comment_buf; cp < comment_buf + comlen; ++cp)
+                if (*cp == '\0')
+                    ++i;
+            ++i;            /* extra for NULL pointer at end */
             /* Get space to put pointers to comments */
             MALLOCARRAY(the_hdr->comments, i);
-            if ( the_hdr->comments == NULL )
-            {
+            if (the_hdr->comments == NULL) {
                 pm_error("Malloc failed for %d comment pointers "
                          "in rle_get_setup, reading '%s'",
                          i, the_hdr->file_name );
@@ -197,366 +186,331 @@ rle_get_setup(rle_hdr * const the_hdr) {
             }
             /* Get pointers to the comments */
             *the_hdr->comments = comment_buf;
-            for ( i = 1, cp = comment_buf + 1;
-                  cp < comment_buf + comlen;
-                  cp++ )
-                if ( *(cp - 1) == 0 )
+            for (i = 1, cp = comment_buf + 1;
+                 cp < comment_buf + comlen;
+                 ++cp)
+                if (*(cp - 1) == '\0')
                     the_hdr->comments[i++] = cp;
             the_hdr->comments[i] = NULL;
-        }
-        else
+        } else
             the_hdr->comments = NULL;
-    }
-    else
+    } else
         the_hdr->comments = NULL;
 
     /* Initialize state for rle_getrow */
     the_hdr->priv.get.scan_y = the_hdr->ymin;
     the_hdr->priv.get.vert_skip = 0;
     the_hdr->priv.get.is_eof = 0;
-    the_hdr->priv.get.is_seek = ftell( infile ) > 0;
+    the_hdr->priv.get.is_seek = ftell(infile) > 0;
     debug_f = 0;
 
-    if ( !feof( infile ) )
+    if (!feof(infile))
         return RLE_SUCCESS; /* success! */
-    else
-    {
+    else {
         the_hdr->priv.get.is_eof = 1;
         return RLE_EOF;
     }
 }
 
 
-/*****************************************************************
- * TAG( rle_get_setup_ok )
- * 
- * Read the initialization information from an RLE file.
- * Inputs:
- *  the_hdr:    Contains pointer to the input file.
- *  prog_name:  Program name to be printed in the error message.
- *      file_name:  File name to be printed in the error message.
- *                  If NULL, the string "stdin" is generated.
- * Outputs:
- *  the_hdr:    Initialized with information from the
- *          input file.
- *      If reading the header fails, it prints an error message
- *  and exits with the appropriate status code.
- * Algorithm:
- *  rle_get_setup does all the work.
- */
+
 void
-rle_get_setup_ok( the_hdr, prog_name, file_name )
-rle_hdr * the_hdr;
-const char *prog_name;
-const char *file_name;
-{
+rle_get_setup_ok(rle_hdr *    const the_hdr,
+                 const char * const prog_name,
+                 const char * const file_name) {
+/*-----------------------------------------------------------------------------
+  Read the initialization information from an RLE file.
+
+  Inputs:
+   the_hdr:    Contains pointer to the input file.
+   prog_name:  Program name to be printed in the error message.
+       file_name:  File name to be printed in the error message.
+                   If NULL, the string "stdin" is generated.
+
+  Outputs:
+   the_hdr:    Initialized with information from the input file.
+       If reading the header fails, it prints an error message
+       and exits with the appropriate status code.
+  Algorithm:
+   rle_get_setup does all the work.
+---------------------------------------------------------------------------- */
     int code;
 
     /* Backwards compatibility: if is_init is not properly set, 
      * initialize the header.
      */
-    if ( the_hdr->is_init != RLE_INIT_MAGIC )
-    {
-    FILE *f = the_hdr->rle_file;
-    rle_hdr_init( the_hdr );
-    the_hdr->rle_file = f;
-    rle_names( the_hdr, prog_name, file_name, 0 );
+    if (the_hdr->is_init != RLE_INIT_MAGIC) {
+        FILE * const f = the_hdr->rle_file;
+        rle_hdr_init( the_hdr );
+        the_hdr->rle_file = f;
+        rle_names(the_hdr, prog_name, file_name, 0);
     }
 
-    code = rle_get_error( rle_get_setup( the_hdr ),
-              the_hdr->cmd, the_hdr->file_name );
+    code = rle_get_error(rle_get_setup(the_hdr),
+                         the_hdr->cmd, the_hdr->file_name);
     if (code)
-    exit( code );
+        exit(code);
 }
 
 
-/*****************************************************************
- * TAG( rle_debug )
- * 
- * Turn RLE debugging on or off.
- * Inputs:
- *  on_off:     if 0, stop debugging, else start.
- * Outputs:
- *  Sets internal debug flag.
- * Assumptions:
- *  [None]
- * Algorithm:
- *  [None]
- */
+
 void
 rle_debug( on_off )
-int on_off;
+    int on_off;
 {
+/*-----------------------------------------------------------------------------
+  Turn RLE debugging on or off.
+  Inputs:
+   on_off:     if 0, stop debugging, else start.
+  Outputs:
+   Sets internal debug flag.
+  Assumptions:
+   [None]
+  Algorithm:
+   [None]
+---------------------------------------------------------------------------- */
     debug_f = on_off;
 
     /* Set line buffering on stderr.  Character buffering is the default, and
      * it is SLOOWWW for large amounts of output.
      */
-    setvbuf( stderr, NULL, _IOLBF, 0);
-/*
-    setlinebuf( stderr );
-*/
+    setvbuf(stderr, NULL, _IOLBF, 0);
 }
 
 
-/*****************************************************************
- * TAG( rle_getrow )
- * 
- * Get a scanline from the input file.
- * Inputs:
- *  the_hdr:    Header structure containing information about 
- *          the input file.
- * Outputs:
- *  scanline:   an array of pointers to the individual color
- *          scanlines.  Scanline is assumed to have
- *          the_hdr->ncolors pointers to arrays of rle_pixel,
- *          each of which is at least the_hdr->xmax+1 long.
- *  Returns the current scanline number.
- * Assumptions:
- *  rle_get_setup has already been called.
- * Algorithm:
- *  If a vertical skip is being executed, and clear-to-background is
- *  specified (the_hdr->background is true), just set the
- *  scanlines to the background color.  If clear-to-background is
- *  not set, just increment the scanline number and return.
- * 
- *  Otherwise, read input until a vertical skip is encountered,
- *  decoding the instructions into scanline data.
- *
- *  If ymax is reached (or, somehow, passed), continue reading and
- *  discarding input until end of image.
- */
+
 int
-rle_getrow( the_hdr, scanline )
-rle_hdr * the_hdr;
-rle_pixel *scanline[];
-{
-    register rle_pixel * scanc;
-    register int nc;
-    register FILE *infile = the_hdr->rle_file;
-    int scan_x = the_hdr->xmin, /* current X position */
-        max_x = the_hdr->xmax,  /* End of the scanline */
-       channel = 0;         /* current color channel */
+rle_getrow(rle_hdr *    const the_hdr,
+           rle_pixel ** const scanline) {
+/*-----------------------------------------------------------------------------
+  Get a scanline from the input file.
+  Inputs:
+   the_hdr:    Header structure containing information about 
+           the input file.
+  Outputs:
+   scanline:   an array of pointers to the individual color
+           scanlines.  Scanline is assumed to have
+           the_hdr->ncolors pointers to arrays of rle_pixel,
+           each of which is at least the_hdr->xmax+1 long.
+   Returns the current scanline number.
+  Assumptions:
+   rle_get_setup has already been called.
+  Algorithm:
+   If a vertical skip is being executed, and clear-to-background is
+   specified (the_hdr->background is true), just set the
+   scanlines to the background color.  If clear-to-background is
+   not set, just increment the scanline number and return.
+  
+   Otherwise, read input until a vertical skip is encountered,
+   decoding the instructions into scanline data.
+ 
+   If ymax is reached (or, somehow, passed), continue reading and
+   discarding input until end of image.
+---------------------------------------------------------------------------- */
+    FILE * const infile = the_hdr->rle_file;
+
+    rle_pixel * scanc;
+
+    int scan_x; /* current X position */
+    int max_x;  /* End of the scanline */
+    int channel;         /* current color channel */
     int ns;         /* Number to skip */
+    int nc;
     short word, long_data;
     char inst[2];
 
+    scan_x = the_hdr->xmin; /* initial value */
+    max_x = the_hdr->xmax;  /* initial value */
+    channel = 0; /* initial value */
     /* Clear to background if specified */
-    if ( the_hdr->background != 1 )
-    {
-    if ( the_hdr->alpha && RLE_BIT( *the_hdr, -1 ) )
-        memset( (char *)scanline[-1] + the_hdr->xmin, 0,
-           the_hdr->xmax - the_hdr->xmin + 1 );
-    for ( nc = 0; nc < the_hdr->ncolors; nc++ )
-        if ( RLE_BIT( *the_hdr, nc ) ) {
-        /* Unless bg color given explicitly, use 0. */
-        if ( the_hdr->background != 2 || the_hdr->bg_color[nc] == 0 )
-            memset( (char *)scanline[nc] + the_hdr->xmin, 0,
-               the_hdr->xmax - the_hdr->xmin + 1 );
-        else
-            memset((char *)scanline[nc] + the_hdr->xmin,
-                   the_hdr->bg_color[nc],
+    if (the_hdr->background != 1) {
+        if (the_hdr->alpha && RLE_BIT( *the_hdr, -1))
+            memset((char *)scanline[-1] + the_hdr->xmin, 0,
                    the_hdr->xmax - the_hdr->xmin + 1);
+        for (nc = 0; nc < the_hdr->ncolors; ++nc) {
+            if (RLE_BIT( *the_hdr, nc)) {
+                /* Unless bg color given explicitly, use 0. */
+                if (the_hdr->background != 2 || the_hdr->bg_color[nc] == 0)
+                    memset((char *)scanline[nc] + the_hdr->xmin, 0,
+                           the_hdr->xmax - the_hdr->xmin + 1);
+                else
+                    memset((char *)scanline[nc] + the_hdr->xmin,
+                           the_hdr->bg_color[nc],
+                           the_hdr->xmax - the_hdr->xmin + 1);
+            }
         }
     }
 
     /* If skipping, then just return */
-    if ( the_hdr->priv.get.vert_skip > 0 )
-    {
-    the_hdr->priv.get.vert_skip--;
-    the_hdr->priv.get.scan_y++;
-    if ( the_hdr->priv.get.vert_skip > 0 ) {
-        if ( the_hdr->priv.get.scan_y >= the_hdr->ymax )
-        {
-        int y = the_hdr->priv.get.scan_y;
-        while ( rle_getskip( the_hdr ) != 32768 )
-            ;
-        return y;
+    if (the_hdr->priv.get.vert_skip > 0) {
+        --the_hdr->priv.get.vert_skip;
+        ++the_hdr->priv.get.scan_y;
+        if (the_hdr->priv.get.vert_skip > 0) {
+            if (the_hdr->priv.get.scan_y >= the_hdr->ymax) {
+                int const y = the_hdr->priv.get.scan_y;
+                while (rle_getskip(the_hdr) != 32768)
+                    ;
+                return y;
+            } else
+                return the_hdr->priv.get.scan_y;
         }
-        else
-        return the_hdr->priv.get.scan_y;
-    }
     }
 
     /* If EOF has been encountered, return also */
-    if ( the_hdr->priv.get.is_eof )
-    return ++the_hdr->priv.get.scan_y;
+    if (the_hdr->priv.get.is_eof)
+        return ++the_hdr->priv.get.scan_y;
 
     /* Otherwise, read and interpret instructions until a skipLines
-     * instruction is encountered.
-     */
-    if ( RLE_BIT( *the_hdr, channel ) )
-    scanc = scanline[channel] + scan_x;
+       instruction is encountered.
+    */
+    if (RLE_BIT(*the_hdr, channel))
+        scanc = scanline[channel] + scan_x;
     else
-    scanc = NULL;
-    for (;;)
-    {
-    inst[0] = getc( infile );
-    inst[1] = getc( infile );
-    if ( feof(infile) )
-    {
-        the_hdr->priv.get.is_eof = 1;
-        break;      /* <--- one of the exits */
-    }
-
-    switch( OPCODE(inst) )
-    {
-    case RSkipLinesOp:
-        if ( LONGP(inst) )
-        {
-        VAXSHORT( the_hdr->priv.get.vert_skip, infile );
+        scanc = NULL;
+    for (;;) {
+        inst[0] = getc(infile);
+        inst[1] = getc(infile);
+        if (feof(infile)) {
+            the_hdr->priv.get.is_eof = 1;
+            break;      /* <--- one of the exits */
         }
-        else
-        the_hdr->priv.get.vert_skip = DATUM(inst);
-        if (debug_f)
-        fprintf(stderr, "Skip %d Lines (to %d)\n",
-            the_hdr->priv.get.vert_skip,
-            the_hdr->priv.get.scan_y +
-                the_hdr->priv.get.vert_skip );
-
-        break;          /* need to break for() here, too */
-
-    case RSetColorOp:
-        channel = DATUM(inst);  /* select color channel */
-        if ( channel == 255 )
-        channel = -1;
-        scan_x = the_hdr->xmin;
-        if ( RLE_BIT( *the_hdr, channel ) )
-        scanc = scanline[channel]+scan_x;
-        if ( debug_f )
-        fprintf( stderr, "Set color to %d (reset x to %d)\n",
-             channel, scan_x );
-        break;
-
-    case RSkipPixelsOp:
-        if ( LONGP(inst) )
-        {
-        VAXSHORT( long_data, infile );
-        scan_x += long_data;
-        scanc += long_data;
-        if ( debug_f )
-            fprintf( stderr, "Skip %d pixels (to %d)\n",
-                long_data, scan_x );
-        }
-        else
-        {
-        scan_x += DATUM(inst);
-        scanc += DATUM(inst);
-        if ( debug_f )
-            fprintf( stderr, "Skip %d pixels (to %d)\n",
-                DATUM(inst), scan_x );
-        }
-        break;
 
-    case RByteDataOp:
-        if ( LONGP(inst) )
-        {
-        VAXSHORT( nc, infile );
-        }
-        else
-        nc = DATUM(inst);
-        nc++;
-        if ( debug_f ) {
-        if ( RLE_BIT( *the_hdr, channel ) )
-            fprintf( stderr, "Pixel data %d (to %d):", nc, scan_x+nc );
-        else
-            fprintf( stderr, "Pixel data %d (to %d)\n", nc, scan_x+nc);
-        }
-        if ( RLE_BIT( *the_hdr, channel ) )
-        {
-        /* Don't fill past end of scanline! */
-        if ( scan_x + nc > max_x )
-        {
-            ns = scan_x + nc - max_x - 1;
-            nc -= ns;
-        }
-        else
-            ns = 0;
-        fread( (char *)scanc, 1, nc, infile );
-        while ( ns-- > 0 )
-            (void)getc( infile );
-        if ( nc & 1 )
-            (void)getc( infile );   /* throw away odd byte */
-        }
-        else
-        if ( the_hdr->priv.get.is_seek )
-            fseek( infile, ((nc + 1) / 2) * 2, 1 );
-        else
-        {
-            register int ii;
-            for ( ii = ((nc + 1) / 2) * 2; ii > 0; ii-- )
-            (void) getc( infile );  /* discard it */
-        }
+        switch(OPCODE(inst)) {
+        case RSkipLinesOp:
+            if (LONGP(inst)) {
+                VAXSHORT(the_hdr->priv.get.vert_skip, infile);
+            } else
+                the_hdr->priv.get.vert_skip = DATUM(inst);
+            if (debug_f)
+                pm_message("Skip %d Lines (to %d)",
+                           the_hdr->priv.get.vert_skip,
+                           the_hdr->priv.get.scan_y +
+                           the_hdr->priv.get.vert_skip);
+
+            break;          /* need to break for() here, too */
+
+        case RSetColorOp:
+            channel = DATUM(inst);  /* select color channel */
+            if (channel == 255)
+                channel = -1;
+            scan_x = the_hdr->xmin;
+            if (RLE_BIT(*the_hdr, channel))
+                scanc = scanline[channel]+scan_x;
+            if (debug_f)
+                pm_message("Set color to %d (reset x to %d)",
+                           channel, scan_x );
+            break;
+
+        case RSkipPixelsOp:
+            if (LONGP(inst)) {
+                VAXSHORT(long_data, infile);
+                scan_x += long_data;
+                scanc += long_data;
+                if (debug_f)
+                    pm_message("Skip %d pixels (to %d)", long_data, scan_x);
+            } else {
+                scan_x += DATUM(inst);
+                scanc += DATUM(inst);
+                if (debug_f)
+                    pm_message("Skip %d pixels (to %d)", DATUM(inst), scan_x);
+            }
+            break;
+
+        case RByteDataOp:
+            if (LONGP(inst)) {
+                VAXSHORT(nc, infile);
+            } else
+                nc = DATUM(inst);
+            ++nc;
+            if (debug_f) {
+                if (RLE_BIT(*the_hdr, channel))
+                    pm_message("Pixel data %d (to %d):", nc, scan_x + nc);
+                else
+                    pm_message("Pixel data %d (to %d)", nc, scan_x + nc);
+            }
+            if (RLE_BIT(*the_hdr, channel)) {
+                /* Don't fill past end of scanline! */
+                if (scan_x + nc > max_x) {
+                    ns = scan_x + nc - max_x - 1;
+                    nc -= ns;
+                } else
+                    ns = 0;
+                fread((char *)scanc, 1, nc, infile);
+                while (ns-- > 0)
+                    getc(infile);
+                if (nc & 0x1)
+                    getc(infile);   /* throw away odd byte */
+            } else {
+                if (the_hdr->priv.get.is_seek)
+                    fseek(infile, ((nc + 1) / 2) * 2, 1);
+                else {
+                    int ii;
+                    for (ii = ((nc + 1) / 2) * 2; ii > 0; --ii)
+                        getc(infile);  /* discard it */
+                }
+            }
+            scanc += nc;
+            scan_x += nc;
+            if (debug_f && RLE_BIT(*the_hdr, channel)) {
+                rle_pixel * cp;
+                for (cp = scanc - nc; nc > 0; --nc)
+                    fprintf(stderr, "%02x", *cp++);
+                putc('\n', stderr);
+            }
+            break;
+
+        case RRunDataOp:
+            if (LONGP(inst)) {
+                VAXSHORT(nc, infile);
+            } else
+                nc = DATUM(inst);
+            ++nc;
+            scan_x += nc;
+
+            VAXSHORT(word, infile);
+            if (debug_f)
+                pm_message("Run length %d (to %d), data %02x",
+                           nc, scan_x, word);
+            if (RLE_BIT(*the_hdr, channel)) {
+                if (scan_x > max_x) {
+                    ns = scan_x - max_x - 1;
+                    nc -= ns;
+                } else
+                    ns = 0;
+                if (nc >= 10) {    /* break point for 785, anyway */
+                    memset((char *)scanc, word, nc);
+                    scanc += nc;
+                } else {
+                    for (nc--; nc >= 0; --nc, ++scanc)
+                        *scanc = word;
+                }
+            }
+            break;
 
-        scanc += nc;
-        scan_x += nc;
-        if ( debug_f && RLE_BIT( *the_hdr, channel ) )
-        {
-        rle_pixel * cp = scanc - nc;
-        for ( ; nc > 0; nc-- )
-            fprintf( stderr, "%02x", *cp++ );
-        putc( '\n', stderr );
-        }
-        break;
+        case REOFOp:
+            the_hdr->priv.get.is_eof = 1;
+            if (debug_f)
+                pm_message("End of Image");
+            break;
 
-    case RRunDataOp:
-        if ( LONGP(inst) )
-        {
-        VAXSHORT( nc, infile );
+        default:
+            pm_error("rle_getrow: Unrecognized opcode: %d, reading %s",
+                     inst[0], the_hdr->file_name);
         }
-        else
-        nc = DATUM(inst);
-        nc++;
-        scan_x += nc;
-
-        VAXSHORT( word, infile );
-        if ( debug_f )
-        fprintf( stderr, "Run length %d (to %d), data %02x\n",
-                nc, scan_x, word );
-        if ( RLE_BIT( *the_hdr, channel ) )
-        {
-        if ( scan_x > max_x )
-        {
-            ns = scan_x - max_x - 1;
-            nc -= ns;
-        } 
-        else
-            ns = 0;
-        if ( nc >= 10 )     /* break point for 785, anyway */
-        {
-            memset((char *)scanc, word, nc);
-            scanc += nc;
-        }
-        else
-            for ( nc--; nc >= 0; nc--, scanc++ )
-            *scanc = word;
-        }
-        break;
-
-    case REOFOp:
-        the_hdr->priv.get.is_eof = 1;
-        if ( debug_f )
-        fprintf( stderr, "End of Image\n" );
-        break;
-
-    default:
-        fprintf( stderr,
-             "%s: rle_getrow: Unrecognized opcode: %d, reading %s\n",
-             the_hdr->cmd, inst[0], the_hdr->file_name );
-        exit(1);
-    }
-    if ( OPCODE(inst) == RSkipLinesOp || OPCODE(inst) == REOFOp )
-        break;          /* <--- the other loop exit */
+        if (OPCODE(inst) == RSkipLinesOp || OPCODE(inst) == REOFOp)
+            break;          /* <--- the other loop exit */
     }
 
     /* If at end, skip the rest of a malformed image. */
-    if ( the_hdr->priv.get.scan_y >= the_hdr->ymax )
-    {
-    int y = the_hdr->priv.get.scan_y;
-    while ( rle_getskip( the_hdr ) != 32768 )
-        ;
-    return y;
+    if (the_hdr->priv.get.scan_y >= the_hdr->ymax) {
+        int const y = the_hdr->priv.get.scan_y;
+        while (rle_getskip(the_hdr) != 32768 )
+            ;
+        return y;
     }
 
     return the_hdr->priv.get.scan_y;
 }
+
+
+
diff --git a/urt/rle_getskip.c b/urt/rle_getskip.c
index f1c333e7..1366e162 100644
--- a/urt/rle_getskip.c
+++ b/urt/rle_getskip.c
@@ -25,6 +25,8 @@
  * Copyright (c) 1990, University of Michigan
  */
 
+#include <stdlib.h>
+
 #include "rle.h"
 #include "rle_code.h"
 
diff --git a/urt/rle_global.c b/urt/rle_global.c
index 90d3f975..6014a229 100644
--- a/urt/rle_global.c
+++ b/urt/rle_global.c
@@ -31,6 +31,8 @@
  */
 
 #include <stdio.h>
+
+#include "rle_config.h"
 #include "rle_put.h"
 #include "rle.h"
 #include "Runput.h"
diff --git a/urt/rle_hdr.c b/urt/rle_hdr.c
index 3cc0401d..1611324c 100644
--- a/urt/rle_hdr.c
+++ b/urt/rle_hdr.c
@@ -25,10 +25,11 @@
  * Copyright (c) 1991, University of Michigan
  */
 
-#include "rle.h"
-
 #include <string.h>
 
+#include "rle_config.h"
+#include "rle.h"
+
 /*****************************************************************
  * TAG( rle_names )
  *
diff --git a/urt/rle_open_f.c b/urt/rle_open_f.c
index 07dbea01..ae8548b9 100644
--- a/urt/rle_open_f.c
+++ b/urt/rle_open_f.c
@@ -15,8 +15,9 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-#include "pm_c_util.h"
-#include "nstring.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/nstring.h"
+#include "rle_config.h"
 #include "rle.h"
 
 
@@ -52,7 +53,7 @@ my_popen(const char * const cmd,
         return NULL;
     }
 
-    if ( pipe(pipefd) < 0 )
+    if (pm_pipe(pipefd) < 0 )
         return NULL;
     
     /* Flush known files. */
@@ -199,11 +200,11 @@ dealWithSubprocess(const char *  const file_name,
         *noSubprocessP = FALSE;
         
         if (*mode == 'w')
-            asprintfN(&command, "compress > %s", file_name);
+            pm_asprintf(&command, "compress > %s", file_name);
         else if (*mode == 'a')
-            asprintfN(&command, "compress >> %s", file_name);
+            pm_asprintf(&command, "compress >> %s", file_name);
         else
-            asprintfN(&command, "compress -d < %s", file_name);
+            pm_asprintf(&command, "compress -d < %s", file_name);
         
         *fpP = my_popen(command, mode, &thepid);
 
@@ -215,7 +216,7 @@ dealWithSubprocess(const char *  const file_name,
             if (*catchingChildrenP < MAX_CHILDREN)
                 pids[(*catchingChildrenP)++] = thepid;
         }
-        strfree(command);
+        pm_strfree(command);
     } else {
         *noSubprocessP = TRUE;
         *errorP = NULL;
@@ -234,7 +235,7 @@ dealWithSubprocess(const char *  const file_name,
  *  will return a pointer to stdin or stdout depending on the mode.
  *    If the user specifies a non-null file name and an I/O error occurs
  *  when trying to open the file, rle_open_f will terminate execution with
- *  an appropiate error message.
+ *  an appropriate error message.
  *
  *  parameters
  *   input:
diff --git a/urt/rle_put.h b/urt/rle_put.h
index d611b438..1f8cc85b 100644
--- a/urt/rle_put.h
+++ b/urt/rle_put.h
@@ -28,6 +28,7 @@
  */
 
 #include "rle.h"
+#include "rle_config.h"
 
 /* ****************************************************************
  * Dispatch table for different output types.
diff --git a/urt/rle_putcom.c b/urt/rle_putcom.c
index b1215661..ab2eb208 100644
--- a/urt/rle_putcom.c
+++ b/urt/rle_putcom.c
@@ -27,8 +27,8 @@
 
 #include <stdio.h>
 
-#include "mallocvar.h"
-#include "pm.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/pm.h"
 #include "rle.h"
 
 /*****************************************************************
diff --git a/urt/rle_putrow.c b/urt/rle_putrow.c
index 230720f8..399633e4 100644
--- a/urt/rle_putrow.c
+++ b/urt/rle_putrow.c
@@ -31,6 +31,7 @@
  */
  
 #include <stdio.h>
+
 #include "rle_put.h"
 #include "rle.h"
 
@@ -62,7 +63,7 @@
  * Assumptions:
  *
  * Algorithm:
- * 	Search for occurences of pixels not of the given color outside
+ * 	Search for occurrences of pixels not of the given color outside
  *	the runs already found.  When some are found, add a new run or
  *	extend an existing one.  Adjacent runs with fewer than two
  *	pixels intervening are merged.
diff --git a/urt/rle_row_alc.c b/urt/rle_row_alc.c
index 0f29523e..982e1c5e 100644
--- a/urt/rle_row_alc.c
+++ b/urt/rle_row_alc.c
@@ -28,7 +28,9 @@
  * Copyright (c) 1986, Spencer W. Thomas
  */
 
+#include <stdlib.h>
 #include <stdio.h>
+
 #include "rle.h"
 
 /*****************************************************************
diff --git a/urt/scanargs.c b/urt/scanargs.c
index b91f3e37..f3af3342 100644
--- a/urt/scanargs.c
+++ b/urt/scanargs.c
@@ -40,17 +40,14 @@
  *  to have all "void" functions so declared.
  */
 
-#include "rle.h"
 #include <stdio.h>
 #include <ctype.h>
-#ifndef USE_STDARG
-#include <varargs.h>
-#else
 #include <stdarg.h>
-#endif
 
-#include "pm_c_util.h"
-#include "nstring.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/nstring.h"
+#include "rle_config.h"
+#include "rle.h"
 
 /* 
  * An explicit assumption is made in this code that all pointers look
@@ -68,48 +65,21 @@ typedef int *ptr;
 #define NEW( type, cnt )	(type *) malloc( (cnt) * sizeof( type ) )
 #define RENEW( type, ptr, cnt )	(type *) realloc( ptr, (cnt) * sizeof( type ) )
 
-#if defined(c_plusplus) && !defined(USE_PROTOTYPES)
-#define USE_PROTOTYPES
-#endif
-
-#ifndef USE_PROTOTYPES
-static char * prformat();
-static int isnum();
-static int	_do_scanargs();
-void		scan_usage();
-#else
 static CONST_DECL char * prformat( CONST_DECL char *, int );
 static int isnum( CONST_DECL char *, int, int );
 static int	_do_scanargs( int argc, char **argv, CONST_DECL char *format,
 			      va_list argl );
 void		scan_usage( char **, CONST_DECL char * );
-#endif
 
 /* 
  * Argument list is (argc, argv, format, ... )
  */
 int
-#ifndef USE_STDARG
-scanargs ( va_alist )
-va_dcl
-#else
 scanargs ( int argc, char **argv, CONST_DECL char *format, ... )
-#endif /* !USE_STDARG */
 {
     va_list argl;
     int retval;
-#ifndef USE_STDARG
-    int argc;
-    char ** argv;
-    CONST_DECL char *format;
-
-    va_start( argl );
-    argc = va_arg( argl, int );
-    argv = va_arg( argl, char ** );
-    format = va_arg( argl, CONST_DECL char * );
-#else
     va_start( argl, format );
-#endif
     retval = _do_scanargs( argc, argv, format, argl );
     va_end( argl );
     return retval;
diff --git a/version.mk b/version.mk
index 40f9ce62..2dbfb7ff 100644
--- a/version.mk
+++ b/version.mk
@@ -1,3 +1,3 @@
 NETPBM_MAJOR_RELEASE = 10
-NETPBM_MINOR_RELEASE = 47
-NETPBM_POINT_RELEASE = 73
+NETPBM_MINOR_RELEASE = 73
+NETPBM_POINT_RELEASE = 28
diff --git a/vms/Add_List.com b/vms/Add_List.com
deleted file mode 100755
index 830cfc96..00000000
--- a/vms/Add_List.com
+++ /dev/null
@@ -1,59 +0,0 @@
-$ VERIFY = F$Verify (0)
-$!
-$!     ADD_LIST.COM command procedure
-$!         Usage:
-$!             ADD_LIST library file_spec [logical_name_table]
-$!
-$!     Last Modified: 18-JAN-1991 Rick Dyson
-$!
-$!     Escape routes
-$ On Control_Y Then GoTo FINISH
-$ On Error     Then GoTo FINISH
-$ On Warning   Then GoTo FINISH
-$ On Severe    Then GoTo FINISH
-$!
-$!     We're out'a here if the calling parameter is null
-$ P2 = F$Edit (P2, "TRIM, UPCASE")
-$ If P2 .eqs. "" Then GoTo FINISH
-$!
-$!     Check logical name table argument and default if necessary.
-$!
-$ TABLE = F$Edit (P3, "UNCOMMENT, UPCASE, TRIM")
-$ If (TABLE .eqs. "PROCESS")
-$    Then
-$    Else If (TABLE .eqs. "GROUP")
-$            Then
-$            Else If (TABLE .eqs. "JOB")
-$                    Then
-$                    Else If (TABLE .eqs. "SYSTEM")
-$                            Then
-$                            Else
-$                                TABLE = "Process"
-$                         EndIf
-$                 EndIf
-$         EndIf
-$ EndIf
-$!
-$! Check the first value in the library list
-$ LIB = P1
-$ X = F$TrnLnm (LIB, "LNM$''TABLE'")
-$ If X .eqs. "" Then GoTo INSERT
-$ If X .eqs. P2 Then GoTo FINISH
-$!
-$! Find the first free logical to assign the library file to
-$ BASE = P1 + "_"
-$ N = 1
-$NEXTLIB:
-$   LIB := 'BASE''N'
-$   X = F$TrnLnm (LIB, "LNM$''TABLE'")
-$   If X .eqs. "" Then GoTo INSERT
-$   If X .eqs. P2 Then GoTo FINISH
-$   N = N + 1
-$   GoTo NEXTLIB
-$!
-$! Add the library file to the library file list
-$INSERT:
-$   Define /'TABLE' 'LIB' 'P2'
-$FINISH:
-$   VERIFY = F$Verify (VERIFY)
-$   Exit
diff --git a/vms/Make_PBMplus.com b/vms/Make_PBMplus.com
deleted file mode 100755
index d3243d96..00000000
--- a/vms/Make_PBMplus.com
+++ /dev/null
@@ -1,519 +0,0 @@
-$ If F$Mode () .eqs. "INTERACTIVE"
-$    Then
-$        VERIFY = F$Verify (0)
-$    Else
-$        VERIFY = F$Verify (1)
-$ EndIf
-$ THIS_PATH = F$Element (0, "]", F$Environment ("PROCEDURE")) + "]"
-$ Set Default 'THIS_PATH'
-$!
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("DEFAULT")) + ".]"
-$ Define /NoLog /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ Define /NoLog PBMplus_Dir PBMplus_Root:[000000]
-$ Define /NoLog PBMplusShr PBMplus_Dir:PBMplusShr
-$ @ PBMplus_Dir:STAMP-DATE.COM
-$ Purge /NoLog /NoConfirm COMPILE.H
-$!
-$!            Make the Shareable Library
-$!
-$ Set Default PBMplus_Root:[pbm]
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("DEFAULT")) - ".PBM" + ".]"
-$ If F$TrnLnm ("PBMplus_Root") .eqs. "" Then 	Define /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ If F$TrnLnm ("PBMplus_Dir") .eqs. "" Then 	Define PBMplus_Dir PBMplus_Root:[000000]
-$ If F$TrnLnm ("Sys") .eqs. "" Then Define Sys Sys$Library
-$ CC /NOLIST/OBJECT=LIBPBM1.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] LIBPBM1.C
-$ CC /NOLIST/OBJECT=LIBPBM2.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] LIBPBM2.C
-$ CC /NOLIST/OBJECT=LIBPBM3.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] LIBPBM3.C
-$ CC /NOLIST/OBJECT=LIBPBM4.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] LIBPBM4.C
-$ CC /NOLIST/OBJECT=LIBPBM5.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] LIBPBM5.C
-$ Library /Create libpbm.olb libpbm%.obj
-$ Set Default PBMplus_Root:[pgm]
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("DEFAULT")) - ".PGM" + ".]"
-$ If F$TrnLnm ("PBMplus_Root") .eqs. "" Then 	Define /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ If F$TrnLnm ("PBMplus_Dir") .eqs. "" Then 	Define PBMplus_Dir PBMplus_Root:[000000]
-$ If F$TrnLnm ("Sys") .eqs. "" Then Define Sys Sys$Library
-$ CC /NOLIST/OBJECT=LIBPGM1.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) LIBPGM1.C
-$ CC /NOLIST/OBJECT=LIBPGM2.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) LIBPGM2.C
-$ Library /Create libpgm.olb libpgm%.obj
-$ Set Default PBMplus_Root:[ppm]
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("DEFAULT")) - ".PPM" + ".]"
-$ If F$TrnLnm ("PBMplus_Root") .eqs. "" Then 	Define /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ If F$TrnLnm ("PBMplus_Dir") .eqs. "" Then 	Define PBMplus_Dir PBMplus_Root:[000000]
-$ If F$TrnLnm ("Sys") .eqs. "" Then Define Sys Sys$Library
-$ CC /NOLIST/OBJECT=LIBPPM1.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) LIBPPM1.C
-$ CC /NOLIST/OBJECT=LIBPPM2.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) LIBPPM2.C
-$ CC /NOLIST/OBJECT=LIBPPM3.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) LIBPPM3.C
-$ CC /NOLIST/OBJECT=LIBPPM4.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) LIBPPM4.C
-$ CC /NOLIST/OBJECT=LIBPPM5.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) LIBPPM5.C
-$ CC /NOLIST/OBJECT=BITIO.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) BITIO.C
-$ Library /Create libppm.olb libppm%.obj,bitio.obj
-$ Set Default PBMplus_Root:[pnm]
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("DEFAULT")) - ".PNM" + ".]"
-$ If F$TrnLnm ("PBMplus_Root") .eqs. "" Then 	Define /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ If F$TrnLnm ("PBMplus_Dir") .eqs. "" Then 	Define PBMplus_Dir PBMplus_Root:[000000]
-$ If F$TrnLnm ("Sys") .eqs. "" Then Define Sys Sys$Library
-$ CC /NOLIST/OBJECT=LIBPNM1.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) LIBPNM1.C
-$ CC /NOLIST/OBJECT=LIBPNM2.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) LIBPNM2.C
-$ CC /NOLIST/OBJECT=LIBPNM3.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) LIBPNM3.C
-$ CC /NOLIST/OBJECT=LIBPNM4.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) LIBPNM4.C
-$ Library /Create libpnm.olb libpnm%.obj
-$ Set Default PBMplus_Dir
-$ @ PBMplus_Dir:MAKE_PBMplusShr.COM
-$!
-$!		PBM
-$!
-$ Set Default PBMplus_Root:[pbm]
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("DEFAULT")) - ".PBM" + ".]"
-$ If F$TrnLnm ("PBMplus_Root") .eqs. "" Then 	Define /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ If F$TrnLnm ("PBMplus_Dir") .eqs. "" Then 	Define PBMplus_Dir PBMplus_Root:[000000]
-$ If F$TrnLnm ("Sys") .eqs. "" Then Define Sys Sys$Library
-$ CC /NOLIST/OBJECT=ATKTOPBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] ATKTOPBM.C
-$ LINK /TRACE/NOMAP/EXEC=ATKTOPBM.EXE ATKTOPBM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=BRUSHTOPBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] BRUSHTOPBM.C
-$ LINK /TRACE/NOMAP/EXEC=BRUSHTOPBM.EXE BRUSHTOPBM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=CMUWMTOPBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] CMUWMTOPBM.C
-$ LINK /TRACE/NOMAP/EXEC=CMUWMTOPBM.EXE CMUWMTOPBM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=G3TOPBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] G3TOPBM.C
-$ LINK /TRACE/NOMAP/EXEC=G3TOPBM.EXE G3TOPBM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=GEMTOPBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] GEMTOPBM.C
-$ LINK /TRACE/NOMAP/EXEC=GEMTOPBM.EXE GEMTOPBM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=ICONTOPBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] ICONTOPBM.C
-$ LINK /TRACE/NOMAP/EXEC=ICONTOPBM.EXE ICONTOPBM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=MACPTOPBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] MACPTOPBM.C
-$ LINK /TRACE/NOMAP/EXEC=MACPTOPBM.EXE MACPTOPBM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=MGRTOPBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] MGRTOPBM.C
-$ LINK /TRACE/NOMAP/EXEC=MGRTOPBM.EXE MGRTOPBM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMLIFE.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMLIFE.C
-$ LINK /TRACE/NOMAP/EXEC=PBMLIFE.EXE PBMLIFE.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMMAKE.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMMAKE.C
-$ LINK /TRACE/NOMAP/EXEC=PBMMAKE.EXE PBMMAKE.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMMASK.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMMASK.C
-$ LINK /TRACE/NOMAP/EXEC=PBMMASK.EXE PBMMASK.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMREDUCE.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMREDUCE.C
-$ LINK /TRACE/NOMAP/EXEC=PBMREDUCE.EXE PBMREDUCE.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTEXT.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTEXT.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTEXT.EXE PBMTEXT.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTO10X.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTO10X.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTO10X.EXE PBMTO10X.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTO4425.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTO4425.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTO4425.EXE PBMTO4425.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOASCII.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOASCII.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOASCII.EXE PBMTOASCII.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOATK.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOATK.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOATK.EXE PBMTOATK.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOBBNBG.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOBBNBG.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOBBNBG.EXE PBMTOBBNBG.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOCMUWM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOCMUWM.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOCMUWM.EXE PBMTOCMUWM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOEPSON.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOEPSON.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOEPSON.EXE PBMTOEPSON.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOG3.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOG3.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOG3.EXE PBMTOG3.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOGEM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOGEM.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOGEM.EXE PBMTOGEM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOGO.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOGO.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOGO.EXE PBMTOGO.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOICON.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOICON.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOICON.EXE PBMTOICON.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOLJ.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOLJ.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOLJ.EXE PBMTOLJ.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOMACP.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOMACP.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOMACP.EXE PBMTOMACP.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOMGR.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOMGR.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOMGR.EXE PBMTOMGR.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOPI3.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOPI3.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOPI3.EXE PBMTOPI3.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOPLOT.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOPLOT.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOPLOT.EXE PBMTOPLOT.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOPTX.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOPTX.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOPTX.EXE PBMTOPTX.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOX10BM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOX10BM.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOX10BM.EXE PBMTOX10BM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOXBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOXBM.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOXBM.EXE PBMTOXBM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOYBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOYBM.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOYBM.EXE PBMTOYBM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOZINC.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOZINC.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOZINC.EXE PBMTOZINC.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMUPC.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMUPC.C
-$ LINK /TRACE/NOMAP/EXEC=PBMUPC.EXE PBMUPC.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PI3TOPBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PI3TOPBM.C
-$ LINK /TRACE/NOMAP/EXEC=PI3TOPBM.EXE PI3TOPBM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=XBMTOPBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] XBMTOPBM.C
-$ LINK /TRACE/NOMAP/EXEC=XBMTOPBM.EXE XBMTOPBM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=YBMTOPBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] YBMTOPBM.C
-$ LINK /TRACE/NOMAP/EXEC=YBMTOPBM.EXE YBMTOPBM.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOLN03.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOLN03.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOLN03.EXE PBMTOLN03.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMCLEAN.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMCLEAN.C
-$ LINK /TRACE/NOMAP/EXEC=PBMCLEAN.EXE PBMCLEAN.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMPSCALE.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMPSCALE.C
-$ LINK /TRACE/NOMAP/EXEC=PBMPSCALE.EXE PBMPSCALE.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOEPSI.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOEPSI.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOEPSI.EXE PBMTOEPSI.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOLPS.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOLPS.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOLPS.EXE PBMTOLPS.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PBMTOPK.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PBMTOPK.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOPK.EXE PBMTOPK.obj,PBMplusSHR/Option
-$ CC /NOLIST/OBJECT=PKTOPBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = [-] PKTOPBM.C
-$ LINK /TRACE/NOMAP/EXEC=PKTOPBM.EXE PKTOPBM.obj,PBMplusSHR/Option
-$ Set Protection = (System:RWE, Owner:RWE, Group:RE, World:RE) *.exe
-$!
-$!		PGM
-$!
-$ Set Default PBMplus_Root:[pgm]
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("DEFAULT")) - ".PGM" + ".]"
-$ If F$TrnLnm ("PBMplus_Root") .eqs. "" Then 	Define /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ If F$TrnLnm ("PBMplus_Dir") .eqs. "" Then 	Define PBMplus_Dir PBMplus_Root:[000000]
-$ If F$TrnLnm ("Sys") .eqs. "" Then Define Sys Sys$Library
-$ CC /NOLIST/OBJECT=ASCIITOPGM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) ASCIITOPGM.C
-$ LINK /TRACE/NOMAP/EXEC=ASCIITOPGM.EXE ASCIITOPGM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=FSTOPGM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) FSTOPGM.C
-$ LINK /TRACE/NOMAP/EXEC=FSTOPGM.EXE FSTOPGM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=HIPSTOPGM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) HIPSTOPGM.C
-$ LINK /TRACE/NOMAP/EXEC=HIPSTOPGM.EXE HIPSTOPGM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=LISPMTOPGM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) LISPMTOPGM.C
-$ LINK /TRACE/NOMAP/EXEC=LISPMTOPGM.EXE LISPMTOPGM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMBENTLEY.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMBENTLEY.C
-$ LINK /TRACE/NOMAP/EXEC=PGMBENTLEY.EXE PGMBENTLEY.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMCRATER.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMCRATER.C
-$ LINK /TRACE/NOMAP/EXEC=PGMCRATER.EXE PGMCRATER.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMEDGE.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMEDGE.C
-$ LINK /TRACE/NOMAP/EXEC=PGMEDGE.EXE PGMEDGE.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMENHANCE.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMENHANCE.C
-$ LINK /TRACE/NOMAP/EXEC=PGMENHANCE.EXE PGMENHANCE.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMHIST.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMHIST.C
-$ LINK /TRACE/NOMAP/EXEC=PGMHIST.EXE PGMHIST.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMKERNEL.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMKERNEL.C
-$ LINK /TRACE/NOMAP/EXEC=PGMKERNEL.EXE PGMKERNEL.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMNOISE.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMNOISE.C
-$ LINK /TRACE/NOMAP/EXEC=PGMNOISE.EXE PGMNOISE.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMNORM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMNORM.C
-$ LINK /TRACE/NOMAP/EXEC=PGMNORM.EXE PGMNORM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMOIL.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMOIL.C
-$ LINK /TRACE/NOMAP/EXEC=PGMOIL.EXE PGMOIL.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMRAMP.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMRAMP.C
-$ LINK /TRACE/NOMAP/EXEC=PGMRAMP.EXE PGMRAMP.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMTOFS.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMTOFS.C
-$ LINK /TRACE/NOMAP/EXEC=PGMTOFS.EXE PGMTOFS.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMTOLISPM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMTOLISPM.C
-$ LINK /TRACE/NOMAP/EXEC=PGMTOLISPM.EXE PGMTOLISPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMTOPBM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMTOPBM.C
-$ LINK /TRACE/NOMAP/EXEC=PGMTOPBM.EXE PGMTOPBM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PSIDTOPGM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PSIDTOPGM.C
-$ LINK /TRACE/NOMAP/EXEC=PSIDTOPGM.EXE PSIDTOPGM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=RAWTOPGM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) RAWTOPGM.C
-$ LINK /TRACE/NOMAP/EXEC=RAWTOPGM.EXE RAWTOPGM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMTEXTURE.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PGMTEXTURE.C
-$ LINK /TRACE/NOMAP/EXEC=PGMTEXTURE.EXE PGMTEXTURE.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=BIORADTOPGM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) BIORADTOPGM.C
-$ LINK /TRACE/NOMAP/EXEC=BIORADTOPGM.EXE BIORADTOPGM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PBMTOPGM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) PBMTOPGM.C
-$ LINK /TRACE/NOMAP/EXEC=PBMTOPGM.EXE PBMTOPGM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=SPOTTOPGM.OBJ /Define = (PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-.pbm],[-]) SPOTTOPGM.C
-$ LINK /TRACE/NOMAP/EXEC=SPOTTOPGM.EXE SPOTTOPGM.obj,[-]PBMplusSHR.OPT/Option
-$ Set Protection = (System:RWE, Owner:RWE, Group:RE, World:RE) *.exe
-$!
-$!		PPM
-$!
-$ Set Default PBMplus_Root:[ppm]
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("DEFAULT")) - ".PPM" + ".]"
-$ If F$TrnLnm ("PBMplus_Root") .eqs. "" Then 	Define /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ If F$TrnLnm ("PBMplus_Dir") .eqs. "" Then 	Define PBMplus_Dir PBMplus_Root:[000000]
-$ If F$TrnLnm ("Sys") .eqs. "" Then Define Sys Sys$Library
-$ CC /NOLIST/OBJECT=GOULDTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) GOULDTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=GOULDTOPPM.EXE GOULDTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=HPCDTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) HPCDTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=HPCDTOPPM.EXE HPCDTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=ILBMTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) ILBMTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=ILBMTOPPM.EXE ILBMTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=IMGTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) IMGTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=IMGTOPPM.EXE IMGTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=MTVTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) MTVTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=MTVTOPPM.EXE MTVTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PCXTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PCXTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=PCXTOPPM.EXE PCXTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PGMTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PGMTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=PGMTOPPM.EXE PGMTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PI1TOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PI1TOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=PI1TOPPM.EXE PI1TOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PICTTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PICTTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=PICTTOPPM.EXE PICTTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PJTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PJTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=PJTOPPM.EXE PJTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPM3D.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPM3D.C
-$ LINK /TRACE/NOMAP/EXEC=PPM3D.EXE PPM3D.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMCHANGE.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMCHANGE.C
-$ LINK /TRACE/NOMAP/EXEC=PPMCHANGE.EXE PPMCHANGE.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMDIM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMDIM.C
-$ LINK /TRACE/NOMAP/EXEC=PPMDIM.EXE PPMDIM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMDITHER.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMDITHER.C
-$ LINK /TRACE/NOMAP/EXEC=PPMDITHER.EXE PPMDITHER.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMFLASH.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMFLASH.C
-$ LINK /TRACE/NOMAP/EXEC=PPMFLASH.EXE PPMFLASH.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMFORGE.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMFORGE.C
-$ LINK /TRACE/NOMAP/EXEC=PPMFORGE.EXE PPMFORGE.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMHIST.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMHIST.C
-$ LINK /TRACE/NOMAP/EXEC=PPMHIST.EXE PPMHIST.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMMAKE.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMMAKE.C
-$ LINK /TRACE/NOMAP/EXEC=PPMMAKE.EXE PPMMAKE.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMMIX.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMMIX.C
-$ LINK /TRACE/NOMAP/EXEC=PPMMIX.EXE PPMMIX.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMQUANT.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMQUANT.C
-$ LINK /TRACE/NOMAP/EXEC=PPMQUANT.EXE PPMQUANT.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMRELIEF.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMRELIEF.C
-$ LINK /TRACE/NOMAP/EXEC=PPMRELIEF.EXE PPMRELIEF.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMSHIFT.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMSHIFT.C
-$ LINK /TRACE/NOMAP/EXEC=PPMSHIFT.EXE PPMSHIFT.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMSPREAD.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMSPREAD.C
-$ LINK /TRACE/NOMAP/EXEC=PPMSPREAD.EXE PPMSPREAD.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOACAD.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOACAD.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOACAD.EXE PPMTOACAD.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOGIF.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOGIF.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOGIF.EXE PPMTOGIF.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOICR.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOICR.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOICR.EXE PPMTOICR.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOILBM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOILBM.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOILBM.EXE PPMTOILBM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOMITSU.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOMITSU.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOMITSU.EXE PPMTOMITSU.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOPCX.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOPCX.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOPCX.EXE PPMTOPCX.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOPGM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOPGM.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOPGM.EXE PPMTOPGM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOPI1.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOPI1.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOPI1.EXE PPMTOPI1.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOPICT.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOPICT.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOPICT.EXE PPMTOPICT.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOPJ.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOPJ.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOPJ.EXE PPMTOPJ.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOPUZZ.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOPUZZ.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOPUZZ.EXE PPMTOPUZZ.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTORGB3.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTORGB3.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTORGB3.EXE PPMTORGB3.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOSIXEL.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOSIXEL.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOSIXEL.EXE PPMTOSIXEL.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOTGA.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOTGA.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOTGA.EXE PPMTOTGA.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOUIL.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOUIL.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOUIL.EXE PPMTOUIL.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOXPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOXPM.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOXPM.EXE PPMTOXPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOYUV.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOYUV.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOYUV.EXE PPMTOYUV.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=QRTTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) QRTTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=QRTTOPPM.EXE QRTTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=RAWTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) RAWTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=RAWTOPPM.EXE RAWTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=RGB3TOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) RGB3TOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=RGB3TOPPM.EXE RGB3TOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=SLDTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) SLDTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=SLDTOPPM.EXE SLDTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=SPCTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) SPCTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=SPCTOPPM.EXE SPCTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=SPUTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) SPUTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=SPUTOPPM.EXE SPUTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=TGATOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) TGATOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=TGATOPPM.EXE TGATOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=XIMTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) XIMTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=XIMTOPPM.EXE XIMTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=XPMTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) XPMTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=XPMTOPPM.EXE XPMTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=XVMINITOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) XVMINITOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=XVMINITOPPM.EXE XVMINITOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=YUVTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) YUVTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=YUVTOPPM.EXE YUVTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=BMPTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) BMPTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=BMPTOPPM.EXE BMPTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMBRIGHTEN.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMBRIGHTEN.C
-$ LINK /TRACE/NOMAP/EXEC=PPMBRIGHTEN.EXE PPMBRIGHTEN.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMDIST.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMDIST.C
-$ LINK /TRACE/NOMAP/EXEC=PPMDIST.EXE PPMDIST.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMQVGA.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMQVGA.C
-$ LINK /TRACE/NOMAP/EXEC=PPMQVGA.EXE PPMQVGA.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOBMP.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOBMP.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOBMP.EXE PPMTOBMP.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOMAP.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOMAP.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOMAP.EXE PPMTOMAP.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOPJXL.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOPJXL.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOPJXL.EXE PPMTOPJXL.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMTOYUVSPLIT.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMTOYUVSPLIT.C
-$ LINK /TRACE/NOMAP/EXEC=PPMTOYUVSPLIT.EXE PPMTOYUVSPLIT.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=YUVSPLITTOPPM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) YUVSPLITTOPPM.C
-$ LINK /TRACE/NOMAP/EXEC=YUVSPLITTOPPM.EXE YUVSPLITTOPPM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PPMPAT.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.pgm],[-.pbm]) PPMPAT.C
-$ LINK /TRACE/NOMAP/EXEC=PPMPAT.EXE PPMPAT.obj,[-]PBMplusSHR.OPT/Option
-$ Set Protection = (System:RWE, Owner:RWE, Group:RE, World:RE) *.exe
-$ Set Default PBMplus_Root:[libtiff]
-$ Define /NoLog Sys Sys$Library
-$ CC /NOLIST/OBJECT=MKG3STATES.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize mkg3states.c
-$ Define /User_Mode LNK$Library Sys$Library:VAXCRTL
-$ LINK /TRACE/NOMAP/EXEC=MKG3STATES.EXE mkg3states
-$ Define /User_Mode Sys$Output g3states.h
-$ Run mkg3states.exe
-$ CC /NOLIST/OBJECT=TIF_FAX3.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_FAX3.C
-$ CC /NOLIST/OBJECT=TIF_FAX4.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_FAX4.C
-$ CC /NOLIST/OBJECT=TIF_AUX.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_AUX.C
-$ CC /NOLIST/OBJECT=TIF_CCITTRLE.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_CCITTRLE.C
-$ CC /NOLIST/OBJECT=TIF_CLOSE.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_CLOSE.C
-$ CC /NOLIST/OBJECT=TIF_COMPRESS.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_COMPRESS.C
-$ CC /NOLIST/OBJECT=TIF_DIR.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_DIR.C
-$ CC /NOLIST/OBJECT=TIF_DIRINFO.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_DIRINFO.C
-$ CC /NOLIST/OBJECT=TIF_DIRREAD.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_DIRREAD.C
-$ CC /NOLIST/OBJECT=TIF_DIRWRITE.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_DIRWRITE.C
-$ CC /NOLIST/OBJECT=TIF_DUMPMODE.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_DUMPMODE.C
-$ CC /NOLIST/OBJECT=TIF_ERROR.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_ERROR.C
-$ CC /NOLIST/OBJECT=TIF_GETIMAGE.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_GETIMAGE.C
-$ CC /NOLIST/OBJECT=TIF_JPEG.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_JPEG.C
-$ CC /NOLIST/OBJECT=TIF_FLUSH.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_FLUSH.C
-$ CC /NOLIST/OBJECT=TIF_LZW.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_LZW.C
-$ CC /NOLIST/OBJECT=TIF_MACHDEP.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_MACHDEP.C
-$ CC /NOLIST/OBJECT=TIF_NEXT.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_NEXT.C
-$ CC /NOLIST/OBJECT=TIF_OPEN.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_OPEN.C
-$ CC /NOLIST/OBJECT=TIF_PACKBITS.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_PACKBITS.C
-$ CC /NOLIST/OBJECT=TIF_PRINT.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_PRINT.C
-$ CC /NOLIST/OBJECT=TIF_READ.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_READ.C
-$ CC /NOLIST/OBJECT=TIF_STRIP.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_STRIP.C
-$ CC /NOLIST/OBJECT=TIF_SWAB.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_SWAB.C
-$ CC /NOLIST/OBJECT=TIF_THUNDER.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_THUNDER.C
-$ CC /NOLIST/OBJECT=TIF_TILE.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_TILE.C
-$ CC /NOLIST/OBJECT=TIF_VERSION.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_VERSION.C
-$ CC /NOLIST/OBJECT=TIF_VMS.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_VMS.C
-$ CC /NOLIST/OBJECT=TIF_WARNING.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_WARNING.C
-$ CC /NOLIST/OBJECT=TIF_WRITE.OBJ /Include_Directory = ([]) /Define = ("BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /NoDebug /Optimize TIF_WRITE.C
-$ If "''F$Search ("libtiff.olb")'" .eqs. "" Then Library /Create libtiff.olb
-$ Delete /NoConfirm /NoLog mkg3states.obj;
-$ Library /Replace libtiff.olb *.obj
-$!
-$!		PNM
-$!
-$ Set Default PBMplus_Root:[pnm]
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("DEFAULT")) - ".PNM" + ".]"
-$ If F$TrnLnm ("PBMplus_Root") .eqs. "" Then 	Define /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ If F$TrnLnm ("PBMplus_Dir") .eqs. "" Then 	Define PBMplus_Dir PBMplus_Root:[000000]
-$ If F$TrnLnm ("Sys") .eqs. "" Then Define Sys Sys$Library
-$ CC /NOLIST/OBJECT=PNMALIAS.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMALIAS.C
-$ LINK /TRACE/NOMAP/EXEC=PNMALIAS.EXE PNMALIAS.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMARITH.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMARITH.C
-$ LINK /TRACE/NOMAP/EXEC=PNMARITH.EXE PNMARITH.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMCAT.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMCAT.C
-$ LINK /TRACE/NOMAP/EXEC=PNMCAT.EXE PNMCAT.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMCONVOL.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMCONVOL.C
-$ LINK /TRACE/NOMAP/EXEC=PNMCONVOL.EXE PNMCONVOL.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMCROP.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMCROP.C
-$ LINK /TRACE/NOMAP/EXEC=PNMCROP.EXE PNMCROP.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMCUT.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMCUT.C
-$ LINK /TRACE/NOMAP/EXEC=PNMCUT.EXE PNMCUT.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMDEPTH.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMDEPTH.C
-$ LINK /TRACE/NOMAP/EXEC=PNMDEPTH.EXE PNMDEPTH.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMENLARGE.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMENLARGE.C
-$ LINK /TRACE/NOMAP/EXEC=PNMENLARGE.EXE PNMENLARGE.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMFILE.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMFILE.C
-$ LINK /TRACE/NOMAP/EXEC=PNMFILE.EXE PNMFILE.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMFLIP.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMFLIP.C
-$ LINK /TRACE/NOMAP/EXEC=PNMFLIP.EXE PNMFLIP.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMHISTMAP.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMHISTMAP.C
-$ LINK /TRACE/NOMAP/EXEC=PNMHISTMAP.EXE PNMHISTMAP.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMINVERT.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMINVERT.C
-$ LINK /TRACE/NOMAP/EXEC=PNMINVERT.EXE PNMINVERT.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMNORAW.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMNORAW.C
-$ LINK /TRACE/NOMAP/EXEC=PNMNORAW.EXE PNMNORAW.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMNTSC.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMNTSC.C
-$ LINK /TRACE/NOMAP/EXEC=PNMNORAW.EXE PNMNORAW.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMNTSC.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMNTSC.C
-$ LINK /TRACE/NOMAP/EXEC=PNMNTSC.EXE PNMNTSC.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMPASTE.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMPASTE.C
-$ LINK /TRACE/NOMAP/EXEC=PNMPASTE.EXE PNMPASTE.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMSCALE.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMSCALE.C
-$ LINK /TRACE/NOMAP/EXEC=PNMSCALE.EXE PNMSCALE.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMTILE.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMTILE.C
-$ LINK /TRACE/NOMAP/EXEC=PNMTILE.EXE PNMTILE.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMTODDIF.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMTODDIF.C
-$ LINK /TRACE/NOMAP/EXEC=PNMTODDIF.EXE PNMTODDIF.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMTOFITS.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMTOFITS.C
-$ LINK /TRACE/NOMAP/EXEC=PNMTOFITS.EXE PNMTOFITS.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=FITSTOPNM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) FITSTOPNM.C
-$ LINK /TRACE/NOMAP/EXEC=FITSTOPNM.EXE FITSTOPNM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMTOPS.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMTOPS.C
-$ LINK /TRACE/NOMAP/EXEC=PNMTOPS.EXE PNMTOPS.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMTORAST.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMTORAST.C
-$ LINK /TRACE/NOMAP/EXEC=PNMTORAST.EXE PNMTORAST.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMTOXWD.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMTOXWD.C
-$ LINK /TRACE/NOMAP/EXEC=PNMTOXWD.EXE PNMTOXWD.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=RASTTOPNM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) RASTTOPNM.C
-$ LINK /TRACE/NOMAP/EXEC=RASTTOPNM.EXE RASTTOPNM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=XWDTOPNM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) XWDTOPNM.C
-$ LINK /TRACE/NOMAP/EXEC=XWDTOPNM.EXE XWDTOPNM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=GIFTOPNM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) GIFTOPNM.C
-$ LINK /TRACE/NOMAP/EXEC=GIFTOPNM.EXE GIFTOPNM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMCOMP.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMCOMP.C
-$ LINK /TRACE/NOMAP/EXEC=PNMCOMP.EXE PNMCOMP.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMTOSGI.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMTOSGI.C
-$ LINK /TRACE/NOMAP/EXEC=PNMTOSGI.EXE PNMTOSGI.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=SGITOPNM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) SGITOPNM.C
-$ LINK /TRACE/NOMAP/EXEC=SGITOPNM.EXE SGITOPNM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMTOSIR.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMTOSIR.C
-$ LINK /TRACE/NOMAP/EXEC=PNMTOSIR.EXE PNMTOSIR.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=SIRTOPNM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) SIRTOPNM.C
-$ LINK /TRACE/NOMAP/EXEC=SIRTOPNM.EXE SIRTOPNM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMNLFILT.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMNLFILT.C
-$ LINK /TRACE/NOMAP/EXEC=PNMNLFILT.EXE PNMNLFILT.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMPAD.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMPAD.C
-$ LINK /TRACE/NOMAP/EXEC=PNMPAD.EXE PNMPAD.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=ZEISSTOPNM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) ZEISSTOPNM.C
-$ LINK /TRACE/NOMAP/EXEC=ZEISSTOPNM.EXE ZEISSTOPNM.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMGAMMA.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMGAMMA.C
-$ LINK /TRACE/NOMAP/EXEC=PNMGAMMA.EXE PNMGAMMA.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMROTATE.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMROTATE.C
-$ LINK /TRACE/NOMAP/EXEC=PNMROTATE.EXE PNMROTATE.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMSHEAR.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) PNMSHEAR.C
-$ LINK /TRACE/NOMAP/EXEC=PNMSHEAR.EXE PNMSHEAR.obj,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=TIFFTOPNM.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF,"BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) tifftopnm.c
-$ LINK /TRACE/NOMAP/EXEC=TIFFTOPNM.EXE tifftopnm,[-.libtiff]libtiff.olb/Library,[-]PBMplusSHR.OPT/Option
-$ CC /NOLIST/OBJECT=PNMTOTIFF.OBJ /Define = (PPM,PGM,PBM,PBMPLUS_RAWBITS,LIBTIFF,"BSDTYPES=1","USE_VARARGS=1","USE_PROTOTYPES=0","USE_CONST",__STDC__) /Include_Directory = ([-],[-.ppm],[-.pgm],[-.pbm],[-.libtiff]) pnmtotiff.c
-$ LINK /TRACE/NOMAP/EXEC=PNMTOTIFF.EXE pnmtotiff,[-.libtiff]libtiff.olb/Library,[-]PBMplusSHR.OPT/Option
-$ Set Protection = (System:RWE, Owner:RWE, Group:RE, World:RE) *.exe
-$!
-$!		Install the binaries in a separate directory
-$!
-$ Set Default PBMplus_Dir
-$ If F$Parse ("PBMplus_Root:[Exe]") .eqs. "" Then Create /Directory PBMplus_Root:[Exe]
-$ Set Default PBMplus_Root:[PBM]
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("DEFAULT")) - ".PBM" + ".]"
-$ If F$TrnLnm ("PBMplus_Root") .eqs. "" Then 	Define /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ If F$TrnLnm ("PBMplus_Dir") .eqs. "" Then 	Define PBMplus_Dir PBMplus_Root:[000000]
-$ If F$TrnLnm ("Sys") .eqs. "" Then Define Sys Sys$Library
-$ Set Protection = (System:RWE, Owner:RWED, Group:RE, World:RE) *.exe
-$ Rename /Log *.exe PBMplus_Root:[Exe]
-$ Set Protection = Owner:RWE PBMplus_Root:[Exe]*.exe
-$ Set Default PBMplus_Root:[PGM]
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("DEFAULT")) - ".PGM" + ".]"
-$ If F$TrnLnm ("PBMplus_Root") .eqs. "" Then 	Define /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ If F$TrnLnm ("PBMplus_Dir") .eqs. "" Then 	Define PBMplus_Dir PBMplus_Root:[000000]
-$ If F$TrnLnm ("Sys") .eqs. "" Then Define Sys Sys$Library
-$ Set Protection = (System:RWE, Owner:RWED, Group:RE, World:RE) *.exe
-$ Rename /Log *.exe PBMplus_Root:[Exe]
-$ Set Protection = Owner:RWE PBMplus_Root:[Exe]*.exe
-$ Set Default PBMplus_Root:[PPM]
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("DEFAULT")) - ".PPM" + ".]"
-$ If F$TrnLnm ("PBMplus_Root") .eqs. "" Then 	Define /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ If F$TrnLnm ("PBMplus_Dir") .eqs. "" Then 	Define PBMplus_Dir PBMplus_Root:[000000]
-$ If F$TrnLnm ("Sys") .eqs. "" Then Define Sys Sys$Library
-$ Set Protection = (System:RWE, Owner:RWED, Group:RE, World:RE) *.exe
-$ Rename /Log *.exe PBMplus_Root:[Exe]
-$ Set Protection = Owner:RWE PBMplus_Root:[Exe]*.exe
-$ Set Default PBMplus_Root:[PNM]
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("DEFAULT")) - ".PNM" + ".]"
-$ If F$TrnLnm ("PBMplus_Root") .eqs. "" Then 	Define /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ If F$TrnLnm ("PBMplus_Dir") .eqs. "" Then 	Define PBMplus_Dir PBMplus_Root:[000000]
-$ If F$TrnLnm ("Sys") .eqs. "" Then Define Sys Sys$Library
-$ Set Protection = (System:RWE, Owner:RWED, Group:RE, World:RE) *.exe
-$ Rename /Log *.exe PBMplus_Root:[Exe]
-$ Set Protection = Owner:RWE PBMplus_Root:[Exe]*.exe
-$!
-$!		Build the VMS Help Library
-$!
-$ Set Default PBMplus_Dir
-$ Library /Create /Help PBMPLUS.HLB PBMPLUS.HLP
-$ Set File /Truncate PBMPLUS.HLB
-$ Exit
diff --git a/vms/Make_PBMplusShr.com b/vms/Make_PBMplusShr.com
deleted file mode 100755
index 8e43efb9..00000000
--- a/vms/Make_PBMplusShr.com
+++ /dev/null
@@ -1,280 +0,0 @@
-$!
-$! Make shareable image out of PBMPLUS libraries.  This command procedure
-$! takes no arguments, but must be placed in the top-level PBMPLUS directory.
-$!
-$! It uses the following input library files:
-$!
-$!	[.PBM]LIBPBM.OLB,[.PGM]LIBPGM.OLB,[.PPM]LIBPPM.OLB,[.PNM]LIBPNM.OLB
-$!
-$! This procedure generates the following files if missing or out-of-date:
-$!
-$!	TRANSVEC.OBJ	Object file containing transfer vector for PBMPLUSSHR.
-$!	PBMPLUSSHR.EXE	Shareable image file for PBM libraries.
-$!	PBMPLUSSHR.OPT	Linker options file for linking utility program against
-$!			the PBMPLUSSHR.EXE shareable image.
-$!
-$ instruct = 0
-$ proc = f$environment("PROCEDURE")
-$ proc_cdt = f$cvtime(f$file(proc,"CDT"))
-$ if f$search("TRANSVEC.OBJ") .EQS. "" THEN GOTO NEW_TRANSVEC
-$ if f$cvtime(f$file("TRANSVEC.OBJ","CDT")) .GTS. PROC_CDT THEN GOTO TRANSVEC_DONE
-$ NEW_TRANSVEC:
-$ instruct = 1
-$ Write SYS$Output "Making new transvec.obj..."
-$ Macro /NoList /Object = TRANSVEC.OBJ Sys$Input
-; PMBPLUS_TRANSFER_VECTOR
-; This routine defines a transfer vector for use in creating shareable image
-;
-; define macro to make transfer vector entry for a given routine.  Entry mask 
-; is obtained from routine we are transfering to.  Jump to word past entry 
-; since these are VAX procedures (written in FORTRAN).
-;
-	.MACRO TRANSFER_ENTRY routine
-;
-	.TRANSFER routine
-	.MASK	  routine
-	JMP	  routine + 2
-;
-	.ENDM TRANSFER_ENTRY
-;
-	.TITLE PBMPLUS_TRANSFER_VECTOR
-	.IDENT /01/
-	.PSECT PBMPLUS_XVEC PIC,USR,CON,REL,LCL,SHR,EXE,RD,NOWRT,NOVEC
-;
-;	Simply go through iap procedures and declare transfer vector
-;	entry points for them.  New procedure must be added to the END
-;	of this list.
-;
-TRANSFER_VECTOR:
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;Library LIBPBM
-;Module ARGPROC
-	TRANSFER_ENTRY BACKGROUND_PROCESS 
-	TRANSFER_ENTRY GETOPT
-	TRANSFER_ENTRY GETREDIRECTION
-	TRANSFER_ENTRY SET_OUTFILE_BINARY
-
-;Module LIBPBM1
-	TRANSFER_ENTRY PBM_INIT
-	TRANSFER_ENTRY PM_ALLOCARRAY
-	TRANSFER_ENTRY PM_ALLOCROW
-	TRANSFER_ENTRY PM_BITSTOMAXVAL
-	TRANSFER_ENTRY PM_CLOSE
-	TRANSFER_ENTRY PM_ERROR                         
-	TRANSFER_ENTRY PM_FREEARRAY                     
-	TRANSFER_ENTRY PM_FREEROW
-	TRANSFER_ENTRY PM_INIT                          
-	TRANSFER_ENTRY PM_KEYMATCH                      
-	TRANSFER_ENTRY PM_MAXVALTOBITS                  
-	TRANSFER_ENTRY PM_MESSAGE
-	TRANSFER_ENTRY PM_OPENR                         
-	TRANSFER_ENTRY PM_OPENW                         
-	TRANSFER_ENTRY PM_PERROR                        
-	TRANSFER_ENTRY PM_READBIGLONG
-	TRANSFER_ENTRY PM_READBIGSHORT                  
-	TRANSFER_ENTRY PM_READLITTLELONG                
-	TRANSFER_ENTRY PM_READLITTLESHORT               
-	TRANSFER_ENTRY PM_USAGE
-	TRANSFER_ENTRY PM_WRITEBIGLONG                  
-	TRANSFER_ENTRY PM_WRITEBIGSHORT                 
-	TRANSFER_ENTRY PM_WRITELITTLELONG               
-	TRANSFER_ENTRY PM_WRITELITTLESHORT
-	TRANSFER_ENTRY PM_READ_UNKNOWN_SIZE
-
-;Module LIBPBM2
-	TRANSFER_ENTRY PBM_READMAGICNUMBER              
-	TRANSFER_ENTRY PBM_READPBM                      
-	TRANSFER_ENTRY PBM_READPBMINIT                  
-	TRANSFER_ENTRY PBM_READPBMINITREST
-	TRANSFER_ENTRY PBM_READPBMROW
-
-;Module LIBPBM3
-	TRANSFER_ENTRY PBM_WRITEPBM                     
-	TRANSFER_ENTRY PBM_WRITEPBMINIT                 
-	TRANSFER_ENTRY PBM_WRITEPBMROW
-
-;Module LIBPBM4
-	TRANSFER_ENTRY PBM_GETC                         
-	TRANSFER_ENTRY PBM_GETINT                       
-	TRANSFER_ENTRY PBM_GETRAWBYTE
-
-;Module LIBPBM5
-	TRANSFER_ENTRY PBM_DEFAULTFONT                  
-	TRANSFER_ENTRY PBM_DISSECTFONT                  
-	TRANSFER_ENTRY PBM_DUMPFONT
-	TRANSFER_ENTRY PBM_LOADFONT
-	TRANSFER_ENTRY PBM_LOADBDFFONT
-	TRANSFER_ENTRY MK_ARGVN
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;Library LIBPGM
-;Module LIBPGM1
-	TRANSFER_ENTRY PGM_INIT                         
-	TRANSFER_ENTRY PGM_READPGM                      
-	TRANSFER_ENTRY PGM_READPGMINIT                  
-	TRANSFER_ENTRY PGM_READPGMINITREST
-	TRANSFER_ENTRY PGM_READPGMROW
-
-;Module LIBPGM2
-	TRANSFER_ENTRY PGM_WRITEPGM                     
-	TRANSFER_ENTRY PGM_WRITEPGMINIT                 
-	TRANSFER_ENTRY PGM_WRITEPGMROW
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Library LIBPPM
-;
-;Module LIBPPM1
-	TRANSFER_ENTRY PPM_INIT                         
-	TRANSFER_ENTRY PPM_READPPM                      
-	TRANSFER_ENTRY PPM_READPPMINIT                  
-	TRANSFER_ENTRY PPM_READPPMINITREST
-	TRANSFER_ENTRY PPM_READPPMROW
-
-;Module LIBPPM2
-	TRANSFER_ENTRY PPM_WRITEPPM                     
-	TRANSFER_ENTRY PPM_WRITEPPMINIT                 
-	TRANSFER_ENTRY PPM_WRITEPPMROW
-
-;Module LIBPPM3
-	TRANSFER_ENTRY PPM_ADDTOCOLORHASH               
-	TRANSFER_ENTRY PPM_ADDTOCOLORHIST               
-	TRANSFER_ENTRY PPM_ALLOCCOLORHASH               
-	TRANSFER_ENTRY PPM_COLORHASHTOCOLORHIST
-	TRANSFER_ENTRY PPM_COLORHISTTOCOLORHASH         
-	TRANSFER_ENTRY PPM_COMPUTECOLORHASH             
-	TRANSFER_ENTRY PPM_COMPUTECOLORHIST             
-	TRANSFER_ENTRY PPM_FREECOLORHASH
-	TRANSFER_ENTRY PPM_FREECOLORHIST                
-	TRANSFER_ENTRY PPM_LOOKUPCOLOR
-
-;Module LIBPPM4
-	TRANSFER_ENTRY PPM_COLORNAME                    
-	TRANSFER_ENTRY PPM_PARSECOLOR
-
-;Module LIBPPM5
-	TRANSFER_ENTRY PPMD_CIRCLE                      
-	TRANSFER_ENTRY PPMD_FILL                        
-	TRANSFER_ENTRY PPMD_FILLEDRECTANGLE             
-	TRANSFER_ENTRY PPMD_FILL_DRAWPROC
-	TRANSFER_ENTRY PPMD_FILL_INIT                   
-	TRANSFER_ENTRY PPMD_LINE                        
-	TRANSFER_ENTRY PPMD_POINT_DRAWPROC              
-	TRANSFER_ENTRY PPMD_POLYSPLINE
-	TRANSFER_ENTRY PPMD_SETLINECLIP                 
-	TRANSFER_ENTRY PPMD_SETLINETYPE                 
-	TRANSFER_ENTRY PPMD_SPLINE3
-
-;Module BITIO
-	TRANSFER_ENTRY PM_BITINIT
-	TRANSFER_ENTRY PM_BITFINI
-	TRANSFER_ENTRY PM_BITREAD
-	TRANSFER_ENTRY PM_BITWRITE
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-; Library LIBPNM
-;Module LIBPNM1
-	TRANSFER_ENTRY PNM_INIT                         
-	TRANSFER_ENTRY PNM_READPNM                      
-	TRANSFER_ENTRY PNM_READPNMINIT                  
-	TRANSFER_ENTRY PNM_READPNMROW
-
-;Module LIBPNM2
-	TRANSFER_ENTRY PNM_WRITEPNM                     
-	TRANSFER_ENTRY PNM_WRITEPNMINIT                 
-	TRANSFER_ENTRY PNM_WRITEPNMROW
-
-;Module LIBPNM3
-	TRANSFER_ENTRY PNM_BACKGROUNDXEL                
-	TRANSFER_ENTRY PNM_BACKGROUNDXELROW             
-	TRANSFER_ENTRY PNM_BLACKXEL                     
-	TRANSFER_ENTRY PNM_INVERTXEL
-	TRANSFER_ENTRY PNM_PROMOTEFORMAT                
-	TRANSFER_ENTRY PNM_PROMOTEFORMATROW             
-	TRANSFER_ENTRY PNM_WHITEXEL
-
-;Module LIBPNM4
-	TRANSFER_ENTRY MEM_CREATE                       
-	TRANSFER_ENTRY MEM_FREE                         
-	TRANSFER_ENTRY PR_DUMP                          
-	TRANSFER_ENTRY PR_LOAD_COLORMAP
-	TRANSFER_ENTRY PR_LOAD_HEADER                   
-	TRANSFER_ENTRY PR_LOAD_IMAGE
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;	allocate extra space to allow for code modifications without changing 
-;	the size of the shared image.
-;
-	.BLKB 2048-<.-TRANSFER_VECTOR>	; Reserve 4 pages.
-;
-	.END
-
-$ TRANSVEC_DONE:
-$!
-$!   Create new options file if needed.
-$!
-$ if f$search("PBMPLUSSHR.OPT") .EQS. "" THEN GOTO NEW_OPTFILE
-$ if f$cvtime(f$file("PBMPLUSSHR.OPT","CDT")) .GTS. PROC_CDT THEN GOTO OPTFILE_DONE
-$ NEW_OPTFILE:
-$ instruct = 1
-$ write sys$output "Making new pbmplusshr.opt..."
-$ CREATE PBMPLUSSHR.OPT
-PBMplusShr /Share
-Sys$Share:VAXCRTL /Share
-PSECT_ATTR = ARGPROC_VERSION,GBL,NOSHR
-PSECT_ATTR = OPTARG,GBL,NOSHR
-PSECT_ATTR = OPTERR,GBL,NOSHR
-PSECT_ATTR = OPTIND,GBL,NOSHR
-PSECT_ATTR = OPTOPT,GBL,NOSHR
-PSECT_ATTR = PGM_PBMMAXVAL,GBL,NOSHR
-PSECT_ATTR = PNM_PBMMAXVAL,GBL,NOSHR
-PSECT_ATTR = PPM_PBMMAXVAL,GBL,NOSHR
-$!
-$ OPTFILE_DONE:
-$!
-$ if f$search("PBMPLUSSHR.EXE") .EQS. "" THEN GOTO NEW_SHAREABLE
-$ EXE_CDT = f$cvtime(f$file_attributes("PBMPLUSSHR.EXE","CDT"))
-$ if EXE_CDT .LTS. PROC_CDT THEN GOTO NEW_SHAREABLE
-$ if f$cvtime(f$file("[.PBM]LIBPBM.OLB","RDT")) .GTS. EXE_CDT THEN GOTO NEW_SHAREABLE
-$ if f$cvtime(f$file("[.PGM]LIBPGM.OLB","RDT")) .GTS. EXE_CDT THEN GOTO NEW_SHAREABLE
-$ if f$cvtime(f$file("[.PPM]LIBPPM.OLB","RDT")) .GTS. EXE_CDT THEN GOTO NEW_SHAREABLE
-$ if f$cvtime(f$file("[.PNM]LIBPNM.OLB","RDT")) .GTS. EXE_CDT THEN GOTO NEW_SHAREABLE
-$ GOTO SHAREABLE_DONE
-$ NEW_SHAREABLE:
-$ instruct = 1
-$ write sys$output "Making new pbmplusshr.exe..."
-$ Link /Map = PBMPLUSHSR.MAP /Share = SYS$DISK:[]PBMPLUSSHR.EXE Sys$Input/Option
-COLLECT=FIRST,PBMPLUS_XVEC
-COLLECT=GLOBALS1,PGM_PBMMAXVAL,PNM_PBMMAXVAL,PPM_PBMMAXVAL
-COLLECT=GLOBALS2,ARGPROC_VERSION,OPTARG,OPTERR,OPTIND,OPTOPT
-
-TRANSVEC.OBJ
-[.PBM]LIBPBM/LIB,[.PGM]LIBPGM/LIB,[.PPM]LIBPPM/LIB
-[.PNM]LIBPNM/LIB,SYS$SHARE:VAXCRTL/SHARE
-
-
-UNSUPPORTED = 1			! force demand zero pages
-GSMATCH=LEQUAL,2,1		! Major ID = 2, minor ID = 2
-
-PSECT_ATTR = ARGPROC_VERSION,NOSHR
-PSECT_ATTR = OPTARG,NOSHR
-PSECT_ATTR = OPTERR,NOSHR
-PSECT_ATTR = OPTIND,NOSHR
-PSECT_ATTR = OPTOPT,NOSHR
-PSECT_ATTR = PGM_PBMMAXVAL,NOSHR
-PSECT_ATTR = PNM_PBMMAXVAL,NOSHR
-PSECT_ATTR = PPM_PBMMAXVAL,NOSHR
-!PSECT_ATTR = ,LCL,NOSHR
-$!
-$ SHAREABLE_DONE:
-$ if .NOT. instruct then write sys$output "All PBMPLUSSHR files up to date."
-$ if .NOT. instruct then exit $status
-$ create sys$output
-
-	Define the logical name PBMPLUSSHR as "disk:[dir]PBMPLUSSHR", where
-	disk and [dir] are the disk and directory containing the
-	shareable image PBMPLUSSHR.EXE and linker options file PBMPLUSSHR.OPT.
-
-	You can then link an executable against the image with the command
-
-	    LINK program.OBJ,PBMplusShr/Option
-
-$ exit $status
diff --git a/vms/NetPBM.TeX b/vms/NetPBM.TeX
deleted file mode 100644
index 86a9abc8..00000000
--- a/vms/NetPBM.TeX
+++ /dev/null
@@ -1,12115 +0,0 @@
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex (version 1.04, July 15, 1991)
-% on Thu Dec 12 09:12:27 1991
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-
-\documentstyle[troffman,twoside]{article}
-\begin{document}
-%
-% input file: pbmplus.1
-%
-\phead{PBMPLUS}{1L}{13 October 1993}{}{}
-
-\shead{NAME}
-pbmplus - enhanced portable bitmap toolkit
-
-\shead{DESCRIPTION}
-The {\it pbmplus} toolkit allows conversions between image files of
-different format.  By means of using common intermediate formats, only
-$2 \times N$ conversion filters are required to support $N$ distinct formats,
-instead of the $N^2$ which would be required to convert directly between
-any one format and any other.  The package also includes simple tools for
-manipulating portable bitmaps.
-
-The package consists of four upwardly compatible sections:
-\begin{TPlist}{pbm}
-\item[{pbm}]
-Supports monochrome bitmaps (1 bit per pixel).
-\item[{pgm}]
-Supports grayscale images.  Reads either {\it pbm} or {\it pgm} formats
-and writes {\it pgm} format.
-\item[{ppm}]
-Supports full-color images.  Reads either {\it pbm}, {\it pgm}, or {\it
-ppm} formats, writes {\it ppm} format.
-\item[{pnm}]
-Supports content-independent manipulations on any of the three formats
-listed above, as well as external formats having multiple types.  Reads
-either {\it pbm}, {\it pgm}, or {\it ppm} formats, and generally writes
-the same type as it read (whenever a {\it pnm} tool makes an exception
-and ``promotes'' a file to a higher format, it informs the user).
-\end{TPlist}
-
-\shead{DESCRIPTION OF CONTENTS}
-\begin{TPlist}{{\it cmuwmtopbm}}
-\item[{{\it atktopbm}}]
-convert Andrew Toolkit raster object to portable bitmap
-\item[{{\it brushtopbm}}]
-convert Xerox doodle brushes to portable bitmap
-\item[{{\it cmuwmtopbm}}]
-convert CMU window manager format to portable bitmap
-\item[{{\it g3topbm}}]
-convert Group 3 FAX to portable bitmap
-\item[{{\it icontopbm}}]
-convert Sun icon to portable bitmap
-\item[{{\it gemtopbm}}]
-convert GEM .img format to portable bitmap
-\item[{{\it macptopbm}}]
-convert MacPaint to portable bitmap
-\item[{{\it mgrtopbm}}]
-convert MGR format to portable bitmap
-\item[{{\it pbmmerge}}]
-merge wrapper routine
-\item[{{\it pbmto10x}}]
-convert portable bitmap to Gemini 10x printer graphics
-\item[{{\it pbmtoascii}}]
-convert portable bitmap to ASCII graphic form
-\item[{{\it pbmtoatk}}]
-convert portable bitmap to Andrew Toolkit raster object
-\item[{{\it pbmtobbnbg}}]
-convert portable bitmap to BBN BitGraph graphics
-\item[{{\it pbmtocmuwm}}]
-convert portable bitmap to CMU window manager format
-\item[{{\it pbmtoepson}}]
-convert portable bitmap to Epson printer graphics
-\item[{{\it pbmtog3}}]
-convert portable bitmap to Group 3 FAX
-\item[{{\it pbmtogem}}]
-convert portable bitmap into GEM .img file
-\item[{{\it pbmtogo}}]
-convert portable bitmap to GraphOn graphics
-\item[{{\it pbmtoicon}}]
-convert portable bitmap to Sun icon
-\item[{{\it pbmtolj}}]
-convert portable bitmap to HP LaserJet graphics
-\item[{{\it pbmtomacp}}]
-convert portable bitmap to MacPaint
-\item[{{\it pbmtomgr}}]
-convert portable bitmap to MGR format
-\item[{{\it pbmtopi3}}]
-convert portable bitmap to Atari Degas .pi3
-\item[{{\it pbmtoplot}}]
-convert portable bitmap into Unix plot(5) file
-\item[{{\it pbmtoptx}}]
-convert portable bitmap to Printronix graphics
-\item[{{\it pbmtoxbm}}]
-convert portable bitmap to X11 bitmap
-\item[{{\it pbmtox10bm}}]
-convert portable bitmap to X10 bitmap
-\item[{{\it pbmtoybm}}]
-convert portable bitmap into Bennet Yee ``face'' file
-\item[{{\it pbmtozinc}}]
-convert portable bitmap to Zinc Interface Library icon
-\item[{{\it pbmlife}}]
-apply Conway's rules of Life to a portable bitmap
-\item[{{\it pbmmake}}]
-create a blank bitmap of a specified size and color
-\item[{{\it pbmmask}}]
-create a mask bitmap from a regular bitmap
-\item[{{\it pbmreduce}}]
-reduce a portable bitmap N times, using Floyd-Steinberg
-\item[{{\it pbmtext}}]
-render text into a bitmap
-\item[{{\it pbmupc}}]
-create a Universal Product Code bitmap
-\item[{{\it pi3topbm}}]
-convert Atari Degas .pi3 to portable bitmap
-\item[{{\it xbmtopbm}}]
-convert X10 or X11 bitmap to portable bitmap
-\item[{{\it ybmtopbm}}]
-convert Bennet Yee ``face'' file into portable bitmap
-\item[{{\it asciitopgm}}]
-convert ASCII graphics into a portable graymap
-\item[{{\it fstopgm}}]
-convert Usenix FaceSaver$^{\rm tm}$ format to portable graymap
-\item[{{\it hipstopgm}}]
-convert HIPS format to portable graymap
-\item[{{\it lispmtopgm}}]
-convert a Lisp Machine bitmap file into pgm format
-\item[{{\it pgmbentley}}]
-Bentleyize a portable graymap
-\item[{{\it pgmcrater}}]
-create cratered terrain by fractal forgery
-\item[{{\it pgmedge}}]
-edge-detect a portable graymap
-\item[{{\it pgmenhance}}]
-edge-enhance a portable graymap
-\item[{{\it pgmhist}}]
-print a histogram of the values in a portable graymap
-\item[{{\it pgmkernel}}]
-generate a convolution kernel
-\item[{{\it pgmmerge}}]
-merge wrapper routine
-\item[{{\it pgmnoise}}]
-create a graymap made up of white noise
-\item[{{\it pgmnorm}}]
-normalize contrast in a portable graymap
-\item[{{\it pgmoil}}]
-turn a portable graymap into an oil painting
-\item[{{\it pgmramp}}]
-generate a grayscale ramp
-\item[{{\it pgmtexture}}]
-calculate textural features on a portable graymap
-\item[{{\it pgmtofits}}]
-convert portable graymap to FITS format
-\item[{{\it pgmtofs}}]
-convert portable graymap to Usenix FaceSaver$^{\rm tm}$ format
-\item[{{\it pgmtolispm}}]
-convert a portable graymap into Lisp Machine format
-\item[{{\it pgmtopbm}}]
-convert portable graymap to portable bitmap
-\item[{{\it psidtopgm}}]
-convert PostScript ``image'' data to portable graymap
-\item[{{\it rawtopgm}}]
-convert raw grayscale bytes to portable graymap
-\item[{{\it giftoppm}}]
-convert GIF to portable pixmap
-\item[{{\it gouldtoppm}}]
-convert Gould scanner file to portable pixmap
-\item[{{\it ilbmtoppm}}]
-convert IFF ILBM to portable pixmap
-\item[{{\it imgtoppm}}]
-convert Img-whatnot to portable pixmap
-\item[{{\it mtvtoppm}}]
-convert MTV ray-tracer output to portable pixmap
-\item[{{\it pcxtoppm}}]
-convert PC Paintbrush format to portable pixmap
-\item[{{\it pgmtoppm}}]
-colorize a portable graymap into a portable pixmap
-\item[{{\it pi1toppm}}]
-convert Atari Degas .pi1 to portable pixmap
-\item[{{\it picttoppm}}]
-convert Macintosh PICT to portable pixmap
-\item[{{\it pjtoppm}}]
-convert HP PaintJet file to portable pixmap
-\item[{{\it ppmchange}}]
-change all pixels of one color to another in a portable pixmap
-\item[{{\it ppmdim}}]
-dim a portable pixmap down to total blackness
-\item[{{\it ppmdist}}]
-simplistic grayscale assignment for machine generated, color images
-\item[{{\it ppmdither}}]
-ordered dither for color images
-\item[{{\it ppmflash}}]
-brighten a picture up to complete white-out
-\item[{{\it ppmforge}}]
-fractal forgeries of clouds, planets, and starry skies
-\item[{{\it ppmhist}}]
-print a histogram of a portable pixmap
-\item[{{\it ppmmake}}]
-create a pixmap of a specified size and color
-\item[{{\it ppmmerge}}]
-merge wrapper routine
-\item[{{\it ppmmix}}]
-blend together two portable pixmaps
-\item[{{\it ppmpat}}]
-create a pretty pixmap
-\item[{{\it ppmquant}}]
-quantize colors down to a specified number
-\item[{{\it ppmquantall}}]
-script to run ppmquant on a set of pixmaps
-\item[{{\it ppmrelief}}]
-run a Laplacian Relief filter on a portable pixmap
-\item[{{\it ppmshift}}]
-shift lines of a portable pixmap left or right by a random amount
-\item[{{\it ppmspread}}]
-displace a portable pixmap's pixels by a random amount
-\item[{{\it ppmtoacad}}]
-convert portable pixmap to AutoCAD database or slide
-\item[{{\it ppmtoicr}}]
-convert portable pixmap to NCSA ICR graphics
-\item[{{\it ppmtoilbm}}]
-convert portable pixmap to IFF ILBM
-\item[{{\it ppmtomitsu}}]
-convert a portable pixmap to a Mitsubishi S340-10 file
-\item[{{\it ppmtopcx}}]
-convert portable pixmap to PC Paintbrush format
-\item[{{\it ppmtopgm}}]
-convert portable pixmap to portable graymap
-\item[{{\it ppmtopi1}}]
-convert portable pixmap to Atari Degas .pi1
-\item[{{\it ppmtopict}}]
-convert portable pixmap to Macintosh PICT
-\item[{{\it ppmtopj}}]
-convert portable pixmap to HP PaintJet file
-\item[{{\it ppmtopuzz}}]
-convert portable pixmap to X11 ``puzzle'' file
-\item[{{\it ppmtorgb3}}]
-separate a portable pixmap into three portable graymaps
-\item[{{\it ppmtosixel}}]
-convert portable pixmap to DEC sixel format
-\item[{{\it ppmtotga}}]
-convert portable pixmap to TrueVision Targa file
-\item[{{\it ppmtouil}}]
-convert portable pixmap to Motif UIL icon file
-\item[{{\it ppmtoxpm}}]
-convert portable pixmap to XPM format
-\item[{{\it ppmtoyuv}}]
-convert portable pixmap to Abekas YUV format
-\item[{{\it qrttoppm}}]
-convert QRT ray-tracer output to portable pixmap
-\item[{{\it rawtoppm}}]
-convert raw RGB bytes to portable pixmap
-\item[{{\it rgb3toppm}}]
-combine three portable graymaps into one portable pixmap
-\item[{{\it sldtoppm}}]
-convert an AutoCAD slide file into a portable pixmap
-\item[{{\it spctoppm}}]
-convert Atari compressed Spectrum to portable pixmap
-\item[{{\it sputoppm}}]
-convert Atari uncompressed Spectrum to portable pixmap
-\item[{{\it tgatoppm}}]
-convert TrueVision Targa file to portable pixmap
-\item[{{\it ximtoppm}}]
-convert Xim to portable pixmap
-\item[{{\it xvminitoppm}}]
-convert a XV ``thumbnail'' picture to PPM
-\item[{{\it xpmtoppm}}]
-convert XPM format to portable pixmap
-\item[{{\it yuvtoppm}}]
-convert Abekas YUV format to portable pixmap
-\item[{{\it anytopnm}}]
-script to attempt to convert any format to P?M
-\item[{{\it pnmalias}}]
-antialias a portable anyumap.
-\item[{{\it pnmarith}}]
-perform arithmetic on two portable anymaps
-\item[{{\it pnmcat}}]
-concatenate portable anymaps
-\item[{{\it pnmconvol}}]
-general MxN convolution on a portable anymap
-\item[{{\it pnmcrop}}]
-crop all like-colored borders off a portable anymap
-\item[{{\it pnmcut}}]
-select a rectangular region from a portable anymap
-\item[{{\it pnmdepth}}]
-change the maxval in a portable anymap
-\item[{{\it pnmenlarge}}]
-enlarge a portable anymap N times
-\item[{{\it pnmfile}}]
-describe a portable anymap
-\item[{{\it pnmflip}}]
-perform one or more flip operations on a portable anymap
-\item[{{\it pnmgamma}}]
-perform gamma correction on a portable anymap
-\item[{{\it pnmindex}}]
-script to build a visual index of a bunch of anymaps
-\item[{{\it pnminvert}}]
-invert a portable anymap
-\item[{{\it pnmmargin}}]
-script to add a margin to a portable anymap
-\item[{{\it pnmmerge}}]
-merge wrapper routine
-\item[{{\it pnmnoraw}}]
-force a portable anymap into ASCII format
-\item[{{\it pnmpaste}}]
-paste a rectangle into a portable anymap
-\item[{{\it pnmrotate}}]
-rotate a portable anymap
-\item[{{\it pnmscale}}]
-scale a portable anymap
-\item[{{\it pnmshear}}]
-shear a portable anymap
-\item[{{\it pnmsmooth}}]
-script that uses pnmconvol to smooth a anymap
-\item[{{\it pnmtile}}]
-replicate a portable anymap into a specified size
-\item[{{\it pnmtofits}}]
-convert a portable anymap into FITS format
-\item[{{\it pnmtogif}}]
-convert portable anymap to GIF
-\item[{{\it pnmtops}}]
-convert portable anymap to PostScript
-\item[{{\it pnmtorast}}]
-convert portable anymap to Sun raster file
-\item[{{\it pnmtotiff}}]
-convert portable anymap to TIFF file
-\item[{{\it pnmtoxwd}}]
-convert portable anymap to X11 window dump
-\item[{{\it fitstopnm}}]
-fitstopnm - convert a FITS file into a portable anymap
-\item[{{\it pstopnm}}]
-script to convert PS to portable anymap with GhostScript
-\item[{{\it rasttopnm}}]
-convert Sun raster file to portable anymap
-\item[{{\it tifftopnm}}]
-convert TIFF file to portable anymap
-\item[{{\it xwdtopnm}}]
-convert X10 or X11 window dump to portable anymap
-\end{TPlist}
-
-\shead{SEE ALSO}
-There are a number of related image-manipulation tools:
-\begin{TPlist}{{\it Fuzzy Pixmap Manipulation}}
-\item[{{\it IM Raster Toolkit}}]
-A portable and efficient format toolkit.  The format supports pixels of
-arbitrary channels, components, and bit precisions, while allowing
-compression and machine byte-order independence.  Support for image
-manipulation, digital halftoning, and format conversion.  Previously
-distributed on tape c/o the University of Waterloo (an {\it ftp}
-version is to appear later).  Author: Alan Paeth
-(awpaeth@watcgl.uwaterloo.ca).
-\item[{{\it Utah RLE Toolkit}}]
-Conversion and manipulation package, similar to {\it pbmplus}.  Available
-via {\it ftp} as {\it cs.utah.edu: pub/toolkit-2.0.tar.Z} and {\it
-ucsd.edu: graphics\-/utah-raster-toolkit.\-tar.Z}.
-\item[{{\it Fuzzy Pixmap Manipulation}}]
-Conversion and manipulation package, similar to {\it pbmplus}.  Version
-1.0 available via {\it ftp} as {\it
-nl.cs.cmu.edu: /usr/mlm/ftp/fbm.tar.Z}, {\it
-uunet.uu.net: pub/fbm.tar.Z}, and {\it ucsd.edu: graphics/fbm.tar.Z}.
-Author: Michael Mauldin (mlm@nl.cs.cmu.edu).
-\item[{{\it Img Software Set}}]
-Reads and writes its own image format, displaying results on an X11
-screen, and does some image manipulations.  Version 1.3 is available via
-{\it ftp} as {\it ftp.x.org: contrib/img\_1.3.tar.Z}, and {\it
-venera.isi.edu: pub/img\_1.3.tar.Z}, along with a large collection of
-color images.  Author: Paul Raveling (raveling@venera.isi.edu).
-\item[{{\it Xim}}]
-Reads and writes its own image format, displays on an X11 screen, and
-does some image manipulations.  Available in your nearest X11R4 source
-tree as {\it contrib/clients/xim}.  A more recent version is available
-via ftp from {\it video.mit.edu}.  It uses X11R4 and the OSF/Motif
-toolkit to provide basic interactive image manipulation and reads/writes
-GIF, xwd, xbm, tiff, rle, xim, and other formats.  Author: Philip R.
-Thompson.
-\item[{{\it xloadimage}}]
-Reads in images in various formats and displays them on an X11 screen.
-Available via {\it ftp} as {\it
-ftp.x.org: contrib/xloadimage$\ast$}, and in your nearest {\it
-comp.sources.\-x} archive.  Author: Jim Frost (madd@std.\-com).
-\item[{{\it TIFF\ Software}}]
-Nice portable library for reading and writing TIFF files, plus a few
-tools for manipulating them and reading other formats.  Available via
-{\it ftp} as {\it sgi.com: graphics/tiff/$\ast$.tar.Z} or {\it
-uunet.uu.net: graphics/tiff.tar.Z}.  Author: Sam Leffler
-(sam@sgi.com).
-\item[{{\it ALV}}]
-A Sun-specific image toolkit.  Version 2.0.6 posted to {\it
-comp.sources.\-sun} on 11 December 1989.  Also available via email to {\it
-alv-users-request @cs.bris.ac.uk}.
-\item[{{\it popi}}]
-An image manipulation language.  Version 2.1 posted to {\it
-comp.\-sources.\-misc} on 12 December 1989.
-\item[{{\it ImageMagick,}}]
-X11 package for display and interactive manipulation of images.  Uses its
-own format (MIFF), and includes some converters.  Available via {\it
-ftp} as {\it ftp.x.org: contrib/ImageMagick.$\ast$.tar.Z}.
-\item[{{\it Khoros}}]
-A huge (\~{}100 meg) graphical development environment based on X11R4.
-Components include a visual programming language, code generators for
-extending the visual language and adding new application packages to the
-system, an interactive user interface editor, an interactive image
-display package, an extensive library of image and signal processing
-routines, and 2D/3D plotting packages.  Available via {\it ftp} as
-{\it pprg.unm.edu: pub/khoros/$\ast$}.
-\item[{JPEG package}]
-JPEG is a a standardized compression method for full-color and gray-scale
-images of ``real-world'' scenes; this experimental package includes
-programs to compress gif and ppm format files to JPEG format ({\it
-cjpeg(1L)),} and to decompress them ({\it djpeg(1L)).} Available by {\it
-ftp} as {\it uunet.uu.net: graphics/jpeg/jpegsrc.v4.tar.Z}.
-\end{TPlist}
-
-libpbm(3), libpgm(3), libpnm(3), libppm(3), pbm(5), pgm(5), pnm(5),
-ppm(5), rasterfile(1)
-
-\shead{AUTHOR}
-Distribution of 10 December 1991.  \copyright 1989, 1991 by Jef Poskanzer.
-
-Feedback and questions are welcome. Please send them to:
-\begin{IPlist}
-\IPitem{{}}
-{\nofill
-        jef@netcom.com
-        jef@well.sf.ca.us
-        apple!well!jef
-\fill}
-\end{IPlist}
-
-When sending bug reports, always include the output from running any
-pbmplus program with the -version flag, including descriptions of the
-type of system you are on, the compiler you use, and whether you are
-using Makefiles or Imakefiles.
-
-When suggesting new formats or features, please include whatever
-documentation you have, and a uuencoded sample.  The response time will
-depend upon my schedule and the complexity of the task;  if you need it
-right away, or it is a complicated job, you might consider paying me.
-
-The Usenet newsgroup {\it alt.graphics.pixutils} is a forum for
-discussion of image conversion and editing packages.  Posting queries
-there may be better than mailing them to me, since it allows other people
-to help provide answers.
-
-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.  Thus, you may do what you want with this software.
-Build it into your package, steal code from it, whatever.  Just be sure
-to let people know where it came from.
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Feb  4 14:35:11 1994
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: asciitopgm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: asciitopgm.1
-%
-\phead{asciitopgm}{1}{26 December 1994}{}{}
-
-%.IX asciitopgm
-\shead{NAME}
-asciitopgm - convert ASCII graphics into a portable graymap
-\shead{SYNOPSIS}
-{\bf asciitopgm}
-{\it [}{\rm -d}
-{\rm divisor}{\it ]}
-{\it height width}
-{\rm [}{\it asciifile}{\rm ]}
-\shead{DESCRIPTION}
-Reads ASCII data as input.
-Produces a portable graymap with pixel values which are an approximation
-of the ``brightness'' of the ASCII characters,
-assuming black-on-white printing.
-In other words, a capital M is very dark, a period is ver light,
-and a space is white.
-Input lines which are fewer than
-{\it width}
-characters are automatically padded with spaces.
-\par
-The
-{\it divisor}
-argument is a floating-point number by which the output pixels are
-divided; the default value is 1.0.
-This can be used to adjust the brightness of the graymap:
-for example, if the image is too dim, reduce the divisor.
-\par
-In keeping with (I believe) Fortran line-printer conventions,
-input lines beginning with a + (plus) character are assumed
-to ``overstrike'' the previous line, allowing a larger range of gray values.
-\par
-This tool contradicts the message in the
-{\it pbmtoascii}
-manual: ``Note that there is no asciitopbm tool - this
-transformation is one-way.''
-\shead{BUGS}
-The table of ASCII-to-grey values is subject to interpretation,
-and, of course, depends on the typeface intended for the input.
-\shead{SEE ALSO}
-pbmtoascii(1), pgm(5)
-\shead{AUTHOR}
-Wilson H. Bent. Jr. (whb@usc.edu)
-%
-% end of input file: asciitopgm.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:07:54 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: atktopbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: atktopbm.1
-%
-\phead{atktopbm}{1}{26 September 1991}{}{}
-
-%.IX atktopbm
-\shead{NAME}
-atktopbm - convert Andrew Toolkit raster object to portable bitmap
-\shead{SYNOPSIS}
-{\bf atktopbm}
-{\rm [}{\it atkfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads an Andrew Toolkit raster object as input.
-%.IX "Andrew Toolkit raster object"
-Produces a portable bitmap as output.
-\shead{SEE ALSO}
-pbmtoatk(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1991 by Bill Janssen.
-% 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.
-%
-% end of input file: atktopbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:35 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: bioradtopgm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: bioradtopgm.1
-%
-\phead{bioradtopgm}{1}{28 June 1993}{}{}
-
-%.IX bioradtopgm
-\shead{NAME}
-bioradtopgm - convert a Biorad confocal file into a portable graymap
-\shead{SYNOPSIS}
-{\bf bioradtopgm}
-{\rm [}{\bf -image\#}{\rm ]}
-{\rm [}{\it imagedata}{\rm ]}
-\shead{DESCRIPTION}
-Reads a Biorad confocal file as input.
-Produces a portable graymap as output.
-If the resulting image is upside down, run it through
-{\bf pnmflip\ -tb .}
-%.IX pnmflip
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -image\#}}
-\item[{{\bf -image\#}}]
-A Biorad image file may contain more than one image. With this flag,
-you can specify which image to extract (only one at a time). The first
-image in the file has number zero. If no
-image number is supplied, only information about the image size and
-the number of images in the input is printed out. No output is produced.
-\end{TPlist}
-
-\shead{BUGS}
-A Biorad image may be in word format. If PbmPlus is not compiled with the
-``BIGGRAYS'' flag, word files can not be converted. See the Makefile.
-\shead{SEE ALSO}
-pgm(5), pnmflip(1)
-\shead{AUTHORS}
-\copyright 1993 by Oliver Trepte (oliver@fysik4.kth.se).
-\nwl
-% 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.
-%
-% end of input file: bioradtopgm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:07 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: bmptoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: bmptoppm.1
-%
-\phead{bmptoppm}{1}{26 Oct 1992}{}{}
-
-%.IX bmptoppm
-\shead{NAME}
-bmptoppm -- convert a BMP file into a portable pixmap
-\shead{SYNOPSIS}
-{\bf bmptoppm}
-{\rm [}{\it bmpfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a Microsoft Windows or OS/2 BMP file as input.
-%.IX BMP
-Produces a portable pixmap as output.
-\shead{SEE ALSO}
-ppmtobmp(1),
-ppm(5)
-\shead{AUTHOR}
-\copyright 1992 by David W. Sanderson.
-% 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.
-%
-% end of input file: bmptoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:07:54 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: brushtopbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: brushtopbm.1
-%
-\phead{brushtopbm}{1}{28 August 1988}{}{}
-
-%.IX brushtopbm
-\shead{NAME}
-brushtopbm - convert a doodle brush file into a portable bitmap
-\shead{SYNOPSIS}
-{\bf brushtopbm}
-{\rm [}{\it brushfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a Xerox doodle brush file as input.
-%.IX "Xerox doodle brush format"
-Produces a portable bitmap as output.
-\par
-Note that there is currently no pbmtobrush tool.
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-\copyright 1988 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.
-%
-% end of input file: brushtopbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:07:55 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: cmuwmtopbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: cmuwmtopbm.1
-%
-\phead{cmuwmtopbm}{1}{15 April 1989}{}{}
-
-%.IX cmuwmtopbm
-\shead{NAME}
-cmuwmtopbm - convert a CMU window manager bitmap into a portable bitmap
-\shead{SYNOPSIS}
-{\bf cmuwmtopbm}
-{\rm [}{\it cmuwmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a CMU window manager bitmap as input.
-%.IX "CMU window manager bitmap"
-Produces a portable bitmap as output.
-\shead{SEE ALSO}
-pbmtocmuwm(1), pbm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: cmuwmtopbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Mon Feb  7 08:49:58 1994
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: fitstopnm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: fitstopnm.1
-%
-\phead{fitstopnm}{1}{20 September 89}{}{}
-
-\shead{NAME}
-fitstopnm - convert a FITS file into a portable anymap
-\shead{SYNOPSIS}
-{\bf fitstopnm}
-{\rm [}{\bf -image}
-{\it N}{\rm ]}
-{\rm [}{\bf -noraw}{\rm ]}
-{\rm [}{\bf -scanmax}{\rm ]}
-{\rm [}{\bf -printmax}{\rm ]}
-{\rm [}{\bf -min}
-{\it f}{\rm ]}
-{\rm [}{\bf -max}
-{\it f}{\rm ]}
-{\rm [}{\it FITSfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a FITS file as input.
-%.IX FITS
-Produces a portable pixmap if the FITS file consists of 3 image planes
-(NAXIS = 3 and NAXIS3 = 3), a portable graymap if the FITS file
-consists of 2 image planes (NAXIS = 2), or whenever the 
-{\bf --image}
-flag is specified.
-The results may need to be flipped top for bottom; if so, just
-pipe the output through
-{\bf pnmflip -tb.}
-%.IX pnmflip
-\shead{OPTIONS}
-\par
-The
-{\bf -image}
-option is for FITS files with three axes.
-The assumption is that the third axis is for multiple images,
-and this option lets you select which one you want.
-\par
-Flags 
-{\bf -min}
-and 
-{\bf -max}
-can be used to override the min and max values as read from the FITS
-header or the image data if no DATAMIN and DATAMAX keywords are found.
-Flag
-{\bf -scanmax}
-can be used to force the program to scan the data even when DATAMIN
-and DATAMAX are found in the header. If 
-{\bf -printmax}
-is specified, the program will just print the min and max values and
-quit.
-Flag
-{\bf -noraw}
-can be used to force 
-the program to produce an ASCII portable anymap.
-\par
-The program will tell what kind of anymap is writing.
-All flags can be abbreviated to their shortest unique prefix.
-\shead{REFERENCES}
-FITS stands for Flexible Image Transport System.  A full description
-can be found in Astronomy \& Astrophysics Supplement Series 44 (1981),
-page 363.
-\shead{SEE ALSO}
-pnmtofits(1), pgm(5), pnmflip(1)
-\shead{AUTHOR}
-Copyright (C) 1989 by Jef Poskanzer, with modifications by 
-Daniel Briggs (dbriggs@nrao.edu) and Alberto
-Accomazzi (alberto@cfa.harvard.edu).
-% 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.
-%
-% end of input file: fitstopnm.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:24 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: fstopgm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: fstopgm.1
-%
-\phead{fstopgm}{1}{06 April 89}{}{}
-
-%.IX fstopgm
-\shead{NAME}
-fstopgm - convert a Usenix FaceSaver(tm) file into a portable graymap
-\shead{SYNOPSIS}
-{\bf fstopgm}
-{\rm [}{\it fsfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a Usenix FaceSaver(tm) file as input.
-%.IX FaceSaver
-Produces a portable graymap as output.
-\par
-FaceSaver(tm) files sometimes have rectangular pixels.
-While
-{\it fstopgm}
-won't re-scale them into square pixels for you,
-it will give you the precise
-{\it pnmscale}
-command that will do the job.
-Because of this, reading a FaceSaver(tm) image is a two-step process.
-First you do:
-\nofill
-  fstopgm $>$ /dev/null
-\fill
-This will tell you whether you need to use
-{\it pnmscale.}
-Then use one of the following pipelines:
-\nofill
-  fstopgm $|$ pgmnorm
-  fstopgm $|$ pnmscale -whatever $|$ pgmnorm
-\fill
-To go to PBM, you want something more like one of these:
-\nofill
-  fstopgm $|$ pnmenlarge 3 $|$ pgmnorm $|$ pgmtopbm
-  fstopgm $|$ pnmenlarge 3 $|$ pnmscale $<$whatever$>$ $|$ pgmnorm $|$ pgmtopbm
-\fill
-You want to enlarge when going to a bitmap because otherwise you lose
-information; but enlarging by more than 3 does not look good.
-\par
-FaceSaver is a registered trademark of Metron Computerware Ltd. of
-Oakland, CA.
-\shead{SEE ALSO}
-pgmtofs(1), pgm(5), pgmnorm(1), pnmenlarge(1), pnmscale(1), pgmtopbm(1)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: fstopgm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:07:55 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: g3topbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: g3topbm.1
-%
-\phead{g3topbm}{1}{02 October 1989}{}{}
-
-%.IX g3topbm
-\shead{NAME}
-g3topbm - convert a Group 3 fax file into a portable bitmap
-\shead{SYNOPSIS}
-{\bf g3topbm}
-{\rm [}{\bf -kludge}{\rm ]}
-{\rm [}{\bf -reversebits}{\rm ]}
-{\rm [}{\bf -stretch}{\rm ]}
-{\rm [}{\it g3file}{\rm ]}
-\shead{DESCRIPTION}
-Reads a Group 3 fax file as input.
-%.IX "Group 3 fax"
-%.IX fax
-Produces a portable bitmap as output.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -kludge}}
-\item[{{\bf -kludge}}]
-Tells
-{\it g3topbm}
-to ignore the first few lines of the file;
-sometimes fax files have some junk at the beginning.
-\item[{{\bf -reversebits}}]
-Tells
-{\it g3topbm}
-to interpret bits least-significant
-first, instead of the default most-significant first.
-Apparently some fax modems do it one way and others do it the other way.
-If you get a whole bunch of ``bad code word'' messages, try using this
-flag.
-\item[{{\bf -stretch}}]
-Tells
-{\it g3topbm}
-to stretch the image vertically by
-duplicating each row.
-This is for the low-quality transmission mode.
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{REFERENCES}
-The standard for Group 3 fax is defined in CCITT Recommendation T.4.
-\shead{BUGS}
-Probably.
-\shead{SEE ALSO}
-pbmtog3(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1989 by Paul Haeberli (paul@manray.sgi.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.
-%
-% end of input file: g3topbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:07:56 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: gemtopbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: gemtopbm.1
-%
-\phead{gemtopbm}{1}{3 December 1988}{}{}
-
-%.IX gemtopbm
-\shead{NAME}
-gemtopbm - convert a GEM .img file into a portable bitmap
-\shead{SYNOPSIS}
-{\bf gemtopbm}
-{\rm [}{\bf -d}{\rm ]}
-{\it gemfile}
-\shead{DESCRIPTION}
-Reads a GEM .img file as input.
-%.IX GEM
-Produces a portable bitmap as output.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -d}}
-\item[{{\bf -d}}]
-Produce output describing the contents of the .img file.
-\end{TPlist}
-
-\shead{BUGS}
-Does not support file containing more than one plane.
-Can't read from standard input.
-\shead{SEE ALSO}
-pbmtogem(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1988 Diomidis D. Spinellis (dds@cc.ic.ac.uk).
-% 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.
-%
-% end of input file: gemtopbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:27 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: giftopnm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: giftopnm.1
-%
-\phead{giftopnm}{1}{29 September 1993}{}{}
-
-%.IX giftopnm
-\shead{NAME}
-giftopnm - convert a GIF file into a portable anymap
-\shead{SYNOPSIS}
-{\bf giftopnm}
-{\rm [}{\bf -verbose}{\rm ]}
-{\rm [}{\bf -comments}{\rm ]}
-{\rm [}{\bf -image}
-{\it N}{\rm ]}
-{\rm [}{\it GIFfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a GIF file for input, and outputs portable anymap.
-%.IX GIF
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -verbose}}
-\item[{{\bf -verbose}}]
-Produces verbose output about the GIF file input.
-\item[{{\bf -comments}}]
-Only outputs GIF89 comment fields.
-\item[{{\bf -image}}]
-Output the specified gif image from the
-input gif archive (where
-{\it N}
-is '1', '2', '20'...).
-Normally there is only one image per file, so this option
-is not needed.
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{BUGS}
-This does not correctly handle the Plain Text Extension of the GIF89
-standard, since I did not have any example input files containing them.
-\shead{SEE ALSO}
-ppmtogif(1), ppm(5)
-\shead{AUTHOR}
-\copyright 1993 by David Koblas (koblas@netcom.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.
-%
-% end of input file: giftopnm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:40 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: gouldtoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: gouldtoppm.1
-%
-\phead{gouldtoppm}{1}{20 May 1990}{}{}
-
-%.IX gouldtoppm
-\shead{NAME}
-gouldtoppm - convert Gould scanner file into a portable pixmap
-\shead{SYNOPSIS}
-{\bf gouldtoppm}
-{\rm [}{\it gouldfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a file produced by the Gould scanner as input.
-%.IX "Gould scanner"
-Produces a portable pixmap as output.
-\shead{SEE ALSO}
-ppm(5)
-\shead{AUTHOR}
-\copyright 1990 by Stephen Paul Lesniewski.
-% 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.
-%
-% end of input file: gouldtoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:25 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: hipstopgm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: hipstopgm.1
-%
-\phead{hipstopgm}{1}{24 August 89}{}{}
-
-%.IX hipstopgm
-\shead{NAME}
-hipstopgm - convert a HIPS file into a portable graymap
-\shead{SYNOPSIS}
-{\bf hipstopgm}
-{\rm [}{\it hipsfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a HIPS file as input.
-%.IX HIPS
-Produces a portable graymap as output.
-\par
-If the HIPS file contains more than one frame in sequence, hipstopgm
-will concatenate all the frames vertically.
-\par
-HIPS is a format developed at the Human Information Processing
-Laboratory, NYU.
-\shead{SEE ALSO}
-pgm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: hipstopgm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:07:57 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: icontopbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: icontopbm.1
-%
-\phead{icontopbm}{1}{31 August 1988}{}{}
-
-%.IX icontopbm
-\shead{NAME}
-icontopbm - convert a Sun icon into a portable bitmap
-\shead{SYNOPSIS}
-{\bf icontopbm}
-{\rm [}{\it iconfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a Sun icon as input.
-%.IX Sun
-%.IX "Sun icon format"
-Produces a portable bitmap as output.
-\shead{SEE ALSO}
-pbmtoicon(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1988 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.
-%
-% end of input file: icontopbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:41 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ilbmtoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ilbmtoppm.1
-%
-\phead{ilbmtoppm}{1}{20 June 1993}{}{}
-
-%.IX ilbmtoppm
-\shead{NAME}
-ilbmtoppm - convert an ILBM file into a portable pixmap
-\shead{SYNOPSIS}
-{\bf ilbmtoppm}
-{\rm [}{\bf -verbose}{\rm ]}
-{\rm [}{\it ILBMfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads an IFF ILBM file as input.
-%.IX "IFF"
-%.IX "ILBM"
-Produces a portable pixmap as output.
-Supported ILBM types are:
-\begin{TPlist}{Normal ILBMs with 1-16 planes.}
-\item[{Normal ILBMs with 1-16 planes.}]
-\item[{Amiga Extra-Halfbrite (EHB)}]
-\item[{Amiga Hold-and-Modify (HAM) with 3-16 planes.}]
-%.IX "HAM"
-\item[{24 bit.}]
-\item[{Color map (BMHD + CMAP chunk only, nPlanes = 0).}]
-\item[{Unofficial direct color.}]
-1-16 planes for each color component.
-\item[{Chunks used:}]
-BMHD, CMAP, CAMG (only HAM \& EHB flags used), BODY
-unofficial DCOL chunk to identify direct color ILBM
-\item[{Chunks ignored:}]
-GRAB, DEST, SPRT, CRNG, CCRT, CLUT, DPPV, DRNG, EPSF
-\item[{Other chunks (ignored but displayed in verbose mode):}]
-NAME, AUTH, (c), ANNO, DPI
-\item[{Unknown chunks are skipped.}]
-\end{TPlist}
-
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -verbose}}
-\item[{{\bf -verbose}}]
-Give some informaton about the ILBM file.
-\end{TPlist}
-
-\shead{BUGS}
-Probably.
-\shead{REFERENCES}
-Amiga ROM Kernel Reference Manual - Devices (3rd Ed.)
-Addison Wesley, ISBN 0--201--56775--X
-\shead{SEE ALSO}
-ppm(5), ppmtoilbm(1)
-\shead{AUTHORS}
-\copyright 1989 by Jef Poskanzer.
-\nwl
-Modified June 1993 by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
-% 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.
-%
-% end of input file: ilbmtoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:41 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: imgtoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: imgtoppm.1
-%
-\phead{imgtoppm}{1}{05 September 1989}{}{}
-
-%.IX imgtoppm
-\shead{NAME}
-imgtoppm - convert an Img-whatnot file into a portable pixmap
-\shead{SYNOPSIS}
-{\bf imgtoppm}
-{\rm [}{\it imgfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads an Img-whatnot file as input.
-Produces a portable pixmap as output.
-The Img-whatnot toolkit is available for FTP on venera.isi.edu,
-along with numerous images in this format.
-%.IX Img-whatnot
-\shead{SEE ALSO}
-ppm(5)
-\shead{AUTHOR}
-Based on a simple conversion program posted to comp.graphics by Ed Falk.
-
-\copyright 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.
-%
-% end of input file: imgtoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:19 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: libpbm.3
-
-\newpage
-%--------------------------------------------------
-% start of input file: libpbm.3
-%
-\phead{libpbm}{3}{}{}{}
-
-\shead{NAME}
-libpbm - functions to support portable bitmap programs
-\shead{SYNOPSIS}
-\def\Ss{\par\vspace{1.0\baselineskip}%.ft CW
-\nofill
-}
-\def\Se{\fill
-%.ft P
-\par\vspace{1.0\baselineskip}}
-\Ss
-\#include $<$pbm.h$>$
-cc ... libpbm.a
-\Se
-\shead{DESCRIPTION - PACKAGE-WIDE ROUTINES}
-%.SS KEYWORD MATCHING
-\Ss
-int pm\_keymatch( char* str, char* keyword, int minchars )
-\Se
-Does a case-insensitive match of
-{\bf str}
-against
-{\bf keyword}{\rm .}
-{\bf str}
-can be a leading sunstring of
-{\bf keyword}{\rm ,}
-but at least
-{\bf minchars}
-must be present.
-%.SS LOG BASE TWO
-\Ss
-int pm\_maxvaltobits( int maxval )
-int pm\_bitstomaxval( int bits )
-\Se
-Convert between a maxval and the minimum number of bits required
-to hold it.
-%.SS MESSAGES AND ERRORS
-\Ss
-void pm\_message( char* fmt, ... )
-\Se
-{\bf printf()}
-style routine to write an informational message.
-\Ss
-void pm\_error( char* fmt, ... )
-\Se
-{\bf printf()}
-style routine to write an error message and abort.
-\Ss
-void pm\_usage( char* usage )
-\Se
-Write a usage message.
-The string should indicate what arguments are to be provided to the program.
-%.SS GENERIC FILE MANAGEMENT
-\Ss
-FILE* pm\_openr( char* name )
-\Se
-Open the given file for reading, with appropriate error checking.
-A filename of ``-'' is taken as equivalent to stdin.
-\Ss
-FILE* pm\_openw( char* name )
-\Se
-Open the given file for writing, with appropriate error checking.
-\Ss
-void pm\_close( FILE* fp )
-\Se
-Close the file descriptor, with appropriate error checking.
-%.SS ENDIAN I/O
-\Ss
-int pm\_readbigshort( FILE* in, short* sP )
-int pm\_writebigshort( FILE* out, short s )
-int pm\_readbiglong( FILE* in, long* lP )
-int pm\_writebiglong( FILE* out, long l )
-int pm\_readlittleshort( FILE* in, short* sP )
-int pm\_writelittleshort( FILE* out, short s )
-int pm\_readlittlelong( FILE* in, long* lP )
-int pm\_writelittlelong( FILE* out, long l )
-\Se
-Routines to read and write short and long ints in either big- or
-little-endian byte order.
-\shead{DESCRIPTION - PBM-SPECIFIC ROUTINES}
-%.SS TYPES AND CONSTANTS
-\Ss
-typedef ... bit;
-\#define PBM\_WHITE ...
-\#define PBM\_BLACK ...
-\Se
-each
-{\bf bit}
-should contain only the values of
-{\bf PBM\_WHITE}
-or
-{\bf PBM\_BLACK}{\rm .}
-\Ss
-\#define PBM\_FORMAT ...
-\#define RPBM\_FORMAT ...
-\#define PBM\_TYPE PBM\_FORMAT
-\#define PBM\_FORMAT\_TYPE(f) ...
-\Se
-For distinguishing different file formats and types.
-%.SS INITIALIZATION
-\Ss
-void pbm\_init( int* argcP, char* argv[] )
-\Se
-All PBM programs must call this routine.
-%.SS MEMORY MANAGEMENT
-\Ss
-bit** pbm\_allocarray( int cols, int rows )
-\Se
-Allocate an array of bits.
-\Ss
-bit* pbm\_allocrow( int cols )
-\Se
-Allocate a row of the given number of bits.
-\Ss
-void pbm\_freearray( bit** bits, int rows )
-\Se
-Free the array allocated with
-{\bf pbm\_allocarray()}
-containing the given number
-of rows.
-\Ss
-void pbm\_freerow( bit* bitrow )
-\Se
-Free a row of bits.
-%.SS READING FILES
-\Ss
-void pbm\_readpbminit( FILE* fp, int* colsP, int* rowsP, int* formatP )
-\Se
-Read the header from a PBM file, filling in the rows, cols and format
-variables.
-\Ss
-void pbm\_readpbmrow( FILE* fp, bit* bitrow, int cols, int format )
-\Se
-Read a row of bits into the bitrow array.
-Format and cols were filled in by
-{\bf pbm\_readpbminit()}{\rm .}
-\Ss
-bit** pbm\_readpbm( FILE* fp, int* colsP, int* rowsP )
-\Se
-Read an entire bitmap file into memory, returning the allocated array and
-filling in the rows and cols variables.
-This function combines
-{\bf pbm\_readpbminit()}{\rm ,}
-{\bf pbm\_allocarray()}
-and
-{\bf pbm\_readpbmrow()}{\rm .}
-\Ss
-char* pm\_read\_unknown\_size( FILE* fp, long* nread )
-\Se
-Read an entire file or input stream of unknown size to a buffer.
-Allocate memory more memory as needed. The calling routine has
-to free the allocated buffer with
-{\bf free()}{\rm .}
-{\bf pm\_read\_unknown\_size()}
-returns a pointer to the allocated buffer. The
-{\bf nread}
-argument returns the number of bytes read.
-%.SS WRITING FILES
-\Ss
-void pbm\_writepbminit( FILE* fp, int cols, int rows, int forceplain )
-\Se
-Write the header for a portable bitmap file.
-The forceplain flag forces a plain-format file to be written, as opposed
-to a raw-format one.
-\Ss
-void pbm\_writepbmrow( FILE* fp, bit* bitrow, int cols, int forceplain )
-\Se
-Write a row from a portable bitmap.
-\Ss
-void pbm\_writepbm( FILE* fp, bit** bits, int cols, int rows, int forceplain )
-\Se
-Write the header and all data for a portable bitmap.
-This function combines
-{\bf pbm\_writepbminit()}
-and
-{\bf pbm\_writepbmrow()}{\rm .}
-\shead{SEE ALSO}
-libpgm(3), libppm(3), libpnm(3)
-\shead{AUTHOR}
-\copyright 1989, 1991 by Tony Hansen and 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.
-%
-% end of input file: libpbm.3
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:37 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: libpgm.3
-
-\newpage
-%--------------------------------------------------
-% start of input file: libpgm.3
-%
-\phead{libpgm}{3}{}{}{}
-
-\shead{NAME}
-libpgm - functions to support portable graymap programs
-\shead{SYNOPSIS}
-\def\Ss{\par\vspace{1.0\baselineskip}%.ft CW
-\nofill
-}
-\def\Se{\fill
-%.ft P
-\par\vspace{1.0\baselineskip}}
-\Ss
-\#include $<$pgm.h$>$
-cc ... libpgm.a libpbm.a
-\Se
-\shead{DESCRIPTION}
-%.SS TYPES AND CONSTANTS
-\Ss
-typedef ... gray;
-\#define PGM\_MAXMAXVAL ...
-extern gray pgm\_pbmmaxval;
-\Se
-Each
-{\bf gray}
-should contain only the values between
-{\bf 0}
-and
-{\bf PGM\_MAXMAXVAL}{\rm .}
-{\bf pgm\_pbmmaxval}
-is the maxval used when a PGM program reads a PBM file.
-Normally it is 1; however, for some programs, a larger value gives better
-results.
-\Ss
-\#define PGM\_FORMAT ...
-\#define RPGM\_FORMAT ...
-\#define PGM\_TYPE PGM\_FORMAT
-int PGM\_FORMAT\_TYPE( int format )
-\Se
-For distinguishing different file formats and types.
-%.SS INITIALIZATION
-\Ss
-void pgm\_init( int* argcP, char* argv[] )
-\Se
-All PGM programs must call this routine.
-%.SS MEMORY MANAGEMENT
-\Ss
-gray** pgm\_allocarray( int cols, int rows )
-\Se
-Allocate an array of grays.
-\Ss
-gray* pgm\_allocrow( int cols )
-\Se
-Allocate a row of the given number of grays.
-\Ss
-void pgm\_freearray( gray** grays, int rows )
-\Se
-Free the array allocated with
-{\bf pgm\_allocarray()}
-containing the given number
-of rows.
-\Ss
-void pgm\_freerow( gray* grayrow )
-\Se
-Free a row of grays.
-%.SS READING FILES
-\Ss
-void pgm\_readpgminit( FILE* fp, int* colsP, int* rowsP, gray* maxvalP, int* formatP )
-\Se
-Read the header from a PGM file, filling in the rows, cols, maxval and format
-variables.
-\Ss
-void pgm\_readpgmrow( FILE* fp, gray* grayrow, int cols, gray maxval, int format )
-\Se
-Read a row of grays into the grayrow array.
-Format, cols, and maxval were filled in by
-{\bf pgm\_readpgminit()}{\rm .}
-\Ss
-gray** pgm\_readpgm( FILE* fp, int* colsP, int* rowsP, gray* maxvalP )
-\Se
-Read an entire graymap file into memory, returning the allocated array and
-filling in the rows, cols and maxval variables.
-This function combines
-{\bf pgm\_readpgminit()}{\rm ,}
-{\bf pgm\_allocarray()}
-and
-{\bf pgm\_readpgmrow()}{\rm .}
-%.SS WRITING FILES
-\Ss
-void pgm\_writepgminit( FILE* fp, int cols, int rows, gray maxval, int forceplain )
-\Se
-Write the header for a portable graymap file.
-The forceplain flag forces a plain-format file to be written, as opposed
-to a raw-format one.
-\Ss
-void pgm\_writepgmrow( FILE* fp, gray* grayrow, int cols, gray maxval, int forceplain )
-\Se
-Write a row from a portable graymap.
-\Ss
-void pgm\_writepgm( FILE* fp, gray** grays, int cols, int rows, gray maxval, int forceplain )
-\Se
-Write the header and all data for a portable graymap.
-This function combines
-{\bf pgm\_writepgminit()}
-and
-{\bf pgm\_writepgmrow()}{\rm .}
-\shead{SEE ALSO}
-libpbm(3), libppm(3), libpnm(3)
-\shead{AUTHOR}
-\copyright 1989, 1991 by Tony Hansen and 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.
-%
-% end of input file: libpgm.3
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:34 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: libpnm.3
-
-\newpage
-%--------------------------------------------------
-% start of input file: libpnm.3
-%
-\phead{libpnm}{3}{}{}{}
-
-\shead{NAME}
-libpnm - functions to support portable anymap programs
-\shead{SYNOPSIS}
-\def\Ss{\par\vspace{1.0\baselineskip}%.ft CW
-\nofill
-}
-\def\Se{\fill
-%.ft P
-\par\vspace{1.0\baselineskip}}
-\Ss
-\#include $<$pnm.h$>$
-cc ... libpnm.a libppm.a libpgm.a libpbm.a
-\Se
-\shead{DESCRIPTION}
-%.SS TYPES AND CONSTANTS
-\Ss
-typedef ... xel;
-typedef ... xelval;
-\#define PNM\_MAXMAXVAL ...
-extern xelval pnm\_pbmmaxval;
-\Se
-Each
-{\bf xel}
-contains three
-{\bf xelval}{\rm s,}
-each of which should contain only the values between
-{\bf 0}
-and
-{\bf PNM\_MAXMAXVAL}{\rm .}
-{\bf pnm\_pbmmaxval}
-is the maxval used when a PNM program reads a PBM file.
-Normally it is 1; however, for some programs, a larger value gives better
-results.
-%.SS XEL MANIPULATIONS
-\Ss
-xelval PNM\_GET1( xel x )
-\Se
-This macro extracts a single value from an xel, when you know it's
-from a PBM or PGM file.
-When it's from a PPM file, use
-{\bf PPM\_GETR()}{\rm ,}
-{\bf PPM\_GETG()}{\rm ,}
-and
-{\bf PPM\_GETB()}{\rm .}
-\Ss
-void PNM\_ASSIGN1( xel x, xelval v )
-\Se
-This macro assigns a single value to an xel, when you know it's
-from a PBM or PGM file.
-When it's from a PPM file, use
-{\bf PPM\_ASSIGN()}{\rm .}
-\Ss
-int PNM\_EQUAL( xel x, xel y )
-\Se
-This macro checks two xels for equality.
-\Ss
-int PNM\_FORMAT\_TYPE( int format )
-\Se
-For distinguishing different file types.
-%.SS INITIALIZATION
-\Ss
-void pnm\_init( int* argcP, char* argv[] )
-\Se
-All PNM programs must call this routine.
-%.SS MEMORY MANAGEMENT
-\Ss
-xel** pnm\_allocarray( int cols, int rows )
-\Se
-Allocate an array of xels.
-\Ss
-xel* pnm\_allocrow( int cols )
-\Se
-Allocate a row of the given number of xels.
-\Ss
-void pnm\_freearray( xel** xels, int rows )
-\Se
-Free the array allocated with
-{\bf pnm\_allocarray()}
-containing the given number
-of rows.
-\Ss
-void pnm\_freerow( xel* xelrow )
-\Se
-Free a row of xels.
-%.SS READING FILES
-\Ss
-void pnm\_readpnminit( FILE* fp, int* colsP, int* rowsP, xelval* maxvalP, int* formatP )
-\Se
-Read the header from a PNM file, filling in the rows, cols, maxval and format
-variables.
-\Ss
-void pnm\_readpnmrow( FILE* fp, xel* xelrow, int cols, xelval maxval, int format )
-\Se
-Read a row of xels into the xelrow array.
-Format, cols, and maxval were filled in by
-{\bf pnm\_readpnminit()}{\rm .}
-\Ss
-xel** pnm\_readpnm( FILE* fp, int* colsP, int* rowsP, xelval* maxvalP, int* formatP )
-\Se
-Read an entire anymap file into memory, returning the allocated array and
-filling in the rows, cols, maxval, and format variables.
-This function combines
-{\bf pnm\_readpnminit()}{\rm ,}
-{\bf pnm\_allocarray()}
-and
-{\bf pnm\_readpnmrow()}{\rm .}
-Unlike the equivalent functions in PBM, PGM, and PPM, it returns the format
-so you can tell what type the file is.
-%.SS WRITING FILES
-\Ss
-void pnm\_writepnminit( FILE* fp, int cols, int rows, xelval maxval, int format, int forceplain )
-\Se
-Write the header for a portable anymap file.
-Unlike the equivalent functions in PBM, PGM, and PPM, you have to specify
-the output type.
-The forceplain flag forces a plain-format file to be written, as opposed
-to a raw-format one.
-\Ss
-void pnm\_writepnmrow( FILE* fp, xel* xelrow, int cols, xelval maxval, int format, int forceplain )
-\Se
-Write a row from a portable anymap.
-\Ss
-void pnm\_writepnm( FILE* fp, xel** xels, int cols, int rows, xelval maxval, int format, int forceplain )
-\Se
-Write the header and all data for a portable anymap.
-This function combines
-{\bf pnm\_writepnminit()}
-and
-{\bf pnm\_writepnmrow()}{\rm .}
-%.SS FORMAT PROMOTION
-\Ss
-void pnm\_promoteformatrow( xel* xelrow, int cols, xelval maxval, int format, xelval newmaxval, int newformat )
-\Se
-Promote a row of xels from one maxval and format to a new set.
-Used when combining multiple anymaps of different types - just
-take the max of the maxvals and the max of the formats, and
-promote them all to that.
-\Ss
-void pnm\_promoteformat( xel** xels, int cols, int rows, xelval maxval, int format, xelval newmaxval, int newformat )
-\Se
-Promote an entire anymap.
-%.SS XEL MANIPULATION
-\Ss
-xel pnm\_whitexel( xelval maxval, int format )
-xel pnm\_blackxel( xelval maxval, int format )
-\Se
-Return a white or black xel for the given maxval and format.
-\Ss
-void pnm\_invertxel( xel* x, xelval maxval, int format )
-\Se
-Invert an xel.
-\Ss
-xel pnm\_backgroundxelrow( xel* xelrow, int cols, xelval maxval, int format )
-\Se
-Figure out an appropriate background xel based on this row.
-\Ss
-xel pnm\_backgroundxel( xel** xels, int cols, int rows, xelval maxval, int format )
-\Se
-Figure out a background xel based on an entire anymap.
-This can do a slightly better job than
-{\bf pnm\_backgroundxelrow()}{\rm .}
-\shead{SEE ALSO}
-pbm(3), pgm(3), ppm(3)
-\shead{AUTHOR}
-\copyright 1989, 1991 by Tony Hansen and 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.
-%
-% end of input file: libpnm.3
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:12 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: libppm.3
-
-\newpage
-%--------------------------------------------------
-% start of input file: libppm.3
-%
-\phead{libppm}{3}{}{}{}
-
-\shead{NAME}
-libppm - functions to support portable pixmap programs
-\shead{SYNOPSIS}
-\def\Ss{\par\vspace{1.0\baselineskip}%.ft CW
-\nofill
-}
-\def\Se{\fill
-%.ft P
-\par\vspace{1.0\baselineskip}}
-\Ss
-\#include $<$ppm.h$>$
-cc ... libppm.a libpgm.a libpbm.a
-\Se
-\shead{DESCRIPTION}
-%.SS TYPES AND CONSTANTS
-\Ss
-typedef ... pixel;
-typedef ... pixval;
-\#define PPM\_MAXMAXVAL ...
-extern pixval ppm\_pbmmaxval;
-\Se
-Each
-{\bf pixel}
-contains three
-{\bf pixval}{\rm s,}
-each of which should contain only the values between
-{\bf 0}
-and
-{\bf PPM\_MAXMAXVAL}{\rm .}
-{\bf ppm\_pbmmaxval}
-is the maxval used when a PPM program reads a PBM file.
-Normally it is 1; however, for some programs, a larger value gives better
-results.
-\Ss
-\#define PPM\_FORMAT ...
-\#define RPPM\_FORMAT ...
-\#define PPM\_TYPE PPM\_FORMAT
-int PPM\_FORMAT\_TYPE( int format )
-\Se
-For distinguishing different file formats and types.
-\Ss
-pixval PPM\_GETR( pixel p )
-pixval PPM\_GETG( pixel p )
-pixval PPM\_GETB( pixel p )
-\Se
-These three macros retrieve the red, green or blue value from the given
-pixel.
-\Ss
-void PPM\_ASSIGN( pixel p, pixval red, pixval grn, pixval blu )
-\Se
-This macro assigns the given red, green and blue values to the pixel.
-\Ss
-int PPM\_EQUAL( pixel p, pixel q )
-\Se
-This macro checks two pixels for equality.
-\Ss
-void PPM\_DEPTH( pixel newp, pixel p, pixval oldmaxval, pixval newmaxval )
-\Se
-This macro scales the colors of pixel
-{\bf p}
-according the old and new maximum values and assigns the new values to
-{\bf newp}{\rm .}
-It is intended to make writing ppmtowhatever easier.
-\Ss
-float PPM\_LUMIN( pixel p )
-\Se
-This macro determines the luminance of the pixel
-{\bf p}{\rm .}
-%.SS MEMORY MANAGEMENT
-\Ss
-pixel** ppm\_allocarray( int cols, int rows )
-\Se
-Allocate an array of pixels.
-\Ss
-pixel* ppm\_allocrow( int cols )
-\Se
-Allocate a row of the given number of pixels.
-\Ss
-void ppm\_freearray( pixel** pixels, int rows )
-\Se
-Free the array allocated with
-{\bf ppm\_allocarray()}
-containing the given number
-of rows.
-\Ss
-void pbm\_freerow( pixel* pixelrow )
-\Se
-Free a row of pixels.
-%.SS READING PBM FILES
-\Ss
-void ppm\_readppminit( FILE* fp, int* colsP, int* rowsP, pixval* maxvalP, int* formatP )
-\Se
-Read the header from a PPM file, filling in the rows, cols, maxval and format
-variables.
-\Ss
-void ppm\_readppmrow( FILE* fp, pixel* pixelrow, int cols, pixval maxval, int format )
-\Se
-Read a row of pixels into the pixelrow array.
-Format, cols, and maxval were filled in by
-{\bf ppm\_readppminit()}{\rm .}
-\Ss
-pixel** ppm\_readppm( FILE* fp, int* colsP, int* rowsP, pixval* maxvalP )
-\Se
-Read an entire pixmap file into memory, returning the allocated array and
-filling in the rows, cols and maxval variables.
-This function combines
-{\bf ppm\_readppminit()}{\rm ,}
-{\bf ppm\_allocarray()}
-and
-{\bf ppm\_readppmrow()}{\rm .}
-%.SS WRITING FILES
-\Ss
-void ppm\_writeppminit( FILE* fp, int cols, int rows, pixval maxval, int forceplain )
-\Se
-Write the header for a portable pixmap file.
-The forceplain flag forces a plain-format file to be written, as opposed
-to a raw-format one.
-\Ss
-void ppm\_writeppmrow( FILE* fp, pixel* pixelrow, int cols, pixval maxval, int forceplain )
-\Se
-Write a row from a portable pixmap.
-\Ss
-void ppm\_writeppm( FILE* fp, pixel** pixels, int cols, int rows, pixval maxval, int forceplain )
-\Se
-Write the header and all data for a portable pixmap.
-This function combines
-{\bf ppm\_writeppminit()}
-and
-{\bf ppm\_writeppmrow()}{\rm .}
-%.SS COLOR NAMES
-\Ss
-pixel ppm\_parsecolor( char* colorname, pixval maxval )
-\Se
-Parses an ASCII color name into a pixel.
-The color can be specified in three ways.  One, as a name, assuming
-that a pointer to an X11-style color names file was compiled in.  Two,
-as an X11-style hexadecimal number: \#rgb, \#rrggbb, \#rrrgggbbb, or
-\#rrrrggggbbbb.  Three, as a triplet of decimal floating point numbers
-separated by commas: r.r,g.g,b.b.
-\Ss
-char* ppm\_colorname( pixel* colorP, pixval maxval, int hexok )
-\Se
-Returns a pointer to a string describing the given color.
-If the X11 color names file is available and the color appears in
-it, that name is returned.
-Otherwise, if the hexok flag is true then a hexadecimal colorspec
-is returned; if hexok is false and the X11 color names file is
-available, then the closest matching color is returned;
-otherwise, it's an error.
-\shead{SEE ALSO}
-pbm(3), pgm(3)
-\shead{AUTHOR}
-\copyright 1989, 1991 by Tony Hansen and 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.
-%
-% end of input file: libppm.3
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:25 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: lispmtopgm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: lispmtopgm.1
-%
-\phead{lispmtopgm}{1}{06 March 1990}{}{}
-
-%.IX lispmtopgm
-\shead{NAME}
-lispmtopgm - convert a Lisp Machine bitmap file into pgm format
-\shead{SYNOPSIS}
-{\bf lispmtopgm}
-{\rm [}{\it lispmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a Lisp Machine bitmap as input.
-%.IX "Lisp Machine bitmap"
-Produces a portable graymap as output.
-\par
-This is the file format written by the tv:write-bit-array-file function on
-TI Explorer and Symbolics lisp machines.
-\par
-Multi-plane bitmaps on lisp machines are color; but the lispm image file
-format does not include a color map, so we must treat it as a graymap 
-instead.  This is unfortunate.
-\shead{SEE ALSO}
-pgmtolispm(1), pgm(5)
-\shead{BUGS}
-The Lispm bitmap file format is a bit quirky;  Usually the image in the file
-has its width rounded up to the next higher multiple of 32, but not always.
-If the width is not a multiple of 32, we don't deal with it properly, but 
-because of the Lispm microcode, such arrays are probably not image data 
-anyway.
-\par
-Also, the lispm code for saving bitmaps has a bug, in that if you are writing a
-bitmap which is not mod32 across, the file may be up to 7 bits too short!  They
-round down instead of up, and we don't handle this bug gracefully.
-\par
-No color.
-\shead{AUTHOR}
-\copyright 1991 by Jamie Zawinski and 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.
-%
-% end of input file: lispmtopgm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:07:57 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: macptopbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: macptopbm.1
-%
-\phead{macptopbm}{1}{29 March 1989}{}{}
-
-%.IX macptopbm
-\shead{NAME}
-macptopbm - convert a MacPaint file into a portable bitmap
-\shead{SYNOPSIS}
-{\bf macptopbm}
-{\rm [}{\bf -extraskip}
-{\it N}{\rm ]}
-{\rm [}{\it macpfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a MacPaint file as input.
-%.IX MacPaint
-%.IX Macintosh
-Produces a portable bitmap as output.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -extraskip}}
-\item[{{\bf -extraskip}}]
-This flag is to get around a problem with some methods
-of transferring files from the Mac world to the Unix world.
-Most of these methods leave the Mac files alone, but a few of
-them add the ``finderinfo'' data onto the front of the Unix file.
-This means an extra 128 bytes to skip over when reading the file.
-The symptom to watch for is that the resulting PBM file looks shifted
-to one side.
-If you get this, try
-{\bf -extraskip}
-128, and if that still doesn't look right try another value.
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-picttoppm(1), pbmtomacp(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1988 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 MacPaint-reading code is \copyright 1987 by Patrick J. Naughton
-(naughton@wind.sun.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. 
-%
-% end of input file: macptopbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:07:58 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: mgrtopbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: mgrtopbm.1
-%
-\phead{mgrtopbm}{1}{24 January 1989}{}{}
-
-%.IX mgrtopbm
-\shead{NAME}
-mgrtopbm - convert a MGR bitmap into a portable bitmap
-\shead{SYNOPSIS}
-{\bf mgrtopbm}
-{\rm [}{\it mgrfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a MGR bitmap as input.
-%.IX MGR
-Produces a portable bitmap as output.
-\shead{SEE ALSO}
-pbmtomgr(1), pbm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: mgrtopbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:42 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: mtvtoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: mtvtoppm.1
-%
-\phead{mtvtoppm}{1}{02 February 1989}{}{}
-
-%.IX mtvtoppm
-\shead{NAME}
-mtvtoppm - convert output from the MTV or PRT ray tracers into a portable pixmap
-\shead{SYNOPSIS}
-{\bf mtvtoppm}
-{\rm [}{\it mtvfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads an input file from Mark VanDeWettering's MTV ray tracer.
-%.IX "MTV raytracer"
-Produces a portable pixmap as output.
-\par
-The PRT raytracer also produces this format.
-%.IX "PRT raytracer"
-\shead{SEE ALSO}
-ppm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: mtvtoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:20 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbm.5
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbm.5
-%
-\phead{pbm}{5}{27 September 1991}{}{}
-
-\shead{NAME}
-pbm - portable bitmap file format
-\shead{DESCRIPTION}
-The portable bitmap format is a lowest common denominator monochrome
-file format.
-%.IX "PBM file format"
-It was originally designed to make it reasonable to mail bitmaps
-between different types of machines using the typical stupid network
-mailers we have today.
-Now it serves as the common language of a large family of bitmap
-conversion filters.
-The definition is as follows:
-\begin{IPlist}
-\IPitem{{-}}
-A ``magic number'' for identifying the file type.
-A pbm file's magic number is the two characters ``P1''.
-%.IX "magic numbers"
-\IPitem{{-}}
-Whitespace (blanks, TABs, CRs, LFs).
-\IPitem{{-}}
-A width, formatted as ASCII characters in decimal.
-\IPitem{{-}}
-Whitespace.
-\IPitem{{-}}
-A height, again in ASCII decimal.
-\IPitem{{-}}
-Whitespace.
-\IPitem{{-}}
-Width * height bits, each either '1' or '0', starting at the top-left
-corner of the bitmap, proceeding in normal English reading order.
-\IPitem{{-}}
-The character '1' means black, '0' means white.
-\IPitem{{-}}
-Whitespace in the bits section is ignored.
-\IPitem{{-}}
-Characters from a ``\#'' to the next end-of-line are ignored (comments).
-\IPitem{{-}}
-No line should be longer than 70 characters.
-\end{IPlist}
-
-\par
-Here is an example of a small bitmap in this format:
-\nofill
-P1
-\# feep.pbm
-24 7
-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 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0
-0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0
-0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 1 0
-0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0
-0 1 0 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 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
-\fill
-\par
-Programs that read this format should be as lenient as possible,
-accepting anything that looks remotely like a bitmap.
-\par
-There is also a variant on the format, available
-by setting the RAWBITS option at compile time.  This variant is
-%.IX RAWBITS
-different in the following ways:
-\begin{IPlist}
-\IPitem{{-}}
-The ``magic number'' is ``P4'' instead of ``P1''.
-\IPitem{{-}}
-The bits are stored eight per byte, high bit first low bit last.
-\IPitem{{-}}
-No whitespace is allowed in the bits section, and only a single character
-of whitespace (typically a newline) is allowed after the height.
-\IPitem{{-}}
-The files are eight times smaller and many times faster to read and write.
-\end{IPlist}
-
-\shead{SEE ALSO}
-atktopbm(1), brushtopbm(1), cmuwmtopbm(1), g3topbm(1),
-gemtopbm(1), icontopbm(1),
-macptopbm(1), mgrtopbm(1), pi3topbm(1), xbmtopbm(1),
-ybmtopbm(1),
-pbmto10x(1), pnmtoascii(1), pbmtoatk(1), pbmtobbnbg(1),
-pbmtocmuwm(1), pbmtoepson(1),
-pbmtog3(1), pbmtogem(1), pbmtogo(1), pbmtoicon(1), pbmtolj(1),
-pbmtomacp(1), pbmtomgr(1), pbmtopi3(1), pbmtoplot(1), pbmtoptx(1),
-pbmtox10bm(1), pbmtoxbm(1), pbmtoybm(1),
-pbmtozinc(1),
-pbmlife(1), pbmmake(1), pbmmask(1), pbmreduce(1),
-pbmtext(1), pbmupc(1),
-pnm(5), pgm(5), ppm(5)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: pbm.5
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:16 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmclean.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmclean.1
-%
-\phead{pbmclean}{12 Dec 1990}{}{}{}
-
-\shead{NAME}
-pbmclean - flip isolated pixels in portable bitmap
-\shead{SYNOPSIS}
-pbmclean [-connect] [pbmfile]
-\shead{DESCRIPTION}
-Reads a portable bitmap as input. Outputs a portable bitmap with every 
-pixel which has less than %
-\it connect %
-\rm identical neighbours inverted.
-Pbmclean can be used to clean up ``snow'' on bitmap images.
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-\copyright 1990 by Angus Duggan.
-\copyright 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.
-%
-% end of input file: pbmclean.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:07:58 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmlife.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmlife.1
-%
-\phead{pbmlife}{1}{21 February 1991}{}{}
-
-%.IX pbmlife
-\shead{NAME}
-pbmlife - apply Conway's rules of Life to a portable bitmap
-\shead{SYNOPSIS}
-{\bf pbmlife}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Applies the rules of Life to it for one generation,
-%.IX Life
-and produces a portable bitmap as output.
-\par
-A white pixel in the image is interpreted as a live beastie, and a
-black pixel as an empty space.
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-\copyright 1988, 1991 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.
-%
-% end of input file: pbmlife.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:07:59 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmmake.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmmake.1
-%
-\phead{pbmmake}{1}{22 February 1989}{}{}
-
-%.IX pbmmake
-\shead{NAME}
-pbmmake - create a blank bitmap of a specified size
-\shead{SYNOPSIS}
-{\bf pbmmake}
-{\rm [}{\bf -white}{\rm $|$}{\bf -black}{\rm $|$}{\bf -gray}
-{\rm ]}
-{\it width height}
-\shead{DESCRIPTION}
-Produces a portable bitmap of the specified width and height.
-%.IX "generating bitmaps"
-The color defaults to white.
-\shead{OPTIONS}
-\par
-In addition to the usual
-{\bf -white}
-and
-{\bf -black}{\rm ,}
-this program implements
-{\bf -gray}{\rm .}
-This gives a simple 50\% gray pattern with 1's and 0's alternating.
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-pbm(5), ppmmake(1)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pbmmake.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:00 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmmask.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmmask.1
-%
-\phead{pbmmask}{1}{08 August 1989}{}{}
-
-%.IX pbmmask
-\shead{NAME}
-pbmmask - create a mask bitmap from a regular bitmap
-\shead{SYNOPSIS}
-{\bf pbmmask}
-{\rm [}{\bf -expand}{\rm ]}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Creates a corresponding mask bitmap and writes it out.
-\par
-The color to be interpreted as ``background'' is determined automatically.
-Regardless of which color is background, the mask will be white where
-the background is and black where the figure is.
-\par
-This lets you do a masked paste like this, for objects with a black background:
-\nofill
-    pbmmask obj $>$ objmask
-    pnmpaste $<$ dest -and objmask $<$x$>$ $<$y$>$ $|$ pnmpaste -or obj $<$x$>$ $<$y$>$
-\fill
-%.IX pnmpaste
-For objects with a white background, you can either invert them or
-add a step:
-\nofill
-    pbmmask obj $>$ objmask
-    pnminvert objmask $|$ pnmpaste -and obj 0 0 $>$ blackback
-    pnmpaste $<$ dest -and objmask $<$x$>$ $<$y$>$ $|$ pnmpaste -or blackback $<$x$>$ $<$y$>$
-\fill
-%.IX pnminvert
-Note that this three-step version works for objects with black backgrounds
-too, if you don't care about the wasted time.
-\par
-You can also use masks with graymaps and pixmaps, using the
-{\it pnmarith}
-tool.  For instance:
-\nofill
-    ppmtopgm obj.ppm $|$ pgmtopbm -threshold $|$ pbmmask $>$ objmask.pbm
-    pnmarith -multiply dest.ppm objmask.pbm $>$ t1.ppm
-    pnminvert objmask.pbm $|$ pnmarith -multiply obj.ppm - $>$ t2.ppm
-    pnmarith -add t1.ppm t2.ppm
-\fill
-%.IX pnmarith
-An interesting variation on this is to pipe the mask through the
-{\it pnmsmooth}
-%.IX pnmsmooth
-script before using it.  This makes the boundary between the two images less
-sharp.
-%.OPTIONS
-\begin{TPlist}{{\bf -expand}}
-\item[{{\bf -expand}}]
-Expands the mask by one pixel out from the image.
-This is useful if you want a little white border around your image.
-(A better solution might be to turn the
-{\it pbmlife}
-tool into a general cellular automaton tool...)
-\end{TPlist}
-
-\shead{SEE ALSO}
-pnmpaste(1), pnminvert(1), pbm(5), pnmarith(1), pnmsmooth(1)
-\shead{AUTHOR}
-\copyright 1988 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.
-%
-% end of input file: pbmmask.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:16 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmpscale.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmpscale.1
-%
-\phead{pbmpscale}{12 Dec 1990}{}{}{}
-
-\shead{NAME}
-pbmpscale - enlarge a portable bitmap with edge smoothing
-\shead{SYNOPSIS}
-pbmpscale N [ pbmfile ]
-\shead{DESCRIPTION}
-Reads a portable bitmap as input, and outputs a portable bitmap
-enlarged N times. Enlargement is done by pixel replication,
-with some additional smoothing of corners and edges.
-\shead{SEE ALSO}
-pnmenlarge(1), ppmscale(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1990 by Angus Duggan.
-\copyright 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.
-\shead{NOTES}
-pbmpscale works best for enlargements of 2. Enlargements greater than 2
-should be done by as many enlargements of 2 as possible, followed by an
-enlargement by the remaining factor.
-%
-% end of input file: pbmpscale.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:00 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmreduce.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmreduce.1
-%
-\phead{pbmreduce}{1}{02 August 1989}{}{}
-
-%.IX pbmreduce
-\shead{NAME}
-pbmreduce - read a portable bitmap and reduce it N times
-\shead{SYNOPSIS}
-{\bf pbmreduce}
-{\rm [}{\bf -floyd}{\rm $|$}{\bf -fs}{\rm $|$}{\bf -threshold}
-{\rm ]}
-{\rm [}{\bf -value}
-{\it val}{\rm ]}
-{\it N}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Reduces it by a factor of
-{\it N}{\rm ,}
-and produces a portable bitmap as output.
-%.IX shrinking
-\par
-{\it pbmreduce}
-duplicates a lot of the functionality of
-{\it pgmtopbm;}
-%.IX pgmtopbm
-you could do something like
-{\bf pnmscale $|$ pgmtopbm,}
-%.IX pnmscale
-but
-{\it pbmreduce}
-is a lot faster.
-\par
-{\it pbmreduce}
-can be used to ``re-halftone'' an image.
-%.IX halftoning
-Let's say you have a scanner that only produces black\&white, not
-grayscale, and it does a terrible job of halftoning (most b\&w scanners
-fit this description).
-One way to fix the halftoning is to scan at the highest possible
-resolution, say 300 dpi, and then reduce by a factor of three or
-so using
-{\it pbmreduce}{\rm .}
-You can even correct the brightness of an image, by using the
-{\bf -value}
-flag.
-\shead{OPTIONS}
-\par
-By default, the halftoning after the reduction is done via
-boustrophedonic Floyd-Steinberg error diffusion; however, the
-%.IX Floyd-Steinberg
-%.IX "error diffusion"
-{\bf -threshold}
-flag can be used to specify simple thresholding.  This gives better
-%.IX thresholding
-results when reducing line drawings.
-\par
-The
-{\bf -value}
-flag alters the thresholding value for all quantizations.
-It should be a real number between 0 and 1.
-Above 0.5 means darker images; below 0.5 means lighter.
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-pnmenlarge(1), pnmscale(1), pgmtopbm(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1988 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.
-%
-% end of input file: pbmreduce.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:01 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtext.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtext.1
-%
-\phead{pbmtext}{1}{5 January 1991}{}{}
-
-%.IX pbmtext
-\shead{NAME}
-pbmtext - render text into a bitmap
-\shead{SYNOPSIS}
-{\bf pbmtext}
-{\rm [}{\bf -font}
-{\it fontfile}{\rm ]}
-{\rm [}{\it text}{\rm ]}
-\shead{DESCRIPTION}
-\par
-Takes the specified text, either a single line from the command
-line or multiple lines from standard input, and renders it
-into a bitmap.
-%.IX text
-\shead{OPTIONS}
-\par
-By default, pbmtext uses a built-in font.
-You can also specify your own font with the
-{\bf -font}
-flag.
-The
-{\it fontfile}
-is a pbm file, created in a very specific way.
-In your window system of choice, display the following text
-in the desired (fixed-width) font:
-\nofill
-
-    M ",/\^{}\_[`jpqy$|$ M
-
-    /  !"\#\$\%\&'()*+ /
-    $<$ ,-./01234567 $<$
-    $>$ 89:;$<$=$>$?@ABC $>$
-    @ DEFGHIJKLMNO @
-    \_ PQRSTUVWXYZ[ \_
-    \{ \bs ]\^{}\_`abcdefg \{
-    \} hijklmnopqrs \}
-    \~{} tuvwxyz\{$|$\}\~{}  \~{}
-
-    M ",/\^{}\_[`jpqy$|$ M
-
-\fill
-Do a screen grab or window dump of that text, using for instance
-{\it xwd}{\rm ,}
-{\it xgrabsc}{\rm ,}
-or
-{\it screendump}{\rm .}
-Convert the result into a pbm file.
-If necessary, use
-{\it pnmcut}
-to remove everything except the text.
-Finally, run it through
-{\it pnmcrop}
-%.IX pnmcrop
-to make sure the edges are right up against the text.
-{\it pbmtext}
-can figure out the sizes and spacings from that.
-\shead{SEE ALSO}
-pbm(5), pnmcut(1), pnmcrop(1)
-\shead{AUTHOR}
-\copyright 1991 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.
-%
-% end of input file: pbmtext.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:02 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmto10x.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmto10x.1
-%
-\phead{pbmto10x}{1}{1 January 1990}{}{}
-
-%.IX pbmto10x
-\shead{NAME}
-pbmto10x - convert a portable bitmap into Gemini 10X printer graphics
-\shead{SYNOPSIS}
-{\bf pbmto10x}
-{\rm [}{\bf -h}{\rm ]}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces a file of Gemini 10X printer graphics as output.
-%.IX "Gemini 10X printer graphics"
-The 10x's printer codes are alleged to be similar to the Epson codes.
-%.IX Epson
-\par
-Note that there is no 10xtopbm tool - this transformation is one way.
-\shead{OPTIONS}
-\par
-The resolution is normally 60H by 72V.
-If the
-{\bf -h}
-flag is specified, resolution is 120H by 144V.
-You may find it useful to rotate landscape images before printing.
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-\copyright 1990 by Ken Yap.
-% 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.
-%
-% end of input file: pbmto10x.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:02 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtoascii.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtoascii.1
-%
-\phead{pbmtoascii}{1}{20 March 1992}{}{}
-
-\shead{NAME}
-pbmtoascii - convert a portable bitmap into ASCII graphics
-\shead{SYNOPSIS}
-{\bf pbmtoascii}
-{\rm [}{\bf -1x2}{\rm $|$}{\bf -2x4}{\rm ]}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces a somewhat crude ASCII graphic as output.
-\par
-Note that there is no asciitopbm tool - this transformation is one-way.
-\shead{OPTIONS}
-The
-{\bf -1x2}
-and
-{\bf -2x4}
-flags give you two alternate ways for the bits to get mapped to characters.
-With
-{\bf 1x2}{\rm ,}
-the default, each character represents a group of 1 bit across by 2 bits down.
-With
-{\bf -2x4}{\rm ,}
-each character represents 2 bits across by 4 bits down.
-With the 1x2 mode you can see the individual bits, so it's useful for
-previewing small bitmaps on a non-graphics terminal.
-The 2x4 mode lets you display larger bitmaps on a standard 80-column display,
-but it obscures bit-level details.
-2x4 mode is also good for displaying
-graymaps - ``pnmscale -width 158 $|$ pgmnorm $|$ pgmtopbm -thresh''
-should give good results.
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-\copyright 1988, 1992 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.
-%
-% end of input file: pbmtoascii.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:03 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtoatk.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtoatk.1
-%
-\phead{pbmtoatk}{1}{26 September 1991}{}{}
-
-%.IX pbmtoatk
-\shead{NAME}
-pbmtoatk - convert portable bitmap to Andrew Toolkit raster object
-\shead{SYNOPSIS}
-{\bf pbmtoatk}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces a Andrew Toolkit raster object as output.
-%.IX "Andrew Toolkit raster object"
-\shead{SEE ALSO}
-atktopbm(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1991 by Bill Janssen.
-% 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.
-%
-% end of input file: pbmtoatk.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:03 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtobbnbg.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtobbnbg.1
-%
-\phead{pbmtobg}{1}{16 May 1989}{}{}
-
-%.IX pbmtobbnbg
-\shead{NAME}
-pbmtobg - convert a portable bitmap into BitGraph graphics
-\shead{SYNOPSIS}
-{\bf pbmtobg}
-{\rm [}{\it rasterop}{\rm ]}
-{\rm [}{\it x}
-{\it y}{\rm ]}
-$<$
-{\it pbmfile}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces BBN BitGraph terminal Display Pixel Data (DPD) sequence as output.
-%.IX "BBN BitGraph"
-\par
-The rasterop can be specified on the command line.  If this is omitted, 3
-(replace) will be used.  A position in (x,y) coordinates can also be
-specified.  If both are given, the rasterop comes first.  The portable bitmap
-is always taken from the standard input.
-\par
-Note that there is no bgtopbm tool.
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-Copyright 1989 by Mike Parker.
-% 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.
-%
-% end of input file: pbmtobbnbg.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:04 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtocmuwm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtocmuwm.1
-%
-\phead{pbmtocmuwm}{1}{15 April 1989}{}{}
-
-%.IX pbmtocmuwm
-\shead{NAME}
-pbmtocmuwm - convert a portable bitmap into a CMU window manager bitmap
-\shead{SYNOPSIS}
-{\bf pbmtocmuwm}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces a CMU window manager bitmap as output.
-%.IX "CMU window manager bitmap"
-\shead{SEE ALSO}
-cmuwmtopbm(1), pbm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pbmtocmuwm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:17 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtoepsi.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtoepsi.1
-%
-\phead{pbmtoepsi}{1}{1992}{}{}
-
-%.IX pbmtoepsi
-\shead{NAME}
-pbmtoepsi - convert a portable bitmap into an encapsulated PostScript
-style preview bitmap
-\shead{SYNOPSIS}
-{\bf pbmtoepsi}
-{\rm [}{\bf -bbonly}{\rm ]}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produce an encapsulated Postscript style bitmap as output. The output
-is not a stand alone postscript file, it is only a preview bitmap,
-which can be included in an encapsulated PostScript file.
-Note that there is no epsitopbm tool - this transformation is one way.
-
-This utility is a part of the pstoepsi tool by Doug Crabill
-(dgc@cs.purdue.edu).
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -bbonly}}
-\item[{{\bf -bbonly}}]
-Only create a boundary box, don't fill it with the image.
-\end{TPlist}
-
-\shead{SEE ALSO}
-pbm(5), pnmtops(1), psidtopgm(1)
-\shead{AUTHOR}
-\copyright 1988 Jef Poskanzer
-\nwl
-modified by Doug Crabill 1992.
-% 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.
-%
-% end of input file: pbmtoepsi.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:04 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtoepson.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtoepson.1
-%
-\phead{pbmtoepson}{1}{4 January 1991}{}{}
-
-%.IX pbmtoepson
-\shead{NAME}
-pbmtoepson - convert a portable bitmap into Epson printer graphics
-\shead{SYNOPSIS}
-{\bf pbmtoepson}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces a file of Epson printer graphics as output.
-%.IX Epson
-\par
-Note that there is no epsontopbm tool - this transformation is one way.
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-\copyright 1991 by John Tiller (tiller@galois.msfc.nasa.gov) and 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.
-%
-% end of input file: pbmtoepson.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:05 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtog3.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtog3.1
-%
-\phead{pbmtog3}{1}{02 October 1989}{}{}
-
-%.IX pbmtog3
-\shead{NAME}
-pbmtog3 - convert a portable bitmap into a Group 3 fax file
-\shead{SYNOPSIS}
-{\bf pbmtog3}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as output.
-Produces a Group 3 fax file as input.
-%.IX "Group 3 fax"
-%.IX fax
-\shead{REFERENCES}
-The standard for Group 3 fax is defined in CCITT Recommendation T.4.
-\shead{BUGS}
-Probably.
-\shead{SEE ALSO}
-g3topbm(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1989 by Paul Haeberli (paul@manray.sgi.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.
-%
-% end of input file: pbmtog3.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:05 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtogem.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtogem.1
-%
-\phead{pbmtogem}{1}{11 March 1990}{}{}
-
-%.IX pbmtogem
-\shead{NAME}
-pbmtogem - convert a portable bitmap into a GEM .img file
-\shead{SYNOPSIS}
-{\bf pbmtogem}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces a GEM .img file as output.
-%.IX GEM
-\shead{BUGS}
-It does not support compression of the data.
-\shead{SEE ALSO}
-gemtopbm(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1988 by David Beckemeyer (bdt!david) and 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.
-%
-% end of input file: pbmtogem.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:06 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtogo.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtogo.1
-%
-\phead{pbmtogo}{1}{24 November 1989}{}{}
-
-%.IX pbmtogo
-\shead{NAME}
-pbmtogo - convert a portable bitmap into compressed GraphOn graphics
-\shead{SYNOPSIS}
-{\bf pbmtogo}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces 2D compressed GraphOn graphics as output.
-%.IX GraphOn
-Be sure to set up your GraphOn with the following modes: 8 bits / no parity;
-obeys no XON/XOFF; NULs are accepted.  These are all on the Comm menu.
-Also, remember to turn off tty post processing.
-Note that there is no gotopbm tool.
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-\copyright 1988, 1989 by Jef Poskanzer, Michael Haberler, and Bo Thide'.
-% 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.
-%
-% end of input file: pbmtogo.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:06 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtoicon.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtoicon.1
-%
-\phead{pbmtoicon}{1}{31 August 1988}{}{}
-
-%.IX pbmtoicon
-\shead{NAME}
-pbmtoicon - convert a portable bitmap into a Sun icon
-\shead{SYNOPSIS}
-{\bf pbmtoicon}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces a Sun icon as output.
-%.IX Sun
-%.IX "Sun icon format"
-\shead{SEE ALSO}
-icontopbm(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1988 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.
-%
-% end of input file: pbmtoicon.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:07 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtolj.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtolj.1
-%
-\phead{pbmtolj}{1}{29 August 1988}{}{}
-
-%.IX pbmtolj
-\shead{NAME}
-pbmtolj - convert a portable bitmap into HP LaserJet format
-\shead{SYNOPSIS}
-{\bf pbmtolj}
-{\rm [}{\bf -resolution}
-{\it N}{\rm ]}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces HP LaserJet data as output.
-%.IX "HP LaserJet"
-\par
-Note that there is no ljtopbm tool.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -resolution}}
-\item[{{\bf -resolution}}]
-Specifies the resolution of the output device, in dpi.
-Typical values are 75, 100, 150, 300.
-The default is 75.
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-\copyright 1988 by Jef Poskanzer and Michael Haberler.
-% 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.
-%
-% end of input file: pbmtolj.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:15 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtoln03.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtoln03.1
-%
-\phead{pbmtoln03}{1}{7 May 1993}{}{}
-
-%.IX pbmtoln03
-\shead{NAME}
-pbmtoln03 - convert protable bitmap to DEC LN03+ Sixel output
-\shead{SYNOPSIS}
-{\bf pbmtoln03}
-{\rm [}{\bf -rltbf}{\rm ]}
-{\it pbmfile}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-%.IX "DEC LN03+ Sixel"
-Produces a DEC LN03+ Sixel output file.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -l nn}}
-\item[{{\bf -l nn}}]
-Use ``nn'' as value for left margin (default 0).
-\item[{{\bf -r nn}}]
-Use ``nn'' as value for right margin (default 2400).
-\item[{{\bf -t nn}}]
-Use ``nn'' as value for top margin (default 0).
-\item[{{\bf -b nn}}]
-Use ``nn'' as value for bottom margin (default 3400).
-\item[{{\bf -f nn}}]
-Use ``nn'' as value for form length (default 3400).
-\end{TPlist}
-
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-Tim Cook, 26 Feb 1992
-%
-% end of input file: pbmtoln03.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:18 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtolps.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtolps.1
-%
-\phead{pbmtolps}{12 Dec 1990}{}{}{}
-
-\shead{NAME}
-pbmtolps - convert portable bitmap to PostScript
-\shead{SYNOPSIS}
-pbmtolps [ -dpi n ] [ pbmfile ]
-\shead{DESCRIPTION}
-Reads a portable bitmap as input, and outputs PostScript.
-The output Postscript uses lines instead of the image operator to
-generate a (device dependent) picture which will be imaged
-much faster.
-\par
-The Postscript path length is constrained to be less that 1000
-points so that no limits are overrun on the Apple Laserwriter
-and (presumably) no other printers.
-\shead{SEE ALSO}
-pgmtops(1), ppmtops(1), pbm(5)
-\shead{AUTHOR}
-\copyright George Phillips (phillips@cs.ubc.ca).
-%
-% end of input file: pbmtolps.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:08 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtomacp.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtomacp.1
-%
-\phead{pbmtomacp}{1}{31 August 1988}{}{}
-
-%.IX pbmtomacp
-\shead{NAME}
-pbmtomacp - convert a portable bitmap into a MacPaint file
-\shead{SYNOPSIS}
-{\bf pbmtomacp}
-{\rm [}{\bf -l}
-{\it left}{\rm ]}
-{\rm [}{\bf -r}
-{\it right}{\rm ]}
-{\rm [}{\bf -b}
-{\it bottom}{\rm ]}
-{\rm [}{\bf -t}
-{\it top}{\rm ]}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-If no input-file is given, standard input is assumed.
-Produces a MacPaint file as output.
-%.IX MacPaint
-%.IX Macintosh
-\par
-The generated file is only the data fork of a picture.
-You will need a program such as
-{\it mcvert}
-to generate a Macbinary or a BinHex file that contains the necessary
-information to identify the file as a PNTG file to MacOS.
-\shead{OPTIONS}
-\par
-Left, right, bottom \& top let you define a square into the pbm file,
-that must be converted.
-Default is the whole file.
-If the file is too large for a MacPaint-file, the bitmap is cut to fit
-from ( left, top ).
-\shead{BUGS}
-The source code contains comments in a language other than English.
-\shead{SEE ALSO}
-ppmtopict(1), macptopbm(1), pbm(5), mcvert(1)
-\shead{AUTHOR}
-\copyright 1988 by Douwe van der Schaaf ($\ldots$!mcvax!uvapsy!vdschaaf).
-% 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.
-%
-% end of input file: pbmtomacp.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:08 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtomgr.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtomgr.1
-%
-\phead{pbmtomgr}{1}{24 January 1989}{}{}
-
-%.IX pbmtomgr
-\shead{NAME}
-pbmtomgr - convert a portable bitmap into a MGR bitmap
-\shead{SYNOPSIS}
-{\bf pbmtomgr}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces a MGR bitmap as output.
-%.IX MGR
-\shead{SEE ALSO}
-mgrtopbm(1), pbm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pbmtomgr.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:36 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtopgm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtopgm.1
-%
-\phead{pbmtopgm}{12 Dec 1990}{}{}{}
-
-\shead{NAME}
-pbmtopgm - convert portable bitmap to portable graymap by averaging areas
-\shead{SYNOPSIS}
-pbmtopgm $<$width$>$ $<$height$>$ [pbmfile]
-\shead{DESCRIPTION}
-Reads a portable bitmap as input. Outputs a portable graymap created by
-averaging the number of pixels within a sample area of
-%
-\it width %
-\rm by %
-\it height %
-\rm around each point. Pbmtopgm is similar to a
-special case of ppmconvol. A ppmsmooth step may be needed after pbmtopgm.
-\par
-Pbmtopgm has the effect of anti-aliasing bitmaps which contain distinct
-line features.
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-\copyright 1990 by Angus Duggan.
-\copyright 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.
-\shead{NOTES}
-Pbmtopgm works best with odd sample width and heights.
-%
-% end of input file: pbmtopgm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:09 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtopi3.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtopi3.1
-%
-\phead{pbmtopi3}{1}{11 March 1990}{}{}
-
-%.IX pbmtopi3
-\shead{NAME}
-pbmtopi3 - convert a portable bitmap into an Atari Degas .pi3 file 
-\shead{SYNOPSIS}
-{\bf pbmtopi3}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces an Atari Degas .pi3 file as output.
-%.IX Atari
-%.IX "Degas .pi3"
-\shead{SEE ALSO}
-pi3topbm(1), pbm(5), ppmtopi1(1), pi1toppm(1)
-\shead{AUTHOR}
-\copyright 1988 by David Beckemeyer (bdt!david) and 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.
-%
-% end of input file: pbmtopi3.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:18 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtopk.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtopk.1
-%
-\phead{pbmtopk}{1}{6 August 1990}{}{}
-
-\shead{NAME}
-pbmtopk - convert a portable bitmap into a packed (PK) format font
-\shead{SYNOPSIS}
-pbmtopk pkfile[.pk] tfmfile[.tfm] resolution [-s designsize] [-p num param...]
-[-C codingscheme] [-F family] [-f optfile] [-c num]
-[-W width] [-H height] [-D depth]
-[-I ital] [-h horiz] [-v vert] [-x xoff] [-y yoff] [pbmfile]...
-\shead{DESCRIPTION}
-Reads portable bitmaps as input, and produces a packed (PK) font file and a
-TFM (TeX font metric) file as output. The resolution parameter indicates the
-resolution of the font, in dots per inch. If the filename ``-'' is used for any
-of the filenames, the standard input stream (or standard output where
-appropriate) will be used.
-\shead{OPTIONS}
-\begin{IPlist}
-\IPitem{{-s\ designsize}}
-Sets the design size of the font, in TeX's points (72.27pt to the inch). The
-default design size is 1. The TFM parameters are given as multiples of the
-design size.
-\IPitem{{-p\ num\ param...}}
-Sets the first num font parameters for the font. The first seven parameters
-are the slant,
-interword spacing, interword space stretchability, interword space
-shrinkability, x-height, quad width, and post-sentence extra space of the
-font. Math and symbol fonts may have more parameters; see The TeXbook for a
-list of these. Reasonable default values are chosen for parameters which are
-not specified.
-\IPitem{{-C\ codingscheme}}
-Sets the coding scheme comment in the TFM file.
-\IPitem{{-F\ family}}
-Sets the font family comment in the TFM file.
-\IPitem{{-f\ optfile}}
-Reads the file optfile, which should contain a lines of the form:
-\par\vspace{1.0\baselineskip}
-\nofill
-\raggedright
-   filename xoff yoff horiz vert width height depth ital
-\fill
-%.ad
-\par\vspace{1.0\baselineskip}
-The pbm files specified by the filename parameters are inserted consecutively
-in the font with the specified attributes. If any of the attributes are
-omitted, or replaced with ``*'', a default value will be calculated from the
-size of the bitmap. The settings of the -W, -H, -D, -I, -h, -v, -x, and -y
-options do not affected characters created in this way.
-The character number can be changed by including a line starting with
-``='',
-followed by the new number.
-Lines beginning with
-``\%'' or ``\#'' are ignored.
-\IPitem{{-c\ num}}
-Sets the character number of the next bitmap encountered to num.
-\IPitem{{-W\ width}}
-Sets the TFM width of the next character to width (in design size multiples).
-\IPitem{{-H\ height}}
-Sets the TFM height of the next character to height (in design size multiples).
-\IPitem{{-D\ depth}}
-Sets the TFM depth of the next character to depth (in design size multiples).
-\IPitem{{-I\ ital}}
-Sets the italic correction of the next character to
-ital (in design size multiples).
-\IPitem{{-h\ horiz}}
-Sets the horizontal escapement of the next character to horiz (in pixels).
-\IPitem{{-v\ vert}}
-Sets the vertical escapement of the next character to vert (in pixels).
-\IPitem{{-x\ xoff}}
-Sets the horizontal offset of the next character to xoff (in pixels).
-\IPitem{{-y\ yoff}}
-Sets the vertical offset of the next character to yoff (in pixels, from the
-top row).
-\end{IPlist}
-
-\shead{SEE ALSO}
-pktopbm(1), pbm(5)
-\shead{AUTHOR}
-Adapted from Tom Rokicki's pxtopk by Angus Duggan (ajcd@dcs.ed.ac.uk).
-
-%
-% end of input file: pbmtopk.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:09 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtoplot.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtoplot.1
-%
-\phead{pbmtoplot}{1}{1 September 1990}{}{}
-
-%.IX pbmtoplot
-\shead{NAME}
-pbmtoplot - convert a portable bitmap into a Unix plot(5) file
-\shead{SYNOPSIS}
-{\bf pbmtoplot}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces a Unix
-{\it plot}
-file.
-%.IX plot
-\par
-Note that there is no plottopbm tool - this transformation is one-way.
-\shead{SEE ALSO}
-pbm(5), plot(5)
-\shead{AUTHOR}
-\copyright 1990 by Arthur David Olson.
-% 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.
-%
-% end of input file: pbmtoplot.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:10 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtoptx.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtoptx.1
-%
-\phead{pbmtoptx}{1}{31 August 1988}{}{}
-
-%.IX pbmtoptx
-\shead{NAME}
-pbmtoptx - convert a portable bitmap into Printronix printer graphics
-\shead{SYNOPSIS}
-{\bf pbmtoptx}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces a file of Printronix printer graphics as output.
-%.IX Printronix
-\par
-Note that there is no ptxtopbm tool - this transformation is one way.
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-\copyright 1988 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.
-%
-% end of input file: pbmtoptx.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:10 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtox10bm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtox10bm.1
-%
-\phead{pbmtox10bm}{1}{31 August 1988}{}{}
-
-%.IX pbmtox10bm
-\shead{NAME}
-pbmtox10bm - convert a portable bitmap into an X10 bitmap
-\shead{SYNOPSIS}
-{\bf pbmtox10bm}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces an X10 bitmap as output.
-This older format is maintained for compatibility.
-%.IX "X bitmap"
-%.IX "X window system"
-\par
-Note that there is no x10bmtopbm tool, because
-{\it xbmtopbm}
-can read both X11 and X10 bitmaps.
-\shead{SEE ALSO}
-pbmtoxbm(1), xbmtopbm(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1988 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.
-%
-% end of input file: pbmtox10bm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:11 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtoxbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtoxbm.1
-%
-\phead{pbmtoxbm}{1}{31 August 1988}{}{}
-
-%.IX pbmtoxbm
-\shead{NAME}
-pbmtoxbm - convert a portable bitmap into an X11 bitmap
-\shead{SYNOPSIS}
-{\bf pbmtoxbm}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces an X11 bitmap as output.
-%.IX "X bitmap"
-%.IX "X window system"
-\shead{SEE ALSO}
-pbmtox10bm(1), xbmtopbm(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1988 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.
-%
-% end of input file: pbmtoxbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:12 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtoybm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtoybm.1
-%
-\phead{pbmtoybm}{1}{06 March 1990}{}{}
-
-%.IX pbmtoybm
-\shead{NAME}
-pgmtoybm - convert a portable bitmap into a Bennet Yee ``face'' file
-\shead{SYNOPSIS}
-{\bf pbmtoybm}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces as output a file acceptable to the
-{\it face}
-and
-{\it xbm}
-programs by Bennet Yee (bsy+@cs.cmu.edu).
-%.IX face
-\shead{SEE ALSO}
-ybmtopbm(1), pbm(5), face(1), face(5), xbm(1)
-\shead{AUTHOR}
-\copyright 1991 by Jamie Zawinski and 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.
-%
-% end of input file: pbmtoybm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:12 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmtozinc.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmtozinc.1
-%
-\phead{pbmtozinc}{l}{02 November 1990}{}{}
-
-%.IX pbmtozinc
-\shead{NAME}
-pbmtozinc - convert a portable bitmap into a Zinc bitmap
-\shead{SYNOPSIS}
-{\bf pbmtozinc}
-{\rm [}{\it pbmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable bitmap as input.
-Produces a bitmap in the format used by the Zinc Interface Library
-(ZIL) Version 1.0 as output.
-%.IX "Zinc Interface Library"
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-\copyright 1988 by James Darrell McCauley (jdm5548@diamond.tamu.edu)
-and 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.
-%
-% Zinc and Zinc Interface Library are trademarks of
-% Zinc Software Inc., Pleasant Grove, Utah.
-%
-% end of input file: pbmtozinc.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:13 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pbmupc.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pbmupc.1
-%
-\phead{pbmupc}{1}{14 March 1989}{}{}
-
-%.IX pbmupc
-\shead{NAME}
-pbmupc - create a Universal Product Code bitmap
-\shead{SYNOPSIS}
-{\bf pbmupc}
-{\rm [}{\bf -s1}{\rm $|$}{\bf -s2}{\rm ]}
-{\it type manufac product}
-\shead{DESCRIPTION}
-Generates a Universal Product Code symbol.
-%.IX "Universal Product Code"
-The three arguments are: a one digit product type, a five digit
-manufacturer code, and a five digit product code.
-For example, ``0 72890 00011'' is the code for Heineken.
-%.IX Heineken
-\par
-As presently configured,
-{\it pbmupc}
-produces a bitmap 230 bits wide and 175 bits high.
-The size can be altered by changing the defines at the beginning of
-the program, or by running the output through
-{\it pnmenlarge}
-or
-{\it pnmscale}{\rm .}
-\shead{OPTIONS}
-\par
-The
-{\bf -s1}
-and
-{\bf -s2}
-flags select the style of UPC to generate.
-The default,
-{\bf -s1}{\rm ,}
-looks more or less like this:
-\nofill
- $|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$
- $|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$
- $|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$
- $|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$
-0$|$$|$12345$|$$|$67890$|$$|$5
-\fill
-The other style,
-{\bf -s2}{\rm ,}
-puts the product type digit higher up, and
-doesn't display the checksum digit:
-\nofill
- $|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$
- $|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$
-0$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$
- $|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$$|$
- $|$$|$12345$|$$|$67890$|$$|$
-\fill
-\shead{SEE ALSO}
-pbm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pbmupc.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:42 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pcxtoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pcxtoppm.1
-%
-\phead{pcxtoppm}{1}{9 April 1990}{}{}
-
-%.IX pcxtoppm
-\shead{NAME}
-pcxtoppm - convert a PCX file into a portable pixmap
-\shead{SYNOPSIS}
-{\bf pcxtoppm}
-{\rm [}{\it pcxfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a PCX file as input.
-%.IX PCX
-Produces a portable pixmap as output.
-\shead{SEE ALSO}
-ppmtopcx(1), ppm(5)
-\shead{AUTHOR}
-\copyright 1990 by Michael Davidson.
-% 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.
-%
-% end of input file: pcxtoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:38 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgm.5
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgm.5
-%
-\phead{pgm}{5}{12 November 1991}{}{}
-
-\shead{NAME}
-pgm - portable graymap file format
-\shead{DESCRIPTION}
-The portable graymap format is a lowest common denominator grayscale
-file format.
-%.IX "PGM file format"
-The definition is as follows:
-\begin{IPlist}
-\IPitem{{-}}
-A ``magic number'' for identifying the file type.
-A pgm file's magic number is the two characters ``P2''.
-%.IX "magic numbers"
-\IPitem{{-}}
-Whitespace (blanks, TABs, CRs, LFs).
-\IPitem{{-}}
-A width, formatted as ASCII characters in decimal.
-\IPitem{{-}}
-Whitespace.
-\IPitem{{-}}
-A height, again in ASCII decimal.
-\IPitem{{-}}
-Whitespace.
-\IPitem{{-}}
-The maximum gray value, again in ASCII decimal.
-\IPitem{{-}}
-Whitespace.
-\IPitem{{-}}
-Width * height gray values, each in ASCII decimal, between 0 and the specified
-maximum value, separated by whitespace, starting at the top-left
-corner of the graymap, proceeding in normal English reading order.
-A value of 0 means black, and the maximum value means white.
-\IPitem{{-}}
-Characters from a ``\#'' to the next end-of-line are ignored (comments).
-\IPitem{{-}}
-No line should be longer than 70 characters.
-\end{IPlist}
-
-\par
-Here is an example of a small graymap in this format:
-\nofill
-P2
-\# feep.pgm
-24 7
-15
-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  3  3  3  3  0  0  7  7  7  7  0  0 11 11 11 11  0  0 15 15 15 15  0
-0  3  0  0  0  0  0  7  0  0  0  0  0 11  0  0  0  0  0 15  0  0 15  0
-0  3  3  3  0  0  0  7  7  7  0  0  0 11 11 11  0  0  0 15 15 15 15  0
-0  3  0  0  0  0  0  7  0  0  0  0  0 11  0  0  0  0  0 15  0  0  0  0
-0  3  0  0  0  0  0  7  7  7  7  0  0 11 11 11 11  0  0 15  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
-\fill
-\par
-Programs that read this format should be as lenient as possible,
-accepting anything that looks remotely like a graymap.
-\par
-There is also a variant on the format, available
-by setting the RAWBITS option at compile time.  This variant is
-different in the following ways:
-%.IX RAWBITS
-\begin{IPlist}
-\IPitem{{-}}
-The ``magic number'' is ``P5'' instead of ``P2''.
-\IPitem{{-}}
-The gray values are stored as plain bytes, instead of ASCII decimal.
-\IPitem{{-}}
-No whitespace is allowed in the grays section, and only a single character
-of whitespace (typically a newline) is allowed after the maxval.
-\IPitem{{-}}
-The files are smaller and many times faster to read and write.
-\end{IPlist}
-
-\par
-Note that this raw format can only be used for maxvals less than
-or equal to 255.
-If you use the
-{\it pgm}
-library and try to write a file with a larger maxval,
-it will automatically fall back on the slower but more general plain
-format.
-\shead{SEE ALSO}
-fitstopgm(1), fstopgm(1), hipstopgm(1), lispmtopgm(1), psidtopgm(1),
-rawtopgm(1),
-pgmbentley(1), pgmcrater(1), pgmedge(1), pgmenhance(1), pgmhist(1), pgmnorm(1),
-pgmoil(1), pgmramp(1), pgmtexture(1),
-pgmtofits(1), pgmtofs(1), pgmtolispm(1), pgmtopbm(1),
-pnm(5), pbm(5), ppm(5)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: pgm.5
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:26 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmbentley.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmbentley.1
-%
-\phead{pgmbentley}{1}{11 January 1991}{}{}
-
-%.IX pgmbentley
-\shead{NAME}
-pgmbentley - Bentleyize a portable graymap
-\shead{SYNOPSIS}
-{\bf pgmbentley}
-{\rm [}{\it pgmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable graymap as input.
-Performs The Bentley Effect, and writes a portable graymap as output.
-%.IX "Bentley Effect"
-\par
-The Bentley Effect is described in ``Beyond Photography'' by Holzmann,
-chapter 4, photo 4.
-It's a vertical smearing based on brightness.
-\shead{SEE ALSO}
-pgmoil(1), ppmrelief(1), pgm(5)
-\shead{AUTHOR}
-\copyright 1990 by Wilson Bent (whb@hoh-2.att.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.
-%
-% end of input file: pgmbentley.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:27 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmcrater.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmcrater.1
-%
-\phead{pgmcrater}{1}{15 October 1991}{}{}
-
-%.IX pgmcrater
-%.IX fractals
-%.IX craters
-\shead{NAME}
-pgmcrater - create cratered terrain by fractal forgery
-\shead{SYNOPSIS}
-\raggedright
-{\bf pgmcrater}
-'ti 15
-{\rm [}{\bf -number}
-{\it n}{\rm ]}
-{\rm [}{\bf -height}{\rm $|$}{\bf -ysize}
-{\it s}{\rm ]}
-{\rm [}{\bf -width}{\rm $|$}{\bf -xsize}
-{\it s}{\rm ]}
-{\rm [}{\bf -gamma}
-{\it g}{\rm ]}
-%.ad
-\shead{DESCRIPTION}
-{\bf pgmcrater}
-creates a portable graymap which mimics cratered terrain.  The graymap
-is created by simulating the impact of a given number of craters with
-random position and size, then rendering the resulting terrain
-elevations based on a light source shining from one side of the
-screen.  The size distribution of the craters is based on a power law
-which results in many more small craters than large ones.  The number
-of craters of a given size varies as the reciprocal of the area as
-described on pages 31 and 32 of Peitgen and Saupe[1]; cratered bodies
-in the Solar System are observed to obey this relationship.  The
-formula used to obtain crater radii governed by this law from a
-uniformly distributed pseudorandom sequence was developed by Rudy
-Rucker.
-\par
-High resolution images with large numbers of craters often benefit
-from being piped through
-{\bf pnmsmooth}{\rm .}
-The averaging performed by this process eliminates some of the jagged
-pixels and lends a mellow ``telescopic image'' feel to the overall
-picture.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -number}{\it \ n}
-}
-\item[{{\bf -number}{\it \ n}
-}]
-Causes
-{\it n}
-craters to be generated.  If no
-{\bf -number}
-specification is given, 50000 craters will be generated.  Don't expect
-to see them all!  For every large crater there are many, many more
-tiny ones which tend simply to erode the landscape.  In general, the
-more craters you specify the more realistic the result; ideally you
-want the entire terrain to have been extensively turned over again and
-again by cratering.  High resolution images containing five to ten
-million craters are stunning but take quite a while to create.
-\item[{{\bf -height}{\it \ height}
-}]
-Sets the height of the generated image to
-{\it height}
-pixels.  The default height is 256 pixels.
-\item[{{\bf -width}{\it \ width}
-}]
-Sets the width of the generated image to
-{\it width}
-pixels.  The default width is 256 pixels.
-\item[{{\bf -xsize}{\it \ width}
-}]
-Sets the width of the generated image to
-{\it width}
-pixels.  The default width is 256 pixels.
-\item[{{\bf -ysize}{\it \ height}
-}]
-Sets the height of the generated image to
-{\it height}
-pixels.  The default height is 256 pixels.
-\item[{{\bf -gamma}{\it \ factor}
-}]
-The specified
-{\it factor}
-is used to gamma correct the graymap in the same manner as performed
-by
-{\bf pnmgamma}{\rm .}
-The default value is 1.0, which results in a medium contrast image.
-Values larger than 1 lighten the image and reduce contrast, while
-values less than 1 darken the image, increasing contrast.
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{BUGS}
-The
-{\bf -gamma}
-option isn't really necessary since you can achieve the same
-effect by piping the output from
-{\bf pgmcrater}
-through
-{\bf pnmgamma}{\rm .}
-However,
-{\bf pgmcrater}
-performs an internal gamma map anyway in the process of rendering the
-elevation array into a graymap, so there's no additional overhead in
-allowing a user-specified gamma.
-\par
-Real craters have two distinct morphologies.
-{\bf pgmcrater}
-simulates only small craters, which are hemispherical in shape
-(regardless of the incidence angle of the impacting body, as long as the
-velocity is sufficiently high).  Large craters, such as Copernicus and
-Tycho on the Moon, have a ``walled plain'' shape with a cross-section more
-like:
-\nofill
-%.ne 2
-%.cs R 18
-                /\bs                             /\bs 
-\nwl
-          \_\_\_\_\_/  \bs \_\_\_\_\_\_\_\_\_\_\_\_/\bs \_\_\_\_\_\_\_\_\_\_\_\_/  \bs \_\_\_\_\_
-%.cs R
-\fill
-%.ss 12
-Larger craters should really use this profile, including the central
-peak, and totally obliterate the pre-existing terrain.
-\shead{SEE ALSO}
-{\bf pgm}{\rm (5),}
-{\bf pnmgamma}{\rm (1),}
-{\bf pnmsmooth}{\rm (1)}
-\begin{TPlist}{[1]}
-\item[{[1]}]
-Peitgen, H.-O., and Saupe, D. eds., The Science Of Fractal Images,
-New York: Springer Verlag, 1988.
-%.ne 10
-\end{TPlist}
-
-\shead{AUTHOR}
-\ind{1\parindent}{\nofill
-    John Walker
-    Autodesk SA
-    Avenue des Champs-Montants 14b
-    CH-2074 MARIN
-    Suisse/Schweiz/Svizzera/Svizra/Switzerland
-\fill}
-\begin{TPlist}{Usenet:}
-\item[{Usenet:}]
-kelvin@Autodesk.com
-\item[{Fax:}]
-038/33 88 15
-\item[{Voice:}]
-038/33 76 33
-\end{TPlist}
-
-\par
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose and without fee is hereby granted,
-without any conditions or restrictions.  This software is provided ``as
-is'' without express or implied warranty.
-\par
-{\bf PLUGWARE!}
-If you like this kind of stuff, you may also enjoy ``James Gleick's
-Chaos--The Software'' for MS-DOS, available for \$59.95 from your
-local software store or directly from Autodesk, Inc., Attn: Science
-Series, 2320 Marinship Way, Sausalito, CA 94965, USA.  Telephone:
-(800) 688-2344 toll-free or, outside the U.S. (415) 332-2344 Ext
-4886.  Fax: (415) 289-4718.  ``Chaos--The Software'' includes a more
-comprehensive fractal forgery generator which creates
-three-dimensional landscapes as well as clouds and planets, plus five
-more modules which explore other aspects of Chaos.  The user guide of
-more than 200 pages includes an introduction by James Gleick and
-detailed explanations by Rudy Rucker of the mathematics and algorithms
-used by each program.
-%
-% end of input file: pgmcrater.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:27 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmedge.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmedge.1
-%
-\phead{pgmedge}{1}{04 February 1990}{}{}
-
-%.IX pgmedge
-\shead{NAME}
-pgmedge - edge-detect a portable graymap
-\shead{SYNOPSIS}
-{\bf pgmedge}
-{\rm [}{\it pgmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable graymap as input.
-Outlines the edges, and writes a portable graymap as output.
-%.IX "edge detection"
-Piping the result through
-{\bf pgmtopbm -threshold}
-and playing with the
-threshold value will give a bitmap of the edges.
-%.IX thresholding
-\par
-The edge detection technique used is to take the Pythagorean sum of
-two Sobel gradient operators at 90 degrees to each other.
-For more details see ``Digital Image Processing'' by Gonzalez and Wintz,
-chapter 7.
-\shead{SEE ALSO}
-pgmenhance(1), pgmtopbm(1), pgm(5), pbm(5)
-\shead{AUTHOR}
-\copyright 1991 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.
-%
-% end of input file: pgmedge.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:28 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmenhance.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmenhance.1
-%
-\phead{pgmenhance}{1}{13 January 1989}{}{}
-
-%.IX pgmenhance
-\shead{NAME}
-pgmenhance - edge-enhance a portable graymap
-\shead{SYNOPSIS}
-{\bf pgmenhance}
-{\rm [}{\it -N}{\rm ]}
-{\rm [}{\it pgmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable graymap as input.
-Enhances the edges, and writes a portable graymap as output.
-%.IX "edge enhancement"
-\par
-The edge enhancing technique is taken from Philip R. Thompson's ``xim''
-program, which in turn took it from section 6 of ``Digital Halftones by
-Dot Diffusion'', D. E. Knuth, ACM Transaction on Graphics Vol. 6, No. 4,
-October 1987, which in turn got it from two 1976 papers by J. F. Jarvis
-{\it et. al.}
-\shead{OPTIONS}
-\par
-The optional
-{\it -N}
-flag should be a digit from 1 to 9.
-1 is the lowest level of enhancement, 9 is the highest,
-The default is 9.
-\shead{SEE ALSO}
-pgmedge(1), pgm(5), pbm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pgmenhance.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:29 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmhist.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmhist.1
-%
-\phead{pgmhist}{1}{28 February 1989}{}{}
-
-%.IX pgmhist
-\shead{NAME}
-pgmhist - print a histogram of the values in a portable graymap
-\shead{SYNOPSIS}
-{\bf pgmhist}
-{\rm [}{\it pgmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable graymap as input.
-Prints a histogram of the gray values.
-\shead{SEE ALSO}
-pgmnorm(1), pgm(5), ppmhist(1)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pgmhist.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Feb  4 14:35:48 1994
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmkernel.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmkernel.1
-%
-\phead{pgmkernel}{1}{10 December 1992}{}{}
-
-%.IX pgmkernel
-\shead{NAME}
-pgmkernel - generate a convolution kernel
-\shead{SYNOPSIS}
-{\bf pgmkernel} [{\bf --weight}{\it w}] {\it width} [{\it height}]
-\shead{DESCRIPTION}
-Generates a portable graymap array of size
-{\it width} x {\it height} (or {\it width} x {\it width} if {\it height}
-is not specified) to be used as a convolution file by
-{\bf pnmconvol}{\rm .}
-The data in the convolution array K are computed according to the
-formula:
-\par
-K(i,j) = 1 / ( 1 + w * sqrt((i-width/2)\^{}2 + (j-height/2)\^{}2)) 
-\par
-where 
-{\it w}
-is a coefficient specified via the 
-{\it --weight}
-flag, and
-{\it width}
-and 
-{\it height}
-are the X and Y filter sizes.
-\par
-The output PGM file is always written out in ASCII format.
-\shead{OPTIONS}
-The optional 
-{\it -weight}
-flag should be a real number greater than -1.
-The default value is 6.0.
-\shead{BUGS}
-The computation time is proportional to 
-{\it width}
-* 
-{\it height}{\rm .}
-This increases rapidly with the increase of the kernel size.  
-A better approach could be using a FFT in these cases.
-\shead{SEE ALSO}
-pnmconvol(1), pnmsmooth(1)
-\shead{AUTHOR}
-Alberto Accomazzi (alberto@cfa.harvard.edu).
-%
-% end of input file: pgmkernel.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Mon Nov 29 13:21:08 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmnoise.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmnoise.1
-%
-\phead{pgmnoise}{1}{16 November 1993}{}{}
-
-%.IX pgmnoise
-\shead{NAME}
-pgmnoise - create a graymap made up of white noise
-\shead{SYNOPSIS}
-{\bf pgmnoise}
-{\it width height}
-\shead{DESCRIPTION}
-Creates a portable graymap that is made up of random pixels with
-gray values in the range of 0 to PGM\_MAXMAXVAL (depends on the compilation,
-either 255 or 65535). The graymap has a size of width * height pixels.
-\shead{SEE ALSO}
-pgm(5)
-\shead{AUTHOR}
-\copyright 1993 by Frank Neumann.
-% 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.
-%
-% end of input file: pgmnoise.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:29 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmnorm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmnorm.1
-%
-\phead{pgmnorm}{1}{28 February 1989}{}{}
-
-%.IX pgmnorm
-\shead{NAME}
-pgmnorm - normalize the contrast in a portable graymap
-\shead{SYNOPSIS}
-{\bf pgmnorm}
-{\rm [}{\bf -bpercent}
-{\it N}
-$|$
-{\bf -bvalue}
-{\it N}{\rm ]}
-{\rm [}{\bf -wpercent}
-{\it N}
-$|$
-{\bf -wvalue}
-{\it N}{\rm ]}
-{\rm [}{\it pgmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable graymap as input.
-Normalizes the contrast by forcing the lightest pixels to white, the
-%.IX "contrast normalization"
-darkest pixels to black, and linearly rescaling the ones in between;
-and produces a portable graymap as output.
-\shead{OPTIONS}
-\par
-By default, the darkest 2 percent of all pixels are mapped to black, and
-the lightest 1 percent are mapped to white.
-You can override these percentages by using the
-{\bf -bpercent}
-and
-{\bf -wpercent}
-flags,
-or you can specify the exact pixel values to be mapped by using the
-{\bf -bvalue}
-and
-{\bf -wvalue}
-flags.
-Appropriate numbers for the flags can be gotten from the
-{\it pgmhist}
-tool.
-%.IX pgmhist
-If you just want to enhance the contrast, then choose values at elbows in the
-histogram; {\it e.g.}, if value 29 represents 3\% of the image but value 30
-represents 20\%, choose 30 for
-{\it bvalue}{\rm .}
-If you want to lighten the
-image, then set
-{\it bvalue}
-to 0 and just fiddle with
-{\it wvalue}{\rm ;}
-similarly, to darken the image, set
-{\it wvalue}
-to maxval and play with
-{\it bvalue}{\rm .}
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-pgmhist(1), pgm(5)
-\shead{AUTHOR}
-Partially based on the fbnorm filter in Michael Mauldin's ``Fuzzy
-Pixmap''
-package.
-
-\copyright 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.
-%
-% end of input file: pgmnorm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:30 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmoil.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmoil.1
-%
-\phead{pgmoil}{1}{11 January 1991}{}{}
-
-%.IX pgmoil
-\shead{NAME}
-pgmoil - turn a portable graymap into an oil painting
-\shead{SYNOPSIS}
-{\bf pgmoil}
-{\rm [}{\bf -n}
-{\it N}{\rm ]}
-{\rm [}{\it pgmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable graymap as input.
-Does an ``oil transfer'', and writes a portable graymap as output.
-%.IX "oil transfer"
-\par
-The oil transfer is described in ``Beyond Photography'' by Holzmann,
-chapter 4, photo 7.
-It's a sort of localized smearing.
-\shead{OPTIONS}
-\par
-The optional
-{\bf -n}
-flag controls the size of the area smeared.  The default value is 3.
-\shead{BUGS}
-Takes a long time to run.
-\shead{SEE ALSO}
-pgmbentley(1), ppmrelief(1), pgm(5)
-\shead{AUTHOR}
-\copyright 1990 by Wilson Bent (whb@hoh-2.att.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.
-%
-% end of input file: pgmoil.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:30 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmramp.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmramp.1
-%
-\phead{pgmramp}{1}{24 November 1989}{}{}
-
-%.IX pgmramp
-\shead{NAME}
-pgmramp - generate a grayscale ramp
-\shead{SYNOPSIS}
-{\bf pgmramp}
-{\bf -lr}{\rm $|$}{\bf -tb}
-$|$
-{\bf -rectangle}{\rm $|$}{\bf -ellipse}
-{\it width height}
-\shead{DESCRIPTION}
-Generates a graymap of the specified size containing a black-to-white ramp.
-%.IX "generating graymaps"
-These ramps are useful for multiplying with other images, using the
-{\it pnmarith}
-tool.
-%.IX pnmarith
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -lr}}
-\item[{{\bf -lr}}]
-A left to right ramp.
-\item[{{\bf -tb}}]
-A top to bottom ramp.
-\item[{{\bf -rectangle}}]
-A rectangular ramp.
-\item[{{\bf -ellipse}}]
-An elliptical ramp.
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-pnmarith(1), pgm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pgmramp.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:35 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmtexture.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmtexture.1
-%
-\phead{pgmtexture}{1}{22 Aug 1991}{}{}
-
-%.IX pgmtexture
-\shead{NAME}
-pgmtexture - calculate textural features on a portable graymap
-\shead{SYNOPSIS}
-{\bf pgmtexture}
-{\rm [}{\bf -d}
-{\it d}{\rm ]}
-{\rm [}{\it pgmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable graymap as input.  Calculates textural features
-based on spatial dependence matrices at 0, 45, 90, and 135 degrees for
-a distance 
-{\it d}
-(default = 1).
-Textural features include:
-\begin{IPlist}
-\IPitem{{}}
-(1) Angular Second Moment,
-\nwl
-(2) Contrast,
-\nwl
-(3) Correlation,
-\nwl
-(4) Variance,          
-\nwl
-(5) Inverse Difference Moment,
-\nwl
-(6) Sum Average,
-\nwl
-(7) Sum Variance,
-\nwl
-(8) Sum Entropy,
-\nwl
-(9) Entropy,
-\nwl
-(10) Difference Variance,
-\nwl
-(11) Difference Entropy,
-\nwl
-(12, 13) Information Measures of Correlation, and
-\nwl
-(14) Maximal Correlation Coefficient.
-\end{IPlist}
-
-\par
-Algorithm taken from:
-\nwl
-Haralick, R.M., K. Shanmugam, and I. Dinstein. 1973. Textural features
-for image classification.  
-{\it IEEE Transactions on Systems, Man,}
-{\it and Cybertinetics,}
-SMC-3(6):610-621.
-\shead{BUGS}
-The program can run incredibly slow for large images (larger than 64 x 64)
-and command line options are limited.
-The method for finding (14) the maximal correlation coefficient, which
-requires finding the second largest eigenvalue of a matrix Q, does not
-always converge.
-\shead{REFERENCES}
-{\it IEEE Transactions on Systems, Man,}
-{\it and Cybertinetics,}
-SMC-3(6):610-621.
-\shead{SEE ALSO}
-pgm(5), pnmcut(1)
-\shead{AUTHOR}
-\copyright 1991 by Texas Agricultural Experiment Station, employer for
-hire of James Darrell McCauley. 
-% 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 TEXAS AGRICULTURAL EXPERIMENT STATION (TAES) AND THE TEXAS A&M
-% UNIVERSITY SYSTEM (TAMUS) MAKE NO EXPRESS OR IMPLIED WARRANTIES
-% (INCLUDING BY WAY OF EXAMPLE, MERCHANTABILITY) WITH RESPECT TO ANY
-% ITEM, AND SHALL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL
-% OR CONSEQUENTAL DAMAGES ARISING OUT OF THE POSESSION OR USE OF
-% ANY SUCH ITEM. LICENSEE AND/OR USER AGREES TO INDEMNIFY AND HOLD
-% TAES AND TAMUS HARMLESS FROM ANY CLAIMS ARISING OUT OF THE USE OR
-% POSSESSION OF SUCH ITEMS.
-%
-% end of input file: pgmtexture.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:31 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmtofits.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmtofits.1
-%
-\phead{pgmtofits}{1}{20 September 1989}{}{}
-
-%.IX pgmtofits
-\shead{NAME}
-pgmtofits - convert a portable graymap into FITS format
-\shead{SYNOPSIS}
-{\bf pgmtofits}
-{\rm [}{\it pgmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable graymap as input.
-Produces a FITS file as output.
-%.IX FITS
-\par
-FITS stands for Flexible Image Transport System.  A full description
-can be found in Astronomy \& Astrophysics Supplement Series 44 (1981),
-page 363.
-\shead{SEE ALSO}
-fitstopgm(1), pgm(5)
-\shead{AUTHOR}
-\copyright 1989 by Wilson H. Bent (whb@hoh-2.att.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.
-%
-% end of input file: pgmtofits.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:31 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmtofs.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmtofs.1
-%
-\phead{pgmtofs}{1}{18 May 1990}{}{}
-
-%.IX pgmtofs
-\shead{NAME}
-pgmtofs - convert portable graymap to Usenix FaceSaver(tm) format
-\shead{SYNOPSIS}
-{\bf pgmtofs}
-{\rm [}{\it pgmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable graymap as input.
-Produces Usenix FaceSaver(tm) format as output.
-%.IX FaceSaver
-\par
-FaceSaver is a registered trademark of Metron Computerware Ltd. of
-Oakland, CA.
-\shead{SEE ALSO}
-fstopgm(1), pgm(5)
-\shead{AUTHOR}
-\copyright 1991 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.
-%
-% end of input file: pgmtofs.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:32 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmtolispm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmtolispm.1
-%
-\phead{pgmtolispm}{1}{06 March 1990}{}{}
-
-%.IX pgmtolispm
-\shead{NAME}
-pgmtolispm - convert a portable graymap into Lisp Machine format
-\shead{SYNOPSIS}
-{\bf pgmtolispm}
-{\rm [}{\it pgmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable graymap as input.
-Produces a Lisp Machine bitmap as output.
-%.IX "Lisp Machine bitmap"
-\par
-This is the file format read by the tv:read-bit-array-file function on
-TI Explorer and Symbolics lisp machines.
-\par
-Given a pgm (instead of a pbm) a multi-plane image will be output.
-This is probably not useful unless you have a color lisp machine.
-\par
-Multi-plane bitmaps on lisp machines are color; but the lispm image file
-format does not include a color map, so we must treat it as a graymap 
-instead.  This is unfortunate.
-\shead{SEE ALSO}
-lispmtopgm(1), pgm(5)
-\shead{BUGS}
-Output width is always rounded up to the nearest multiple of 32; this might 
-not always be what you want, but it probably is (arrays which are not 
-modulo 32 cannot be passed to the Lispm BITBLT function, and thus cannot 
-easily be displayed on the screen).
-\par
-No color.
-\shead{AUTHOR}
-\copyright 1991 by Jamie Zawinski and 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.
-%
-% end of input file: pgmtolispm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:33 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmtopbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmtopbm.1
-%
-\phead{pgmtopbm}{1}{26 July 1988}{}{}
-
-%.IX pgmtopbm
-\shead{NAME}
-pgmtopbm - convert a portable graymap into a portable bitmap
-\shead{SYNOPSIS}
-{\bf pgmtopbm}
-{\rm [}{\bf -floyd}{\rm $|$}{\bf -fs}{\rm $|$}{\bf -threshold}
-{\rm $|$}{\bf -dither8}{\rm $|$}{\bf -d8}{\rm $|$}{\bf -cluster3}
-{\rm $|$}{\bf -c3}{\rm $|$}{\bf -cluster4}{\rm $|$}{\bf -c4}
-{\rm $|$}{\bf -cluster8}{\rm $|$}{\bf -c8}{\rm ]}
-{\rm [}{\bf -value}
-{\it val}{\rm ]}
-{\rm [}{\it pgmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable graymap as input.
-Produces a portable bitmap as output.
-%.IX halftoning
-\par
-Note that there is no pbmtopgm converter, because any pgm program can
-read pbm files automagically.
-\shead{OPTIONS}
-\par
-The default quantization method is boustrophedonic Floyd-Steinberg error
-diffusion
-{\rm (}{\bf -floyd}
-or
-{\bf -fs}{\rm ).}
-%.IX Floyd-Steinberg
-%.IX "error diffusion"
-Also available are simple thresholding
-{\rm (}{\bf -threshold}{\rm );}
-%.IX thresholding
-Bayer's ordered dither
-{\rm (}{\bf -dither8}{\rm )}
-with a 16x16 matrix; and three different sizes of 45-degree clustered-dot dither
-{\rm (}{\bf -cluster3}{\rm ,}
-{\bf -cluster4}{\rm ,}
-{\bf -cluster8}{\rm ).}
-%.IX dithering
-\par
-Floyd-Steinberg will almost always give the best looking results; however,
-looking good is not always what you want.
-For instance, thresholding can be used in a pipeline with the
-{\it pnmconvol}
-%.IX pnmconvol
-tool, for tasks like edge and peak detection.
-And clustered-dot dithering gives a newspaper-ish look, a useful special effect.
-\par
-The
-{\bf -value}
-flag alters the thresholding value for Floyd-Steinberg and
-simple thresholding.
-It should be a real number between 0 and 1.
-Above 0.5 means darker images; below 0.5 means lighter.
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{REFERENCES}
-The only reference you need for this stuff is ``Digital Halftoning'' by
-Robert Ulichney, MIT Press, ISBN 0--262--21009--6.
-\shead{SEE ALSO}
-pbmreduce(1), pgm(5), pbm(5), pnmconvol(1)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pgmtopbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:43 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pgmtoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pgmtoppm.1
-%
-\phead{pgmtoppm}{1}{11 January 1991}{}{}
-
-%.IX pgmtoppm
-\shead{NAME}
-pgmtoppm - colorize a portable graymap into a portable pixmap
-\shead{SYNOPSIS}
-{\bf pgmtoppm}
-{\it colorspec}
-{\rm [}{\it pgmfile}{\rm ]}
-\nwl
-{\bf pgmtoppm}
-{\it colorspec1}{\bf -}{\it colorspec2}
-{\rm [}{\it pgmfile}{\rm ]}
-\nwl
-{\bf pgmtoppm -map}
-{\it mapfile}
-{\rm [}{\it pgmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable graymap as input.
-Colorizes it by multiplying the the gray values by specified color or colors,
-and produces a portable pixmap as output.
-%.IX colorization
-\par
-If only one color is specified, black in the pgm file stays black and
-white in the pgm file turns into the specified color in the ppm file.
-If two colors (separated by a dash) are specified, then black gets mapped
-to the first color and white gets mapped to the second.
-\par
-The color can be specified in five ways:
-%.IX "specifying colors"
-\begin{TPlist}{o}
-\item[{o}]
-A name, assuming
-that a pointer to an X11-style color names file was compiled in.
-\item[{o}]
-An X11-style hexadecimal specifier: rgb:r/g/b, where r g and b are
-each 1- to 4-digit hexadecimal numbers.
-\item[{o}]
-An X11-style decimal specifier: rgbi:r/g/b, where r g and b are
-floating point numbers between 0 and 1.
-\item[{o}]
-For backwards compatibility, an old-X11-style hexadecimal
-number: \#rgb, \#rrggbb, \#rrrgggbbb, or \#rrrrggggbbbb.
-\item[{o}]
-For backwards compatibility, a triplet of numbers
-separated by commas: r,g,b, where r g and b are
-floating point numbers between 0 and 1.
-(This style was added before MIT came up with the similar rgbi style.)
-\end{TPlist}
-
-\par
-Also, the
-{\bf -map}
-flag lets you specify an entire colormap to be used.
-The mapfile is just a
-{\it ppm}
-file; it can be any shape, all that matters
-is the colors in it and their order.
-In this case, black gets mapped into the first
-color in the map file, and white gets mapped to the last.
-\shead{SEE ALSO}
-rgb3toppm(1), ppmtopgm(1), ppmtorgb3(1), ppm(5), pgm(5)
-\shead{AUTHOR}
-\copyright 1991 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.
-%
-% end of input file: pgmtoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:43 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pi1toppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pi1toppm.1
-%
-\phead{pi1toppm}{1}{19 July 1990}{}{}
-
-%.IX pi1toppm
-\shead{NAME}
-pi1toppm - convert an Atari Degas .pi1 into a portable pixmap
-\shead{SYNOPSIS}
-{\bf pi1toppm}
-{\rm [}{\it pi1file}{\rm ]}
-\shead{DESCRIPTION}
-Reads an Atari Degas .pi1 file as input.
-%.IX Atari
-%.IX "Degas .pi1"
-Produces a portable pixmap as output.
-\shead{SEE ALSO}
-ppmtopi1(1), ppm(5), pi3topbm(1), pbmtopi3(1)
-\shead{AUTHOR}
-\copyright 1991 by Steve Belczyk (seb3@gte.com) and 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.
-%
-% end of input file: pi1toppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:13 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pi3topbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pi3topbm.1
-%
-\phead{pi3topbm}{1}{11 March 1990}{}{}
-
-%.IX pi3topbm
-\shead{NAME}
-pi3topbm - convert an Atari Degas .pi3 file into a portable bitmap
-\shead{SYNOPSIS}
-{\bf pi3topbm}
-{\rm [}{\it pi3file}{\rm ]}
-\shead{DESCRIPTION}
-Reads an Atari Degas .pi3 file as input.
-%.IX Atari
-%.IX "Degas .pi3"
-Produces a portable bitmap as output.
-\shead{SEE ALSO}
-pbmtopi3(1), pbm(5), pi1toppm(1), ppmtopi1(1)
-\shead{AUTHOR}
-\copyright 1988 by David Beckemeyer (bdt!david) and Diomidis D. Spinellis.
-% 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.
-%
-% end of input file: pi3topbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:44 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: picttoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: picttoppm.1
-%
-\phead{picttoppm}{1}{29 November 1991}{}{}
-
-%.IX picttoppm
-\shead{NAME}
-picttoppm - convert a Macintosh PICT file into a portable pixmap
-\shead{SYNOPSIS}
-{\bf picttoppm}
-{\rm [}{\bf -verbose}{\rm ]}
-{\rm [}{\bf -fullres}{\rm ]}
-{\rm [}{\bf -noheader}{\rm ]}
-{\rm [}{\it pictfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a PICT file (version 1 or 2) and outputs a portable pixmap.
-%.IX PICT
-%.IX Macintosh
-Useful as the first step in converting a scanned image to something
-that can be displayed on Unix.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf --fullres}}
-\item[{{\bf --fullres}}]
-Force any images in the PICT file to be output with at least their
-full resolution.  A PICT file may indicate that a contained
-image is to be scaled down before output.  This option forces images
-to retain their sizes and prevent information loss.
-\item[{{\bf --noheader}}]
-Do not skip the 512 byte header that is present on all PICT files.
-This is useful when you have PICT data that was not stored in
-the data fork of a PICT file.
-\item[{{\bf --verbose}}]
-Turns on verbose mode which prints a 
-a whole bunch of information that only
-{\it picttoppm}
-hackers really care about.
-\end{TPlist}
-
-\shead{BUGS}
-The PICT file format is a general drawing format.
-{\it picttoppm}
-only supports a small subset of its operations but is still very useful for
-files produced by scanning software.  In particular, text added to a
-scanned image will be silently ignored.
-\shead{SEE ALSO}
-Inside Macintosh volume 5,
-ppmtopict(1),
-ppm(5)
-\shead{AUTHOR}
-\copyright 1989 George Phillips (phillips@cs.ubc.ca).
-% Permission is granted to freely distribute this program in whole or in
-% part provided you don't make money off it, you don't pretend that you
-% wrote it and that this notice accompanies the code.
-%
-% George Phillips <phillips@cs.ubc.ca>
-% Department of Computer Science
-% University of British Columbia
-%
-% end of input file: picttoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:44 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pjtoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pjtoppm.1
-%
-\phead{pjtoppm}{1}{14 July 1991}{}{}
-
-%.IX pjtoppm
-\shead{NAME}
-pjtoppm - convert an HP PaintJet file to a portable pixmap
-\shead{SYNOPSIS}
-{\bf pjtoppm}
-{\rm [}{\it paintjet}{\rm ]}
-\shead{DESCRIPTION}
-Reads an HP PaintJet file as input and converts it into a portable pixmap.
-This was a quick hack to save some trees, and it only handles a small
-subset of the paintjet commands.
-In particular, it will only handle 
-enough commands to convert most raster image files.
-\shead{REFERENCES}
-HP PaintJet XL Color Graphics Printer User's Guide
-\shead{SEE ALSO}
-ppmtopj(1)
-\shead{AUTHOR}
-\copyright 1991 by Christos Zoulas.
-% 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.
-%
-% end of input file: pjtoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:19 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pktopbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pktopbm.1
-%
-\phead{pktopbm}{1}{6 August 1990}{}{}
-
-\shead{NAME}
-pktopbm - convert packed (PK) format font into portable bitmap(s)
-\shead{SYNOPSIS}
-pktopbm pkfile[.pk] [-c num] pbmfile ...
-\shead{DESCRIPTION}
-Reads a packed (PK) font file as input, and produces portable bitmaps as
-output. If the filename ``-'' is used for any
-of the filenames, the standard input stream (or standard output where
-appropriate) will be used.
-\shead{OPTIONS}
-\begin{IPlist}
-\IPitem{{-c\ num}}
-Sets the character number of the next bitmap written to num.
-\end{IPlist}
-
-\shead{SEE ALSO}
-pbmtopk(1), pbm(5)
-\shead{AUTHOR}
-Adapted from Tom Rokicki's pxtopk by Angus Duggan (ajcd@dcs.ed.ac.uk.
-
-%
-% end of input file: pktopbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:35 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnm.5
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnm.5
-%
-\phead{pnm}{5}{27 September 1991}{}{}
-
-\shead{NAME}
-pnm - portable anymap file format
-\shead{DESCRIPTION}
-The
-{\it pnm}
-programs operate on portable bitmaps, graymaps, and pixmaps, produced by the
-{\it pbm, pgm,}
-and
-{\it ppm}
-segments.  There is no file format associated with
-{\it pnm}
-itself.
-\shead{SEE ALSO}
-anytopnm(1), rasttopnm(1), tifftopnm(1), xwdtopnm(1),
-pnmtops(1), pnmtorast(1), pnmtotiff(1), pnmtoxwd(1),
-pnmarith(1), pnmcat(1), pnmconvol(1), pnmcrop(1), pnmcut(1),
-pnmdepth(1), pnmenlarge(1), pnmfile(1), pnmflip(1), pnmgamma(1),
-pnmindex(1), pnminvert(1), pnmmargin(1), pnmnoraw(1), pnmpaste(1),
-pnmrotate(1), pnmscale(1), pnmshear(1), pnmsmooth(1), pnmtile(1),
-ppm(5), pgm(5), pbm(5)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: pnm.5
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Mon Feb  7 08:47:49 1994
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmalias.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmalias.1
-%
-\phead{pnmalias}{1}{30 April 1992}{}{}
-
-\shead{NAME}
-pnmalias - antialias a portable anyumap.
-\shead{SYNOPSIS}
-{\bf pnmalias}
-{\rm [}{\bf -bgcolor}
-{\it color}{\rm ]}
-{\rm [}{\bf -fgcolor}
-{\it color}{\rm ]}
-{\rm [}{\bf -bonly}{\rm ]}
-{\rm [}{\bf -fonly}{\rm ]}
-{\rm [}{\bf -balias}{\rm ]}
-{\rm [}{\bf -falias}{\rm ]}
-{\rm [}{\bf -weight}
-{\it w}{\rm ]}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input, and applies anti-aliasing to background and
-foreground pixels.
-If the input file is a portable bitmap, the 
-output anti-aliased image is promoted to a graymap, and a message is printed
-informing the user of the change in format.
-\shead{OPTIONS}
-\par
-{\bf --bgcolor}
-{\it colorb,}
-{\bf --fgcolor}
-{\it colorf}
-\ind{1\parindent}set the background color to 
-{\it colorb,}
-and the foreground to color to
-{\it colorf.}
-Pixels with these values will be anti-aliased. by default,
-the background color is taken to be black, and foreground color
-is assumed to be white.  
-The colors can be specified in five ways:
-\begin{TPlist}{{\bf o}}
-\item[{{\bf o}}]
-A name, assuming
-that a pointer to an X11-style color names file was compiled in.
-\item[{{\bf o}}]
-An X11-style hexadecimal specifier: rgb:r/g/b, where r g and b are
-each 1- to 4-digit hexadecimal numbers.
-\item[{{\bf o}}]
-An X11-style decimal specifier: rgbi:r/g/b, where r g and b are
-floating point numbers between 0 and 1.
-\item[{{\bf o}}]
-For backwards compatibility, an old-X11-style hexadecimal
-number: \#rgb, \#rrggbb, \#rrrgggbbb, or \#rrrrggggbbbb.
-\item[{{\bf o}}]
-For backwards compatibility, a triplet of numbers
-separated by commas: r,g,b, where r g and b are
-floating point numbers between 0 and 1.
-(This style was added before MIT came up with the similar rgbi style.)
-\end{TPlist}
-\par\noindent
-Note that even when dealing with graymaps, background and foreground
-colors need to be specified in the fashion described above.
-In this case, background and foreground pixel values are taken to be the
-value of the red component for the given color.
-\ind{1\parindent}
-\par
-{\bf --bonly}{\rm ,}
-{\bf --fonly}
-\ind{1\parindent}Apply anti-aliasing only to background 
-{\rm (}{\bf --bonly}{\rm ),}
-or foreground
-{\rm (}{\bf --fonly}{\rm )}
-pixels.
-\ind{1\parindent}
-\par
-{\bf --balias}{\rm ,}
-{\bf --falias}
-\ind{1\parindent}Apply anti-aliasing to all pixels surrounding background
-{\rm (}{\bf --balias}{\rm ),}
-or foreground
-{\rm (}{\bf --falias}{\rm )}
-pixels.  By default, anti-aliasing takes place only among neighboring
-background and foreground pixels.
-\ind{1\parindent}
-\par
-{\bf --weight}
-{\it w}
-\ind{1\parindent}Use 
-{\it w}
-as the central weight for the aliasing filter.
-{\it W}
-must be a real number in the range
-0 $<$ 
-{\it w}
-$<$ 1.
-The lower the value of 
-{\it w}
-is, the ``blurrier'' the output image is.  The default is w = 1/3.
-\shead{SEE ALSO}
-pbmtext(1), pnmsmooth(1), pnm(5)
-\shead{AUTHOR}
-Copyright (C) 1992 by Alberto Accomazzi, Smithsonian Astrophysical Observatory.
-% 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.
-%
-% end of input file: pnmalias.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:17 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmarith.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmarith.1
-%
-\phead{pnmarith}{1}{26 August 1993}{}{}
-
-%.IX pnmarith
-\shead{NAME}
-pnmarith - perform arithmetic on two portable anymaps
-\shead{SYNOPSIS}
-{\bf pnmarith}
-{\bf -add}{\rm $|$}{\bf -subtract}{\rm $|$}{\bf -multiply}{\rm $|$}{\bf -difference}
-{\it pnmfile1 pnmfile2}
-\shead{DESCRIPTION}
-Reads two portable anymaps as input.
-Performs the specified arithmetic operation,
-and produces a portable anymap as output.
-The two input anymaps must be the same width and height.
-\par
-The arithmetic is performed between corresponding pixels in the two
-anymaps, as if maxval was 1.0, black was 0.0, and a linear scale in between.
-Results that fall outside of [0..1) are truncated.
-\par
-The operator
-{\it -difference}
-calculates the absolute value of
-{\it pnmarith -subtract pnmfile1 pnmfile2,}
-{\it i.e.}, no truncation is done.
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-pbmmask(1), pnmpaste(1), pnminvert(1), pnm(5)
-\shead{AUTHOR}
-\copyright 1989, 1991 by Jef Poskanzer..
-Lightly modified by Marcel Wijkstra (wijkstra@fwi.uva.nl).
-% 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.
-%
-% end of input file: pnmarith.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:17 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmcat.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmcat.1
-%
-\phead{pnmcat}{1}{12 March 1989}{}{}
-
-%.IX pnmcat
-\shead{NAME}
-pnmcat - concatenate portable anymaps
-\shead{SYNOPSIS}
-{\bf pnmcat}
-{\rm [}{\bf -white}{\rm $|$}{\bf -black}{\rm ]}
-{\bf -leftright}{\rm $|$}{\bf -lr}
-{\rm [}{\bf -jtop}{\rm $|$}{\bf -jbottom}{\rm ]}
-{\it pnmfile pnmfile}
-{\rm ...}
-\nwl
-{\bf pnmcat}
-{\rm [}{\bf -white}{\rm $|$}{\bf -black}{\rm ]}
-{\bf -topbottom}{\rm $|$}{\bf -tb}
-{\rm [}{\bf -jleft}{\rm $|$}{\bf -jright}{\rm ]}
-{\it pnmfile pnmfile}
-{\rm ...}
-\shead{DESCRIPTION}
-Reads portable anymaps as input.
-Concatenates them either left to right or top to bottom, and produces a
-portable anymap as output.
-%.IX concatenation
-\shead{OPTIONS}
-\par
-If the anymaps are not all the same height (left-right) or width (top-bottom),
-the smaller ones have to be justified with the largest.
-By default, they get centered, but you can specify one side or the other
-with one of the -j* flags.
-So,
-{\bf -topbottom -jleft}
-would stack the anymaps on top of each other, flush with the left edge.
-\par
-The
-{\bf -white}
-and
-{\bf -black}
-flags specify what color to use to fill in the extra space
-when doing this justification.
-If neither is specified, the program makes a guess.
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-pnm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pnmcat.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:31 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmcomp.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmcomp.1
-%
-\phead{pnmcomp}{1}{21 February 1989}{}{}
-
-%.IX pnmcomp
-\shead{NAME}
-pnmcomp - composite two portable anymap files together
-\shead{SYNOPSIS}
-{\bf pnmcomp}
-{\rm [}{\it -invert}{\rm ]}
-{\rm [}{\it -xoff}{\rm N}{\it ]}
-{\rm [}{\it -yoff}{\rm N}{\it ]}
-{\rm [}{\it -alpha}{\rm pgmfile}{\it ]}
-{\rm overlay}
-{\rm [}{\it pnm-input}{\rm ]}
-{\rm [}{\it pnm-output}{\rm ]}
-\shead{DESCRIPTION}
-Reads in a portable any map image and put a overlay upon it, with optional
-alpha mask.  The 
-{\it -alpha pgmfile}
-allows you to also add an alpha mask file to the compositing process, the
-range of max and min can be swapped by using the
-{\it -invert}
-option.
-The
-{\it -xoff}
-and
-{\it -yoff}
-arguments can be negative, allowing you to shift the overlay off the
-top corner of the screen.
-\shead{SEE ALSO}
-pnm(5)
-\shead{AUTHOR}
-\copyright 1992 by David Koblas (koblas@mips.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.
-%
-% end of input file: pnmcomp.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:18 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmconvol.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmconvol.1
-%
-\phead{pnmconvol}{1}{13 January 1991}{}{}
-
-%.IX pnmconvol
-\shead{NAME}
-pnmconvol - general MxN convolution on a portable anymap
-\shead{SYNOPSIS}
-{\bf pnmconvol}
-{\it convolutionfile}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads two portable anymaps as input.
-Convolves the second using the first,
-and writes a portable anymap as output.
-%.IX convolution
-\par
-Convolution means replacing each pixel with a weighted average of the
-nearby pixels.  The weights and the area to average are determined by
-the convolution matrix.
-The unsigned numbers in the convolution file are offset by -maxval/2 to
-make signed numbers, and then normalized, so the actual values in the
-convolution file are only relative.
-\par
-Here is a sample convolution file;
-it does a simple average of the nine immediate neighbors, resulting
-in a smoothed image:
-\nofill
-    P2
-    3 3
-    18
-    10 10 10
-    10 10 10
-    10 10 10
-\fill
-\par
-To see how this works, do the above-mentioned offset: 10 - 18/2 gives 1.
-The possible range of values is from 0 to 18, and after the offset
-that's -9 to 9.  The normalization step makes the range -1 to 1, and
-the values get scaled correspondingly so they become 1/9 - exactly what
-you want.
-The equivalent matrix for 5x5 smoothing would have maxval 50 and be
-filled with 26.
-\par
-The convolution file will usually be a graymap,
-so that the same convolution gets applied to each color component.
-However, if you want to use a pixmap and do a different convolution to
-different colors, you can certainly do that.
-\shead{SEE ALSO}
-pnmsmooth(1), pnm(5)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: pnmconvol.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:18 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmcrop.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmcrop.1
-%
-\phead{pnmcrop}{1}{25 February 1989}{}{}
-
-%.IX pnmcrop
-\shead{NAME}
-pnmcrop - crop a portable anymap
-\shead{SYNOPSIS}
-{\bf pnmcrop}
-{\rm [}{\bf -white}{\rm $|$}{\bf -black}{\rm ]}
-{\rm [}{\bf -left}{\rm ]}
-{\rm [}{\bf -right}{\rm ]}
-{\rm [}{\bf -top}{\rm ]}
-{\rm [}{\bf -bottom}{\rm ]}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Removes edges that are the background color,
-and produces a portable anymap as output.
-%.IX cropping
-\shead{OPTIONS}
-\par
-By default, it makes a guess as to what the background color is.
-You can override the default with the
-{\bf -white}
-and
-{\bf -black}
-flags.
-\par
-The options
-{\bf -left, -right, -top}
-and
-{\bf -bottom}
-restrict cropping to the sides specified. The default is to crop all sides of
-the image.
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-pnmcut(1), pnm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pnmcrop.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:19 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmcut.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmcut.1
-%
-\phead{pnmcut}{1}{21 February 1989}{}{}
-
-%.IX pnmcut
-\shead{NAME}
-pnmcut - cut a rectangle out of a portable anymap
-\shead{SYNOPSIS}
-{\bf pnmcut}
-{\it x y width height}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Extracts the specified rectangle,
-and produces a portable anymap as output.
-%.IX cut
-The
-{\it x}
-and
-{\it y}
-can be negative, in which case they are interpreted
-relative to the right and bottom of the anymap, respectively.
-\shead{SEE ALSO}
-pnm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pnmcut.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:20 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmdepth.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmdepth.1
-%
-\phead{pnmdepth}{1}{12 January 1991}{}{}
-
-%.IX pnmdepth
-\shead{NAME}
-pnmdepth - change the maxval in a portable anymap
-\shead{SYNOPSIS}
-{\bf pnmdepth}
-{\it newmaxval}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Scales all the pixel values, and writes out the image with the new maxval.
-Scaling the colors down to a smaller maxval will result in some loss
-of information.
-\par
-Be careful of off-by-one errors when choosing the new maxval.
-For instance, if you want the color values to be five bits wide,
-use a maxval of 31, not 32.
-\shead{SEE ALSO}
-pnm(5), ppmquant(1), ppmdither(1)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: pnmdepth.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:20 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmenlarge.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmenlarge.1
-%
-\phead{pnmenlarge}{1}{26 February 1989}{}{}
-
-%.IX pnmenlarge
-\shead{NAME}
-pnmenlarge - read a portable anymap and enlarge it N times
-\shead{SYNOPSIS}
-{\bf pnmenlarge}
-{\it N}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Replicates its pixels
-{\it N}
-times, and produces a portable anymap as output.
-%.IX enlarging
-\par
-{\it pnmenlarge}
-can only enlarge by integer factors.
-The slower but more general
-{\it pnmscale}
-%.IX pnmscale
-can enlarge or reduce by arbitrary
-factors, and
-{\it pbmreduce}
-%.IX pbmreduce
-can reduce by integer factors, but only for bitmaps.
-\par
-If you enlarge by a factor of 3 or more, you should probably add a
-{\it pnmsmooth}
-%.IX pnmsmooth
-step; otherwise, you can see the original pixels in the resulting image.
-\shead{SEE ALSO}
-pbmreduce(1), pnmscale(1), pnmsmooth(1), pnm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pnmenlarge.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:21 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmfile.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmfile.1
-%
-\phead{pnmfile}{1}{9 January 1991}{}{}
-
-%.IX pnmfile
-\shead{NAME}
-pnmfile - describe a portable anymap
-\shead{SYNOPSIS}
-{\bf pnmfile}
-{\rm [}{\it pnmfile}{\rm ]}
-{\rm ...}
-\shead{DESCRIPTION}
-Reads one or more portable anymaps as input.
-Writes out short descriptions of the image type, size, etc.
-This is mostly for use in shell scripts, so the format is not
-particularly pretty.
-\shead{SEE ALSO}
-pnm(5), file(1)
-\shead{AUTHOR}
-\copyright 1991 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.
-%
-% end of input file: pnmfile.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:21 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmflip.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmflip.1
-%
-\phead{pnmflip}{1}{25 July 1989}{}{}
-
-%.IX pnmflip
-\shead{NAME}
-pnmflip - perform one or more flip operations on a portable anymap
-\shead{SYNOPSIS}
-{\bf pnmflip}
-{\rm [}{\bf -leftright}{\rm $|$}{\bf -lr}{\rm ]}
-{\rm [}{\bf -topbottom}{\rm $|$}{\bf -tb}{\rm ]}
-{\rm [}{\bf -transpose}{\rm $|$}{\bf -xy}{\rm ]}
-{\rm [}{\bf -rotate90}{\rm $|$}{\bf -r90}{\rm $|$}{\bf -ccw}
-{\rm ]}
-{\rm [}{\bf -rotate270}{\rm $|$}{\bf -r270}{\rm $|$}{\bf -cw}
-{\rm ]}
-{\rm [}{\bf -rotate180}{\rm $|$}{\bf -r180}{\rm ]}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Performs one or more flip operations, in the order specified, and
-writes out a portable anymap.
-%.IX rotation
-%.IX reflection
-%.IX transposition
-\shead{OPTIONS}
-\par
-The flip operations available are: left for right
-{\rm (}{\bf -leftright}
-or
-{\bf -lr}{\rm );}
-top for bottom
-{\rm (}{\bf -topbottom}
-or
-{\bf -tb}{\rm );}
-and transposition
-{\rm (}{\bf -transpose}
-or
-{\bf -xy}{\rm ).}
-In addition, some canned concatenations are available:
-{\bf -rotate90}
-or
-{\bf -ccw}
-is equivalent to
-{\bf -transpose}
-{\bf -topbottom}{\rm ;}
-{\bf -rotate270}
-or
-{\bf -cw}
-is equivalent to
-{\bf -transpose}
-{\bf -leftright}{\rm ;}
-and
-{\bf -rotate180}
-is equivalent to
-{\bf -leftright}
-{\bf -topbottom}{\rm .}
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-pnmrotate(1), pnm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pnmflip.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:29 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmgamma.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmgamma.1
-%
-\phead{pnmgamma}{1}{12 January 1991}{}{}
-
-%.IX pnmgamma
-\shead{NAME}
-pnmgamma - perform gamma correction on a portable anymap
-\shead{SYNOPSIS}
-{\bf pnmgamma}
-{\it value}
-{\rm [}{\it pnmfile}{\rm ]}
-\nwl
-{\bf pnmgamma}
-{\it redvalue greenvalue bluevalue}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Performs gamma correction,
-and produces a portable anymap as output.
-%.IX "gamma correction"
-\par
-The arguments specify what gamma value(s) to use.
-A value of 1.0 leaves the image alone, less than one darkens it,
-and greater than one lightens it.
-\shead{SEE ALSO}
-pnm(5)
-\shead{AUTHOR}
-\copyright 1991 by Bill Davidson and 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.
-%
-% end of input file: pnmgamma.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:22 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnminvert.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnminvert.1
-%
-\phead{pnminvert}{1}{08 August 1989}{}{}
-
-%.IX pnminvert
-\shead{NAME}
-pnminvert - invert a portable anymap
-\shead{SYNOPSIS}
-{\bf pnminvert}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Inverts it black for white and produces a portable anymap as output.
-\shead{SEE ALSO}
-pnm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pnminvert.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:32 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmnlfilt.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmnlfilt.1
-%
-\phead{pnmnlfilt}{1}{5 February 1993}{}{}
-
-%.IX pnmnlfilt
-\shead{NAME}
-pnmnlfilt - non-linear filters: smooth, alpha trim mean, optimal
-estimation smoothing, edge enhancement.
-\shead{SYNOPSIS}
-{\bf pnmnlfilt}
-{\rm alpha}
-{\rm radius}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-%.IX smoothing
-%.IX dithering
-%.IX alpha trim
-%.IX mean filter
-%.IX median filter
-%.IX optimal estimation
-This is something of a swiss army knife filter. It has 3 distinct operating
-modes. In all of the modes each pixel in the image is examined and processed
-according to it and its surrounding pixels values. Rather than using the
-9 pixels in a 3x3 block, 7 hexagonal area samples are taken, the size of
-the hexagons being controlled by the radius parameter. A radius value of
-0.3333 means that the 7 hexagons exactly fit into the center pixel ({\it i.e.},
-there will be no filtering effect). A radius value of 1.0 means that
-the 7 hexagons exactly fit a 3x3 pixel array.
-\shead{Alpha trimmed mean filter.	(0.0 $<$= alpha $<$= 0.5)}
-\par
-The value of the center pixel will be
-replaced by the mean of the 7 hexagon values, but the 7 values are
-sorted by size and the top and bottom alpha portion of the 7 are
-excluded from the mean.  This implies that an alpha value of 0.0 gives
-the same sort of output as a normal convolution ({\it i.e.}, averaging or
-smoothing filter), where radius will determine the ``strength'' of the
-filter. A good value to start from for subtle filtering is alpha = 0.0, radius = 0.55
-For a more blatant effect, try alpha 0.0 and radius 1.0
-\par
-An alpha value of 0.5 will cause the median value of the
-7 hexagons to be used to replace the center pixel value. This sort
-of filter is good for eliminating ``pop'' or single pixel noise from
-an image without spreading the noise out or smudging features on
-the image. Judicious use of the radius parameter will fine tune the
-filtering. Intermediate values of alpha give effects somewhere
-between smoothing and ``pop'' noise reduction. For subtle filtering
-try starting with values of alpha = 0.4, radius = 0.6  For a more blatant
-effect try alpha = 0.5, radius = 1.0
-\shead{Optimal estimation smoothing. (1.0 $<$= alpha $<$= 2.0)}
-\par
-This type of filter applies a smoothing filter adaptively over the image.
-For each pixel the variance of the surrounding hexagon values is calculated,
-and the amount of smoothing is made inversely proportional to it. The idea
-is that if the variance is small then it is due to noise in the image, while
-if the variance is large, it is because of ``wanted'' image features. As usual
-the radius parameter controls the effective radius, but it probably advisable to
-leave the radius between 0.8 and 1.0 for the variance calculation to be meaningful.
-The alpha parameter sets the noise threshold, over which less smoothing will be done.
-This means that small values of alpha will give the most subtle filtering effect,
-while large values will tend to smooth all parts of the image. You could start
-with values like alpha = 1.2, radius = 1.0 and try increasing or decreasing the
-alpha parameter to get the desired effect. This type of filter is best for
-filtering out dithering noise in both bitmap and color images.
-\shead{Edge enhancement. (-0.1 $>$= alpha $>$= -0.9)}
-\par
-This is the opposite type of filter to the smoothing filter. It enhances
-edges. The alpha parameter controls the amount of edge enhancement, from
-subtle (-0.1) to blatant (-0.9). The radius parameter controls the effective
-radius as usual, but useful values are between 0.5 and 0.9. Try starting
-with values of alpha = 0.3, radius = 0.8
-\shead{Combination use.}
-\par
-The various modes of 
-{\bf pnmnlfilt}
-can be used one after the other to get the desired result. For instance to
-turn a monochrome dithered image into a grayscale image you could try
-one or two passes of the smoothing filter, followed by a pass of the optimal estimation
-filter, then some subtle edge enhancement. Note that using edge enhancement is
-only likely to be useful after one of the non-linear filters (alpha trimmed mean
-or optimal estimation filter), as edge enhancement is the direct opposite of
-smoothing.
-\par
-For reducing color quantization noise in images ({\it i.e.}, turning .gif files back into
-24 bit files) you could try a pass of the optimal estimation filter
-(alpha 1.2, radius 1.0), a pass of the median filter (alpha 0.5, radius 0.55),
-and possibly a pass of the edge enhancement filter.
-Several passes of the optimal estimation filter with declining alpha
-values are more effective than a single pass with a large alpha value.
-As usual, there is a tradeoff between filtering effectiveness and loosing
-detail. Experimentation is encouraged.
-\shead{References:}
-\par
-The alpha-trimmed mean filter is 
-based on the description in IEEE CG\&A May 1990 
-Page 23 by Mark E. Lee and Richard A. Redner,
-and has been enhanced to allow continuous alpha adjustment.
-\par
-The optimal estimation filter is taken from an article ``Converting Dithered
-Images Back to Gray Scale'' by Allen Stenger, Dr Dobb's Journal, November
-1992, and this article references ``Digital Image Enhancement and Noise Filtering by
-Use of Local Statistics'', Jong-Sen Lee, IEEE Transactions on Pattern Analysis and
-Machine Intelligence, March 1980.
-\par
-The edge enhancement details are from pgmenhance(1),
-which is taken from Philip R. Thompson's ``xim''
-program, which in turn took it from section 6 of ``Digital Halftones by
-Dot Diffusion'', D. E. Knuth, ACM Transaction on Graphics Vol. 6, No. 4,
-October 1987, which in turn got it from two 1976 papers by J. F. Jarvis
-{\it et. al.}
-\shead{SEE ALSO}
-pgmenhance(1), pnmconvol(1), pnm(5)
-\shead{BUGS}
-Integers and tables may overflow if PPM\_MAXMAXVAL is greater than 255.
-\shead{AUTHOR}
-Graeme W. Gill    graeme@labtam.oz.au
-%
-% end of input file: pnmnlfilt.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:23 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmnoraw.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmnoraw.1
-%
-\phead{pnmnoraw}{1}{8 January 1991}{}{}
-
-%.IX pnmnoraw
-\shead{NAME}
-pnmnoraw - force a portable anymap into plain format
-\shead{SYNOPSIS}
-{\bf pnmnoraw}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Writes it out in plain (non-raw) format.
-This is fairly useless if you haven't defined the PBMPLUS\_RAWBITS
-compile-time option.
-%.IX RAWBITS
-\shead{SEE ALSO}
-pnm(5)
-\shead{AUTHOR}
-\copyright 1991 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.
-%
-% end of input file: pnmnoraw.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:33 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmpad.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmpad.1
-%
-\phead{pnmpad}{12 Dec 1990}{}{}{}
-
-\shead{NAME}
-pnmpad - add borders to portable anymap
-\shead{SYNOPSIS}
-pnmpad [-white$|$-black] [-l\#] [-r\#] [-t\#] [-b\#] [pnmfile]
-\shead{DESCRIPTION}
-Reads a portable anymap as input. Outputs a portable anymap with extra
-borders of the sizes specified. The colour of the borders can be set to
-black or white (default black).
-
-\shead{SEE ALSO}
-pbmmake(1), pnmpaste(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1990 by Angus Duggan.
-\copyright 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.
-%
-% end of input file: pnmpad.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:23 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmpaste.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmpaste.1
-%
-\phead{pnmpaste}{1}{21 February 1991}{}{}
-
-%.IX pnmpaste
-\shead{NAME}
-pnmpaste - paste a rectangle into a portable anymap
-\shead{SYNOPSIS}
-{\bf pnmpaste}
-{\rm [}{\bf -replace}{\rm $|$}{\bf -or}{\rm $|$}{\bf -and}
-{\rm $|$}{\bf -xor}{\rm ]}
-{\it frompnmfile x y}
-{\rm [}{\it intopnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads two portable anymaps as input.
-Inserts the first anymap into the second at the specified location,
-and produces a portable anymap the same size as the second as output.
-%.IX paste
-If the second anymap is not specified, it is read from stdin.
-The
-{\it x}
-and
-{\it y}
-can be negative, in which case they are interpreted
-relative to the right and bottom of the anymap, respectively.
-\par
-This tool is most useful in combination with
-{\it pnmcut}{\rm .}
-%.IX pnmcut
-For instance, if you want to edit a small segment of a large
-image, and your image editor cannot edit the
-large image, you can cut out the segment you are interested in,
-edit it, and then paste it back in.
-\par
-Another useful companion tool is
-{\it pbmmask}{\rm .}
-%.IX pnmmask
-%.OPTIONS
-\par
-The optional flag specifies the operation to use when doing the paste.
-The default is
-{\bf -replace}{\rm .}
-The other, logical operations are only allowed if both input images
-are bitmaps.
-%.IX "logical operations"
-These operations act as if white is TRUE and black is FALSE.
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-pnmcut(1), pnminvert(1), pnmarith(1), pnm(5), pbmmask(1)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: pnmpaste.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:29 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmrotate.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmrotate.1
-%
-\phead{pnmrotate}{1}{12 January 1991}{}{}
-
-%.IX pnmrotate
-\shead{NAME}
-pnmrotate - rotate a portable anymap by some angle
-\shead{SYNOPSIS}
-{\bf pnmrotate}
-{\rm [}{\bf -noantialias}{\rm ]}
-{\it angle}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Rotates it by the specified angle
-and produces a portable anymap as output.
-%.IX rotation
-If the input file is in color, the output will be too,
-otherwise it will be grayscale.
-The angle is in degrees (floating point), measured counter-clockwise.
-It can be negative, but it should be between -90 and 90.
-Also, for rotations greater than 45 degrees you may get better results
-if you first use
-{\it pnmflip}
-%.IX pnmflip
-to do a 90 degree rotation and then
-{\it pnmrotate}
-less than 45 degrees back the other direction
-\par
-The rotation algorithm is Alan Paeth's three-shear method.
-Each shear is implemented by looping over the source pixels and distributing
-fractions to each of the destination pixels.
-This has an ``anti-aliasing'' effect - it avoids jagged edges and similar
-artifacts.
-%.IX anti-aliasing
-However, it also means that the original colors or gray levels in the image
-are modified.
-If you need to keep precisely the same set of colors, you can use the
-{\bf -noantialias}
-flag.  This does the shearing by moving pixels without changing their values.
-If you want anti-aliasing and don't care about the precise colors, but
-still need a limited *number* of colors, you can run the result through
-{\it ppmquant}{\rm .}
-%.IX ppmquant
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{REFERENCES}
-``A Fast Algorithm for General Raster Rotation'' by Alan Paeth,
-Graphics Interface '86, pp. 77-81.
-\shead{SEE ALSO}
-pnmshear(1), pnmflip(1), pnm(5), ppmquant(1)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: pnmrotate.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:24 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmscale.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmscale.1
-%
-\phead{pnmscale}{1}{12 January 1991}{}{}
-
-%.IX pnmscale
-\shead{NAME}
-pnmscale - scale a portable anymap
-\shead{SYNOPSIS}
-{\bf pnmscale}
-{\it s}
-{\rm [}{\it pnmfile}{\rm ]}
-\nwl
-{\bf pnmscale}
-{\bf -xsize}{\rm $|$}{\bf -width}{\rm $|$}{\bf -ysize}{\rm $|$}
-{\bf -height}
-{\it s}
-{\rm [}{\it pnmfile}{\rm ]}
-\nwl
-{\bf pnmscale}
-{\bf -xscale}{\rm $|$}{\bf -yscale}
-{\it s}
-{\rm [}{\it pnmfile}{\rm ]}
-\nwl
-{\bf pnmscale}
-{\bf -xscale}{\rm $|$}{\bf -xsize}{\rm $|$}{\bf -width}
-{\it s}
-{\bf -yscale}{\rm $|$}{\bf -ysize}{\rm $|$}{\bf -height}
-{\it s}
-{\rm [}{\it pnmfile}{\rm ]}
-\nwl
-{\bf pnmscale -xysize}
-{\it x y}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Scales it by the specified factor or factors and produces a portable
-anymap as output.
-%.IX shrinking
-%.IX enlarging
-If the input file is in color, the output will be too,
-otherwise it will be grayscale.
-You can both enlarge (scale factor $>$ 1) and reduce (scale factor $<$ 1).
-\par
-You can specify one dimension as a pixel size, and the other dimension
-will be scaled correspondingly.
-\par
-You can specify one dimension as a scale, and the other dimension
-will not be scaled.
-\par
-You can specify different sizes or scales for each axis.
-\par
-Or, you can use the special
-{\bf -xysize}
-flag, which fits the image into
-the specified size without changing the aspect ratio.
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\par
-If you enlarge by a factor of 3 or more, you should probably add a
-{\it pnmsmooth}
-%.IX pnmsmooth
-step; otherwise, you can see the original pixels in the resulting image.
-\shead{SEE ALSO}
-pbmreduce(1), pnmenlarge(1), pnmsmooth(1), pnm(5)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: pnmscale.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:30 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmshear.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmshear.1
-%
-\phead{pnmshear}{1}{12 January 1991}{}{}
-
-%.IX pnmshear
-\shead{NAME}
-pnmshear - shear a portable anymap by some angle
-\shead{SYNOPSIS}
-{\bf pnmshear}
-{\rm [}{\bf -noantialias}{\rm ]}
-{\it angle}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Shears it by the specified angle and produces a portable
-anymap as output.
-%.IX shearing
-If the input file is in color, the output will be too,
-otherwise it will be grayscale.
-The angle is in degrees (floating point), and measures this:
-\nofill
-    +-------+  +-------+
-    $|$       $|$  $|$\bs        \bs 
-    $|$  OLD  $|$  $|$ \bs   NEW  \bs 
-    $|$       $|$  $|$an\bs        \bs 
-    +-------+  $|$gle+-------+
-\fill
-If the angle is negative, it shears the other way:
-\nofill
-    +-------+  $|$-an+-------+
-    $|$       $|$  $|$gl/       /
-    $|$  OLD  $|$  $|$e/  NEW  /
-    $|$       $|$  $|$/       /
-    +-------+  +-------+
-\fill
-The angle should not get too close to 90 or -90, or the resulting
-anymap will be unreasonably wide.
-\par
-The shearing is implemented by looping over the source pixels and distributing
-fractions to each of the destination pixels.
-This has an ``anti-aliasing'' effect - it avoids jagged edges and similar
-artifacts.
-%.IX anti-aliasing
-However, it also means that the original colors or gray levels in the image
-are modified.
-If you need to keep precisely the same set of colors, you can use
-the
-{\bf -noantialias}
-flag.  This does the shearing by moving pixels without changing their values.
-If you want anti-aliasing and don't care about the precise colors, but
-still need a limited *number* of colors, you can run the result through
-{\it ppmquant}{\rm .}
-%.IX ppmquant
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-pnmrotate(1), pnmflip(1), pnm(5), ppmquant(1)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: pnmshear.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:24 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmtile.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmtile.1
-%
-\phead{pnmtile}{1}{13 May 1989}{}{}
-
-%.IX pnmtile
-\shead{NAME}
-pnmtile - replicate a portable anymap into a specified size
-\shead{SYNOPSIS}
-{\bf pnmtile}
-{\it width height}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Replicates it until it is the specified size,
-and produces a portable anymap as output.
-%.IX tiling
-\shead{SEE ALSO}
-pnm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: pnmtile.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Mon Feb  7 08:49:06 1994
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmtofits.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmtofits.1
-%
-\phead{pnmtofits}{1}{5 Dec 1992}{}{}
-\shead{NAME}
-pnmtofits - convert a portable anymap into FITS format
-\shead{SYNOPSIS}
-{\bf pnmtofits}
-{\rm [}{\bf --max}
-{\it f}{\rm ]}
-{\rm [}{\bf --min}
-{\it f}{\rm ]}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Produces a FITS (Flexible Image Transport System) file as output.
-The resolution of the output file is either 8 bits/pixel,
-or 16 bits/pixel, depending on the value of maxval in the input file.
-If the input file is a portable bitmap or a portable graymap, the output file
-consists of a single plane image (NAXIS = 2). If instead the input file is
-a portable pixmap, the output file will consist of a three-plane image
-(NAXIS = 3, NAXIS3 = 3).
-A full description of the FITS format
-can be found in Astronomy \& Astrophysics Supplement Series 44 (1981), page 363.
-\shead{OPTIONS}
-\par
-Flags 
-{\bf --min}
-and 
-{\bf --max}
-can be used to set DATAMAX, DATAMIN, BSCALE and BZERO in the FITS
-header, but do not cause the data to be rescaled.
-\shead{SEE ALSO}
-fitstopnm(1), pgm(5)
-\shead{AUTHOR}
-Copyright (C) 1989 by Wilson H. Bent (whb@hoh-2.att.com), with
-modifications
-by Alberto Accomazzi (alberto@cfa.harvard.edu).
-% 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.
-%
-% end of input file: pnmtofits.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:25 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmtops.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmtops.1
-%
-\phead{pnmtops}{1}{26 October 1991}{}{}
-
-%.IX pnmtops
-\shead{NAME}
-pnmtops - convert portable anymap to PostScript
-\shead{SYNOPSIS}
-{\bf pnmtops}
-{\rm [}{\bf -scale}
-{\it s}{\rm ]}
-{\rm [}{\bf -turn}{\rm $|$}{\bf -noturn}{\rm ]}
-{\rm [}{\bf -rle}{\rm $|$}{\bf -runlength}{\rm ]}
-{\rm [}{\bf -dpi}
-{\it n}{\rm ]}
-{\rm [}{\bf -width}
-{\it n}{\rm ]}
-{\rm [}{\bf -height}
-{\it n}{\rm ]}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Produces Encapsulated PostScript as output.
-%.IX PostScript
-\par
-If the input file is in color (PPM), a color PostScript file gets
-written.
-Some PostScript interpreters can't handle color PostScript.
-If you have one of these you will need to run your image through
-{\it ppmtopgm}
-first.
-\par
-Note that there is no pstopnm
-tool - this transformation is one-way, because a pstopnm tool would
-be a full-fledged PostScript interpreter, which is beyond the scope
-of this package.
-However, see the
-{\it psidtopgm}
-tool, which can read grayscale non-runlength PostScript image data.
-Also, if you're willing to install the fairly large GhostScript package,
-it comes with a pstoppm script.
-\shead{OPTIONS}
-\par
-The
-{\bf -scale}
-flag controls the scale of the result.  The default scale is 1,
-which on a 300 dpi printer such as the Apple LaserWriter makes
-the output look about the same size as the input would if it was displayed
-on a typical 72 dpi screen.
-To get one PNM pixel per 300 dpi printer pixel, use ``-scale 0.25''.
-\par
-The
-{\bf -turn}
-and
-{\bf -noturn}
-flags control whether the image gets turned 90 degrees.
-Normally, if an image is wider than it is tall, it gets turned
-automatically to better fit the page.
-If the
-{\bf -turn}
-flag is specified, it will be turned no matter what its shape; and if the
-{\bf -noturn}
-flag is specified, it will
-{\it not}
-be turned no matter what its shape.
-\par
-The
-{\bf -rle}
-or
-{\bf -runlength}
-flag specifies run-length compression.  This may save
-time if the host-to-printer link is slow; but normally the printer's processing
-time dominates, so
-{\bf -rle}
-makes things slower.
-\par
-The
-{\bf -dpi}
-flag lets you specify the dots per inch of your output device.
-The default is 300 dpi.
-In theory PostScript is device-independent and you don't have to
-worry about this, but in practice its raster rendering can have
-unsightly bands if the device pixels and the image pixels aren't
-in sync.
-\par
-The
-{\bf -width}
-and
-{\bf -height}
-flags let you specify the size of the page.
-The default is 8.5 inches by 11 inches.
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-pnm(5), psidtopgm(1)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: pnmtops.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:26 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmtorast.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmtorast.1
-%
-\phead{pnmtorast}{1}{12 January 1991}{}{}
-
-%.IX pnmtorast
-\shead{NAME}
-pnmtorast - convert a portable pixmap into a Sun rasterfile
-\shead{SYNOPSIS}
-{\bf pnmtorast}
-{\rm [}{\bf -standard}{\rm $|$}{\bf -rle}{\rm ]}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces a Sun rasterfile as output.
-%.IX Sun
-%.IX rasterfile
-\par
-Color values in Sun rasterfiles are eight bits wide, so
-{\it pnmtorast}
-will automatically scale colors to have a maxval of 255.
-An extra
-{\it pnmdepth}
-step is not necessary.
-\shead{OPTIONS}
-\par
-The
-{\bf -standard}
-flag forces the result to be in RT\_STANDARD form; the
-{\bf -rle}
-flag, RT\_BYTE\_ENCODED, which is smaller but, well, less standard.
-The default is
-{\bf -rle}{\rm .}
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-rasttopnm(1), pnm(5)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: pnmtorast.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:31 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmtosir.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmtosir.1
-%
-\phead{pnmtosir}{1}{20 March 1991}{}{}
-
-\shead{NAME}
-pnmtosir - convert a portable anymap into a Solitaire format
-\shead{SYNOPSIS}
-{\bf pnmtosir}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Produces a Solitaire Image Recorder format.
-\par
-pnmtosir produces an MGI TYPE 17 file for
-{\it pbm}
-and
-{\it pgm}
-files.
-For
-{\it ppm}{\rm ,}
-it writes a MGI TYPE 11 file.
-\shead{SEE ALSO}
-sirtopnm(1), pnm(5)
-\shead{BUGS}
-
-\shead{AUTHOR}
-\copyright 1991 by Marvin Landis.
-% 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.
-
-%
-% end of input file: pnmtosir.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:36 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmtotiff.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmtotiff.1
-%
-\phead{pnmtotiff}{1}{13 January 1991}{}{}
-
-%.IX pnmtotiff
-\shead{NAME}
-pnmtotiff - convert a a portable anymap into a TIFF file
-\shead{SYNOPSIS}
-{\bf pnmtotiff}
-{\rm [}{\bf -none}{\rm $|$}{\bf -packbits}{\rm $|$}
-{\bf -lzw}{\rm $|$}{\bf -g3}{\rm $|$}{\bf -g4}{\rm ]}
-{\rm [}{\bf -2d}{\rm ]}
-{\rm [}{\bf -fill}{\rm ]}
-{\rm [}{\bf -predictor}
-{\it n}{\rm ]}
-{\rm [}{\bf -msb2lsb}{\rm $|$}{\bf -lsb2msb}{\rm ]}
-{\rm [}{\bf -rowsperstrip}
-{\it n}{\rm ]}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Produces a TIFF file as output.
-%.IX TIFF
-\shead{OPTIONS}
-\par
-By default,
-{\it pnmtotiff}
-creates a TIFF file with LZW compression.
-This is your best bet most of the time.
-However, some TIFF readers can't deal with it.
-If you want to try another compression scheme or tweak some of the
-other even more obscure output options, there are a number of
-flags to play with.
-\par
-The
-{\bf -none}{\rm ,}
-{\bf -packbits}{\rm ,}
-{\bf -lzw}{\rm ,}
-{\bf -g3}{\rm ,}
-and
-{\bf -g4}
-options are used to override the default and set the compression
-scheme used in creating the output file.  The CCITT Group 3 and Group
-4 compression algorithms can only be used with bilevel data.  The
-{\bf -2d}
-and
-{\bf -fill}
-options are meaningful only with Group 3 compression:
-{\bf -2d}
-requests 2-dimensional encoding, while
-{\bf -fill}
-requests that each encoded scanline be zero-filled to a byte boundry.
-The
-{\bf -predictor}
-option is only meaningful with LZW compression: a predictor value of 2
-causes each scanline of the output image to undergo horizontal
-differencing before it is encoded; a value of 1 forces each scanline
-to be encoded without differencing.
-\rm
-By default,
-{\it pnmtotiff}
-creates a TIFF file with msb-to-lsb fill order.
-The
-{\bf -msb2lsb}
-and
-{\bf -lsb2msb}
-options are used to override the default and set the fill order used
-in creating the file.
-\rm
-The
-{\bf -rowsperstrip}
-option can be used to set the number of rows (scanlines) in each
-strip of data in the output file.  By default, the output file has
-the number of rows per strip set to a value that will ensure each
-strip is no more than 8 kilobytes long.
-\shead{BUGS}
-This program is not self-contained.  To use it you must fetch the
-TIFF Software package listed in the OTHER.SYSTEMS file and configure
-PBMPLUS to use libtiff.  See PBMPLUS's Makefile for details on this
-configuration.
-\shead{SEE ALSO}
-tifftopnm(1), pnm(5)
-\shead{AUTHOR}
-Derived by Jef Poskanzer from ras2tiff.c, which is
-\copyright 1990 by Sun Microsystems, Inc.\hfil\break
-Author: Patrick J. Naughton (naughton@wind.sun.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 file is provided AS IS with no warranties of any kind.  The author
-% shall have no liability with respect to the infringement of copyrights,
-% trade secrets or any patents by this file or any part thereof.  In no
-% event will the author be liable for any lost revenue or profits or
-% other special, indirect and consequential damages.
-%
-% end of input file: pnmtotiff.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:26 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: pnmtoxwd.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: pnmtoxwd.1
-%
-\phead{pnmtoxwd}{1}{24 September 1991}{}{}
-
-%.IX pnmtoxwd
-\shead{NAME}
-pnmtoxwd - convert a portable anymap into an X11 window dump
-\shead{SYNOPSIS}
-{\bf pnmtoxwd}
-{\rm [}{\bf -pseudodepth}
-{\it n}{\rm ]}
-{\rm [}{\bf -directcolor}{\rm ]}
-{\rm [}{\it pnmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable anymap as input.
-Produces an X11 window dump as output.
-%.IX XWD
-%.IX "X window system"
-This window dump can be displayed using the xwud tool.
-\par
-Normally, pnmtoxwd produces a StaticGray dump file for
-{\it pbm}
-and
-{\it pgm}
-files.
-For
-{\it ppm}{\rm ,}
-it writes a PseudoColor dump file if there are up
-to 256 colors in the input, and a DirectColor dump file otherwise.
-The
-{\bf -directcolor}
-flag can be used to force a DirectColor dump.
-And the
-{\bf -pseudodepth}
-flag can be used to change the depth of PseudoColor dumps from the default
-of 8 bits / 256 colors.
-\shead{SEE ALSO}
-xwdtopnm(1), pnm(5), xwud(1)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: pnmtoxwd.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:13 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppm.5
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppm.5
-%
-\phead{ppm}{5}{27 September 1991}{}{}
-
-\shead{NAME}
-ppm - portable pixmap file format
-\shead{DESCRIPTION}
-The portable pixmap format is a lowest common denominator color image
-file format.
-%.IX "PPM file format"
-The definition is as follows:
-\begin{IPlist}
-\IPitem{{-}}
-A ``magic number'' for identifying the file type.
-A ppm file's magic number is the two characters ``P3''.
-%.IX "magic numbers"
-\IPitem{{-}}
-Whitespace (blanks, TABs, CRs, LFs).
-\IPitem{{-}}
-A width, formatted as ASCII characters in decimal.
-\IPitem{{-}}
-Whitespace.
-\IPitem{{-}}
-A height, again in ASCII decimal.
-\IPitem{{-}}
-Whitespace.
-\IPitem{{-}}
-The maximum color-component value, again in ASCII decimal.
-\IPitem{{-}}
-Whitespace.
-\IPitem{{-}}
-Width * height pixels, each three ASCII decimal values between 0 and the
-specified maximum value, starting at the top-left
-corner of the pixmap, proceeding in normal English reading order.
-The three values for each pixel represent red, green, and blue, respectively;
-a value of 0 means that color is off, and the maximum value means that color
-is maxxed out.
-\IPitem{{-}}
-Characters from a ``\#'' to the next end-of-line are ignored (comments).
-\IPitem{{-}}
-No line should be longer than 70 characters.
-\end{IPlist}
-
-\par
-Here is an example of a small pixmap in this format:
-\nofill
-P3
-\# feep.ppm
-4 4
-15
- 0  0  0    0  0  0    0  0  0   15  0 15
- 0  0  0    0 15  7    0  0  0    0  0  0
- 0  0  0    0  0  0    0 15  7    0  0  0
-15  0 15    0  0  0    0  0  0    0  0  0
-\fill
-\par
-Programs that read this format should be as lenient as possible,
-accepting anything that looks remotely like a pixmap.
-\par
-There is also a variant on the format, available
-by setting the RAWBITS option at compile time.  This variant is
-different in the following ways:
-%.IX RAWBITS
-\begin{IPlist}
-\IPitem{{-}}
-The ``magic number'' is ``P6'' instead of ``P3''.
-\IPitem{{-}}
-The pixel values are stored as plain bytes, instead of ASCII decimal.
-\IPitem{{-}}
-Whitespace is not allowed in the pixels area, and only a single character
-of whitespace (typically a newline) is allowed after the maxval.
-\IPitem{{-}}
-The files are smaller and many times faster to read and write.
-\end{IPlist}
-
-\par
-Note that this raw format can only be used for maxvals less than
-or equal to 255.
-If you use the
-{\it ppm}
-library and try to write a file with a larger maxval,
-it will automatically fall back on the slower but more general plain
-format.
-\shead{SEE ALSO}
-giftoppm(1), gouldtoppm(1), ilbmtoppm(1), imgtoppm(1), mtvtoppm(1),
-pcxtoppm(1), pgmtoppm(1), pi1toppm(1), picttoppm(1), pjtoppm(1), qrttoppm(1),
-rawtoppm(1), rgb3toppm(1), sldtoppm(1), spctoppm(1), sputoppm(1), tgatoppm(1),
-ximtoppm(1), xpmtoppm(1), yuvtoppm(1),
-ppmtoacad(1), ppmtogif(1), ppmtoicr(1), ppmtoilbm(1), ppmtopcx(1), ppmtopgm(1),
-ppmtopi1(1), ppmtopict(1), ppmtopj(1), ppmtopuzz(1), ppmtorgb3(1),
-ppmtosixel(1), ppmtotga(1), ppmtouil(1), ppmtoxpm(1), ppmtoyuv(1),
-ppmdither(1), ppmforge(1), ppmhist(1), ppmmake(1), ppmpat(1), ppmquant(1),
-ppmquantall(1), ppmrelief(1),
-pnm(5), pgm(5), pbm(5)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: ppm.5
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:08 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmbrighten.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmbrighten.1
-%
-\phead{ppmbrighten}{1}{20 Nov 1990}{}{}
-
-\shead{NAME}
-ppmbrighten - change an images Saturation and Value from an HSV map
-\shead{SYNOPSIS}
-ppmbrighten [-n] [-s $<$+- saturation$>$] [-v $<$+- value$>$] $<$ppmfile$>$
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Converts the image from RGB space to HSV space and changes
-the Value by $<$+- value$>$ as a percentage.
-Likewise with the Saturation.
-Doubling the Value would involve
-\par\vspace{1.0\baselineskip}
-ppmbrighten -v 100
-\par\vspace{1.0\baselineskip}
-to add 100 percent to the Value.
-\par
-The 'n' option normalizes the Value to exist between 0 and 1
-(normalized).
-\shead{SEE ALSO}
-pgmnorm(1), ppm(5)
-\shead{AUTHOR}
-\copyright 1990 by Brian Moffet.
-\copyright 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.
-\shead{NOTES}
-This program does not change the number of colors.
-%
-% end of input file: ppmbrighten.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Mon Feb  7 08:50:59 1994
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmchange.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmchange.1
-%
-\phead{ppmchange}{1}{3 December 1993}{}{}
-
-\shead{NAME}
-ppmchange - change all pixels of one color to another in a portable pixmap
-\shead{SYNOPSIS}
-{\bf ppmchange}
-{\it oldcolor newcolor [...]}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Changes all pixels of 
-{\it oldcolor}
-to 
-{\it newcolor}{\rm ,}
-leaving all others unchanged.
-Up to 256 colors may be replaced by specifying couples of colors on
-the command line.  
-\par
-The colors can be specified in five ways:
-\begin{TPlist}{o}
-\item[{o}]
-A name, assuming
-that a pointer to an X11-style color names file was compiled in.
-\item[{o}]
-An X11-style hexadecimal specifier: rgb:r/g/b, where r g and b are
-each 1- to 4-digit hexadecimal numbers.
-\item[{o}]
-An X11-style decimal specifier: rgbi:r/g/b, where r g and b are
-floating point numbers between 0 and 1.
-\item[{o}]
-For backwards compatibility, an old-X11-style hexadecimal
-number: \#rgb, \#rrggbb, \#rrrgggbbb, or \#rrrrggggbbbb.
-\item[{o}]
-For backwards compatibility, a triplet of numbers
-separated by commas: r,g,b, where r g and b are
-floating point numbers between 0 and 1.
-(This style was added before MIT came up with the similar rgbi style.)
-\end{TPlist}
-\shead{SEE ALSO}
-pgmtoppm(1), ppm(5)
-\shead{AUTHOR}
-Wilson H. Bent. Jr. (whb@usc.edu)
-with modifications by Alberto Accomazzi (alberto@cfa.harvard.edu)
-%
-% end of input file: ppmchange.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Mon Nov 29 13:33:23 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmdim.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmdim.1
-%
-\phead{ppmdim}{1}{16 November 1993}{}{}
-
-%.IX ppmdim
-\shead{NAME}
-ppmdim - dim a portable pixmap down to total blackness
-\shead{SYNOPSIS}
-ppmdim
-{\it dimfactor}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input. Diminishes its brightness by
-the specified dimfactor down to total blackness.
-The dimfactor may be in the range from 0.0 (total blackness,
-deep night, nada, null, nothing) to 1.0 (original picture's
-brightness).
-\par
-As
-{\it pnmgamma}
-does not do the brightness correction in the way I
-wanted it, this small program was written.
-\par
-ppmdim is similar to
-{\it ppmbrighten}
-, but not exactly the same.
-\shead{SEE ALSO}
-ppm(5), ppmflash(1), pnmgamma(1), ppmbrighten(1)
-\shead{AUTHOR}
-Copyright (C) 1993 by Frank Neumann
-% 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.
-%
-% end of input file: ppmdim.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:08 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmdist.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmdist.1
-%
-\phead{ppmdist}{1}{22 July 1992}{}{}
-
-%.IX ppmdist
-\shead{NAME}
-ppmdist - simplistic grayscale assignment for machine generated, color images
-\shead{SYNOPSIS}
-{\bf ppmdist}
-{\rm [}{\bf -intensity}{\rm $|$}{\bf -frequency}{\rm ]}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input, performs a simplistic grayscale
-assignment intended for use with grayscale or bitmap printers.
-
-Often conversion from ppm to pgm will yield an image with contrast too
-low for good printer output.  The program maximizes contrast between
-the gray levels output.
-
-A ppm input of n colors is read, and a pgm of n gray levels is written.
-The gray levels take on the values 0..n-1, while maxval takes on n-1.
-
-The mapping from color to stepped grayscale can be performed in order
-of input pixel intensity, or input pixel frequency (number of repetitions).
-\shead{OPTIONS}
-\begin{TPlist}{}
-\item[{}]
-{\bf -frequency}
-Sort input colors by the number of times a color appears in the input,
-before mapping to evenly distributed graylevels of output.
-{\bf -intensity}
-Sort input colors by their grayscale intensity, before mapping to evenly
-distributed graylevels of output.  This is the default.
-\end{TPlist}
-
-\shead{BUGS}
-Helpful only for images with a very small number of colors.
-Perhaps should have been an option to ppmtopgm(1).
-\shead{SEE ALSO}
-ppmtopgm(1), ppmhist(1), ppm(5)
-\shead{AUTHOR}
-\copyright 1993 by Dan Stromberg.
-% 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.
-%
-% end of input file: ppmdist.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:45 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmdither.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmdither.1
-%
-\phead{ppmdither}{1}{14 July 1991}{}{}
-
-%.IX ppmdither
-\shead{NAME}
-ppmdither - ordered dither for color images
-\shead{SYNOPSIS}
-{\bf ppmdither}
-{\rm [}{\bf -dim}
-{\it dimension}{\rm ]}
-{\rm [}{\bf -red}
-{\it shades}{\rm ]}
-{\rm [}{\bf -green}
-{\it shades}{\rm ]}
-{\rm [}{\bf -blue}
-{\it shades}{\rm ]}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input, and applies dithering to it to reduce
-the number of colors used down to the specified number of shades for
-each primary.
-The default number of shades is red=5, green=9, blue=5, for
-a total of 225 colors.
-To convert the image to a binary rgb format 
-suitable for color printers, use -red 2 -green 2 -blue 2.
-The maximum
-number of colors that can be used is 256 and can be computed as the 
-product of the number of red, green and blue shades.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -dim}{\it \ dimension}
-}
-\item[{{\bf -dim}{\it \ dimension}
-}]
-The size of the dithering matrix.
-Must be a power of 2.
-\item[{{\bf -red}{\it \ shades}
-}]
-The number of red shades to be used; minimum of 2.
-\item[{{\bf -green}{\it \ shades}
-}]
-The number of green shades to be used; minimum of 2.
-\item[{{\bf -blue}{\it \ shades}
-}]
-The number of blue shades to be used; minimum of 2.
-\end{TPlist}
-
-\shead{SEE ALSO}
-pnmdepth(1), ppmquant(1), ppm(5)
-\shead{AUTHOR}
-\copyright 1991 by Christos Zoulas.
-% 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.
-%
-% end of input file: ppmdither.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Mon Nov 29 13:33:29 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmflash.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmflash.1
-%
-\phead{ppmflash}{1}{16 November 1993}{}{}
-
-%.IX ppmflash
-\shead{NAME}
-ppmflash - brighten a picture up to complete white-out
-\shead{SYNOPSIS}
-ppmflash 
-{\it flashfactor}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input. Increases its brightness by
-the specified flashfactor up to a total white-out image.
-The flashfactor may be in the range from 0.0 (original picture's
-brightness) to 1.0 (full white-out, The Second After).
-\par
-As
-{\it pnmgamma}
-does not do the brightness correction in the way I
-wanted it, this small program was written.
-\par
-This program is similar to
-{\it ppmbrighten}
-, but not exactly the same.
-\shead{SEE ALSO}
-ppm(5), ppmdim(1), pnmgamma(1), ppmbrighten(1)
-\shead{AUTHOR}
-Copyright (C) 1993 by Frank Neumann
-% 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.
-%
-% end of input file: ppmflash.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:46 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmforge.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmforge.1
-%
-\phead{ppmforge}{1}{25 October 1991}{}{}
-
-%.IX ppmforge
-%.IX fractals
-%.IX clouds
-%.IX planets
-%.IX stars
-\shead{NAME}
-ppmforge - fractal forgeries of clouds, planets, and starry skies
-\shead{SYNOPSIS}
-\raggedright
-%.nh
-{\bf ppmforge}
-{\rm [}{\bf -clouds}{\rm ]}
-'in +9n
-{\rm [}{\bf -night}{\rm ]}
-{\rm [}{\bf -dimension}
-{\it dimen}{\rm ]}
-{\rm [}{\bf -hour}
-{\it hour}{\rm ]}
-{\rm [}{\bf -inclination$|$-tilt}
-{\it angle}{\rm ]}
-{\rm [}{\bf -mesh}
-{\it size}{\rm ]}
-{\rm [}{\bf -power}
-{\it factor}{\rm ]}
-{\rm [}{\bf -glaciers}
-{\it level}{\rm ]}
-{\rm [}{\bf -ice}
-{\it level}{\rm ]}
-{\rm [}{\bf -saturation}
-{\it sat}{\rm ]}
-{\rm [}{\bf -seed}
-{\it seed}{\rm ]}
-{\rm [}{\bf -stars}
-{\it fraction}{\rm ]}
-{\rm [}{\bf -xsize$|$-width}
-{\it width}{\rm ]}
-{\rm [}{\bf -ysize$|$-height}
-{\it height}{\rm ]}
-\ind{-4.5em}
-%.ad
-%.hy
-\shead{DESCRIPTION}
-{\bf ppmforge}
-generates three kinds of ``random fractal forgeries,'' the term coined
-by Richard F. Voss of the IBM Thomas J. Watson Research Center for
-seemingly realistic pictures of natural objects generated by simple
-algorithms embodying randomness and fractal self-similarity.  The
-techniques used by
-{\bf ppmforge}
-are essentially those
-given by Voss[1], particularly the technique of spectral synthesis
-explained in more detail by Dietmar Saupe[2].
-\par
-The program generates two varieties of pictures: planets and clouds,
-which are just different renderings of data generated in an identical
-manner, illustrating the unity of the fractal structure of these very
-different objects.  A third type of picture, a starry sky, is
-synthesised directly from pseudorandom numbers.
-\par
-The generation of planets or clouds begins with the preparation of an
-array of random data in the frequency domain.  The size of this
-array, the ``mesh size,'' can be set with the
-{\bf -mesh}
-option; the larger the mesh the more realistic the pictures but the
-calculation time and memory requirement increases as the square of the
-mesh size.  The fractal dimension, which you can specify with the
-{\bf -dimension}
-option, determines the roughness of the terrain on the planet or the
-scale of detail in the clouds.  As the fractal dimension is increased,
-more high frequency components are added into the random mesh.
-\par
-Once the mesh is generated, an inverse two dimensional Fourier
-transform is performed upon it.  This converts the original random
-frequency domain data into spatial amplitudes.  We scale the real
-components that result from the Fourier transform into numbers from 0
-to 1 associated with each point on the mesh.  You can further
-modify this number by applying a ``power law scale'' to it with the
-{\bf -power}
-option.   Unity scale
-leaves the numbers unmodified; a power scale of 0.5 takes the square
-root of the numbers in the mesh, while a power scale of 3 replaces the
-numbers in the mesh with their cubes.  Power law scaling is best
-envisioned by thinking of the data as representing the elevation of
-terrain; powers less than 1 yield landscapes with vertical scarps that
-look like glacially-carved valleys; powers greater than one make
-fairy-castle spires (which require large mesh sizes and high
-resolution for best results).
-\par
-After these calculations, we have a array of the specified size
-containing numbers that range from 0 to 1.  The pixmaps are generated as
-follows:
-\begin{TPlist}{{\bf Clouds}}
-\item[{{\bf Clouds}}]
-A colour map is created that ranges from pure blue to white by
-increasing admixture (desaturation) of blue with white.  Numbers less
-than 0.5 are coloured blue, numbers between 0.5 and 1.0 are coloured
-with corresponding levels of white, with 1.0 being pure white.
-\item[{{\bf Planet}}]
-The mesh is projected onto a sphere.  Values less than 0.5 are treated
-as water and values between 0.5 and 1.0 as land.  The water areas are
-coloured based upon the water depth, and land based on its elevation.
-The random depth data are used to create clouds over the oceans.  An
-atmosphere approximately like the Earth's is simulated; its light
-absorption is calculated to create a blue cast around the limb of the
-planet.  A function that rises from 0 to 1 based on latitude is
-modulated by the local elevation to generate polar ice caps--high
-altitude terrain carries glaciers farther from the pole.  Based on the
-position of the star with respect to the observer, the apparent colour
-of each pixel of the planet is calculated by ray-tracing from the star
-to the planet to the observer and applying a lighting model that sums
-ambient light and diffuse reflection (for most planets ambient light
-is zero, as their primary star is the only source of illumination).
-Additional random data are used to generate stars around the planet.
-\item[{{\bf Night}}]
-A sequence of pseudorandom numbers is used to generate stars with a
-user specified density.
-\end{TPlist}
-
-\par
-Cloud pictures always contain 256 or fewer colours and may be
-displayed on most colour mapped devices without further processing.
-Planet pictures often contain tens of thousands of colours which
-must be compressed with
-{\bf ppmquant}
-or
-{\bf ppmdither}
-before encoding in a colour mapped format.  If the display resolution is
-high enough,
-{\bf ppmdither}
-generally produces better looking planets.
-{\bf ppmquant}
-tends to create discrete colour bands, particularly in the oceans,
-which are unrealistic and distracting.  The number of colours in starry
-sky pictures generated with the
-{\bf -night}
-option depends on the value specified for
-{\bf -saturation}{\rm .}
-Small values limit the colour temperature distribution of the stars
-and reduce the number of colours in the image.
-If the
-{\bf -saturation}
-is set to 0, none of the stars will be coloured and the resulting
-image will never contain more than 256 colours.
-Night sky pictures with many different star colours often look
-best when colour compressed by
-{\bf pnmdepth}
-rather than
-{\bf ppmquant}
-or
-{\bf ppmdither}{\rm .}
-Try
-{\it newmaxval}
-settings of 63, 31, or 15 with
-{\bf pnmdepth}
-to reduce the number of colours in the picture to 256 or fewer.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -clouds}}
-\item[{{\bf -clouds}}]
-Generate clouds.  A pixmap of fractal clouds is generated.  Selecting clouds
-sets the default for fractal dimension to 2.15 and power scale factor
-to 0.75.
-\item[{{\bf -dimension}{\it \ dimen}
-}]
-Sets the fractal dimension to the specified
-{\it dimen}{\rm ,}
-which may be any floating point value between 0 and 3.  Higher fractal
-dimensions create more ``chaotic'' images, which require higher
-resolution output and a larger FFT mesh size to look good.  If no
-dimension is specified, 2.4 is used when generating planets and 2.15
-for clouds.
-\item[{{\bf -glaciers}{\it \ level}
-}]
-The floating point
-{\it level}
-setting controls the extent to which terrain elevation causes ice to
-appear at lower latitudes.  The default value of 0.75 makes the polar
-caps extend toward the equator across high terrain and forms glaciers
-in the highest mountains, as on Earth.  Higher values make ice sheets
-that cover more and more of the land surface, simulating planets in the
-midst of an ice age.  Lower values tend to be boring, resulting in
-unrealistic geometrically-precise ice cap boundaries.
-\item[{{\bf -hour}{\it \ hour}
-}]
-When generating a planet,
-{\it hour}
-is used as the ``hour angle at the central meridian.''  If you specify
-{\bf -hour\ 12}{\rm ,}
-for example, the planet will be fully illuminated, corresponding to
-high noon at the longitude at the centre of the screen.  You can
-specify any floating point value between 0 and 24 for
-{\it hour}{\rm ,}
-but values which place most of the planet in darkness (0 to 4 and 20
-to 24) result in crescents which, while pretty, don't give you many
-illuminated pixels for the amount of computing that's required.  If no
-{\bf -hour}
-option is specified, a random hour angle is chosen, biased so that
-only 25\% of the images generated will be crescents.
-\item[{{\bf -ice}{\it \ level}
-}]
-Sets the extent of the polar ice caps to the given floating point
-{\it level}{\rm .}
-The default level of 0.4 produces ice caps similar to those of the Earth.
-Smaller values reduce the amount of ice, while larger
-{\bf -ice}
-settings create more prominent ice caps.  Sufficiently large values,
-such as 100 or more, in conjunction with small settings for
-{\bf -glaciers}
-(try 0.1) create ``ice balls'' like Europa.
-\item[{{\bf -inclination$|$-tilt}{\it \ angle}
-}]
-The inclination angle of the planet with regard to its primary star is
-set to
-{\it angle}{\rm ,}
-which can be any floating point value from -90 to 90.  The inclination
-angle can be thought of as specifying, in degrees, the ``season'' the
-planet is presently experiencing or, more precisely, the latitude at
-which the star transits the zenith at local noon.  If 0, the planet
-is at equinox; the star is directly overhead at the equator.
-Positive values represent summer in the northern hemisphere, negative
-values summer in the southern hemisphere.  The Earth's inclination
-angle, for example, is about 23.5 at the June solstice, 0 at the
-equinoxes in March and September, and -23.5 at the December solstice.
-If no inclination angle is specified, a random value between -21.6 and
-21.6 degrees is chosen.
-\item[{{\bf -mesh}{\it \ size}
-}]
-A mesh of
-{\it size}{\rm \ by\ }{\it size}
-will be used for the fast Fourier transform (FFT).  Note that memory
-requirements and computation speed increase as the square of
-{\it size}{\rm ;}
-if you double the mesh size, the program will use four times the
-memory and run four times as long.  The default mesh is 256x256, which
-produces reasonably good looking pictures while using half a megabyte
-for the 256x256 array of single precision complex numbers
-required by the FFT.  On machines with limited memory capacity, you
-may have to reduce the mesh size to avoid running out of RAM.
-Increasing the mesh size produces better looking pictures; the
-difference becomes particularly noticeable when generating high
-resolution images with relatively high fractal dimensions (between 2.2
-and 3).
-\item[{{\bf -night}}]
-A starry sky is generated.  The stars are created by the same algorithm
-used for the stars that surround planet pictures, but the output
-consists exclusively of stars.
-\item[{{\bf -power}{\it \ factor}
-}]
-Sets the ``power factor'' used to scale elevations synthesised from
-the FFT to
-{\it factor}{\rm ,}
-which can be any floating point number greater than zero.  If no
-factor is specified a default of 1.2 is used if a planet is being
-generated, or 0.75 if clouds are selected by the
-{\bf -clouds}
-option.  The result of the FFT image synthesis is an array of elevation
-values between 0 and 1.  A non-unity power factor exponentiates each
-of these elevations to the specified power.  For example, a power
-factor of 2 squares each value, while a power factor of 0.5 replaces
-each with its square root.  (Note that exponentiating values between 0
-and 1 yields values that remain within that range.)  Power factors
-less than 1 emphasise large-scale elevation changes at the expense of
-small variations.  Power factors greater than 1 increase the roughness
-of the terrain and, like high fractal dimensions, may require a larger
-FFT mesh size and/or higher screen resolution to look good.
-\item[{{\bf -saturation}{\it \ sat}
-}]
-Controls the degree of colour saturation of the stars that surround planet
-pictures and fill starry skies created with the
-{\bf -night}
-option.  The default value of 125 creates stars which resemble the sky
-as seen by the human eye from Earth's surface.  Stars are dim; only
-the brightest activate the cones in the human retina, causing colour
-to be perceived.  Higher values of
-{\it sat}
-approximate the appearance of stars from Earth orbit, where better
-dark adaptation, absence of skyglow, and the concentration of light
-from a given star onto a smaller area of the retina thanks to the lack
-of atmospheric turbulence enhances the perception of colour.  Values
-greater than 250 create ``science fiction'' skies that, while pretty,
-don't occur in this universe.
-\item[{\ }]
-Thanks to the inverse square law combined with Nature's love of
-mediocrity, there are many, many dim stars for every bright one.
-This population relationship is accurately reflected in the skies
-created by
-{\bf ppmforge}{\rm .}
-Dim, low mass stars live much longer than bright massive stars,
-consequently there are many reddish stars for every blue giant.  This
-relationship is preserved by
-{\bf ppmforge}{\rm .}
-You can reverse the proportion, simulating the sky as seen in a starburst
-galaxy, by specifying a negative
-{\it sat}
-value.
-\item[{{\bf -seed}{\it \ num}
-}]
-Sets the seed for the random number generator to the integer
-{\it num}{\rm .}
-The seed used to create each picture is displayed on standard output (unless
-suppressed with the
-{\bf -quiet}
-option).  Pictures generated with the same seed will be identical.  If no
-{\bf -seed}
-is specified, a random seed derived from the date and time will be
-chosen.  Specifying an explicit seed allows you to re-render a picture
-you particularly like at a higher resolution or with different viewing
-parameters.
-\item[{{\bf -stars}{\it \ fraction}
-}]
-Specifies the percentage of pixels, in tenths of a percent, which will
-appear as stars, either surrounding a planet or filling the entire
-frame if
-{\bf -night}
-is specified.  The default
-{\it fraction}
-is 100.
-\item[{{\bf -xsize$|$-width}{\it \ width}
-}]
-Sets the width of the generated image to
-{\it width}
-pixels.  The default width is 256 pixels.  Images must be at least as
-wide as they are high; if a width less than the height is specified,
-it will be increased to equal the height.  If you must have a long
-skinny pixmap, make a square one with
-{\bf ppmforge}{\rm ,}
-then use
-{\bf pnmcut}
-to extract a portion of the shape and size you require.
-\item[{{\bf -ysize$|$-height}{\it \ height}
-}]
-Sets the height of the generated image to
-{\it height}
-pixels.  The default height is 256 pixels.  If the height specified
-exceeds the width, the width will be increased to equal the height.
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{BUGS}
-\par
-The algorithms require the output pixmap to be at least as wide as it
-is high, and the width to be an even number of pixels.  These
-constraints are enforced by increasing the size of the requested
-pixmap if necessary.
-\par
-You may have to reduce the FFT mesh size on machines with 16 bit
-integers and segmented pointer architectures.
-\shead{SEE ALSO}
-{\bf pnmcut}{\rm (1),}
-{\bf pnmdepth}{\rm (1),}
-{\bf ppmdither}{\rm (1),}
-{\bf ppmquant}{\rm (1),}
-{\bf ppm}{\rm (5)}
-\begin{TPlist}{[1] }
-\item[{[1] }]
-Voss, Richard F., ``Random Fractal Forgeries,'' in Earnshaw
-{\it et. al.}, Fundamental Algorithms for Computer Graphics, Berlin:
-Springer-Verlag, 1985.
-\item[{[2]}]
-Peitgen, H.-O., and Saupe, D. eds., The Science Of Fractal Images,
-New York: Springer Verlag, 1988.
-%.ne 10
-\end{TPlist}
-
-\shead{AUTHOR}
-\ind{1\parindent}{\nofill
-    John Walker
-    Autodesk SA
-    Avenue des Champs-Montants 14b
-    CH-2074 MARIN
-    Suisse/Schweiz/Svizzera/Svizra/Switzerland
-\fill}
-\begin{TPlist}{Usenet:}
-\item[{Usenet:}]
-kelvin@Autodesk.com
-\item[{Fax:}]
-038/33 88 15
-\item[{Voice:}]
-038/33 76 33
-\end{TPlist}
-
-\par
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose and without fee is hereby granted,
-without any conditions or restrictions.  This software is provided ``as
-is'' without express or implied warranty.
-\par
-{\bf PLUGWARE!}
-If you like this kind of stuff, you may also enjoy ``James Gleick's
-Chaos--The Software'' for MS-DOS, available for \$59.95 from your
-local software store or directly from Autodesk, Inc., Attn: Science
-Series, 2320 Marinship Way, Sausalito, CA 94965, USA.  Telephone:
-(800) 688-2344 toll-free or, outside the U.S. (415) 332-2344 Ext
-4886.  Fax: (415) 289-4718.  ``Chaos--The Software'' includes a more
-comprehensive fractal forgery generator which creates
-three-dimensional landscapes as well as clouds and planets, plus five
-more modules which explore other aspects of Chaos.  The user guide of
-more than 200 pages includes an introduction by James Gleick and
-detailed explanations by Rudy Rucker of the mathematics and algorithms
-used by each program.
-%
-% end of input file: ppmforge.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:47 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmhist.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmhist.1
-%
-\phead{ppmhist}{1}{03 April 1989}{}{}
-
-%.IX ppmhist
-\shead{NAME}
-ppmhist - print a histogram of a portable pixmap
-\shead{SYNOPSIS}
-{\bf ppmhist}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Generates a histogram of the colors in the pixmap.
-\shead{SEE ALSO}
-ppm(5), pgmhist(1)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: ppmhist.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:48 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmmake.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmmake.1
-%
-\phead{ppmmake}{1}{24 September 1991}{}{}
-
-%.IX ppmmake
-\shead{NAME}
-ppmmake - create a pixmap of a specified size and color
-\shead{SYNOPSIS}
-{\bf ppmmake}
-{\it color width height}
-\shead{DESCRIPTION}
-Produces a portable pixmap of the specified color, width, and height.
-%.IX "generating pixmaps"
-\par
-The color can be specified in five ways:
-%.IX "specifying colors"
-\begin{TPlist}{o}
-\item[{o}]
-A name, assuming
-that a pointer to an X11-style color names file was compiled in.
-\item[{o}]
-An X11-style hexadecimal specifier: rgb:r/g/b, where r g and b are
-each 1- to 4-digit hexadecimal numbers.
-\item[{o}]
-An X11-style decimal specifier: rgbi:r/g/b, where r g and b are
-floating point numbers between 0 and 1.
-\item[{o}]
-For backwards compatibility, an old-X11-style hexadecimal
-number: \#rgb, \#rrggbb, \#rrrgggbbb, or \#rrrrggggbbbb.
-\item[{o}]
-For backwards compatibility, a triplet of numbers
-separated by commas: r,g,b, where r g and b are
-floating point numbers between 0 and 1.
-(This style was added before MIT came up with the similar rgbi style.)
-\end{TPlist}
-
-\shead{SEE ALSO}
-ppm(5), pbmmake(1)
-\shead{AUTHOR}
-\copyright 1991 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.
-%
-% end of input file: ppmmake.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Mon Nov 29 13:23:43 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmmix.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmmix.1
-%
-\phead{ppmmix}{1}{16 November 1993}{}{}
-
-%.IX ppmmix
-\shead{NAME}
-ppmmix - blend together two portable pixmaps
-\shead{SYNOPSIS}
-ppmmix 
-{\it fadefactor}
-{\it ppmfile1 ppmfile2}
-\shead{DESCRIPTION}
-Reads two portable pixmaps as input. Mixes them together using the
-specified fade factor. The fade factor may be in the range from 0.0
-(only ppmfile1's image data) to 1.0 (only ppmfile2's image data).
-Anything in between gains a smooth blend between the two images.
-\par
-The two pixmaps must have the same size.
-\shead{SEE ALSO}
-ppm(5)
-\shead{AUTHOR}
-\copyright 1993 by Frank Neumann.
-% Permission to use, , 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.
-%
-% end of input file: ppmmix.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:06 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmpat.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmpat.1
-%
-\phead{ppmpat}{1}{04 September 1989}{}{}
-
-%.IX ppmpat
-\shead{NAME}
-ppmpat - make a pretty pixmap
-\shead{SYNOPSIS}
-{\bf ppmpat}
-{\bf -gingham2}{\rm $|$}{\bf -g2}{\rm $|$}{\bf -gingham3}{\rm $|$}
-{\bf -g3}{\rm $|$}{\bf -madras}{\rm $|$}{\bf -tartan}{\rm $|$}
-{\bf -poles}{\rm $|$}{\bf -squig}{\rm $|$}{\bf -camo}{\rm $|$}
-{\bf -anticamo}
-{\it width height}
-\shead{DESCRIPTION}
-Produces a portable pixmap of the specified width and height,
-with a pattern in it.
-%.IX "generating pixmaps"
-\par
-This program is mainly to demonstrate use of the ppmdraw routines, a
-simple but powerful drawing library.
-See the ppmdraw.h include file for more info on using these routines.
-Still, some of the patterns can be rather pretty.
-If you have a color workstation, something like
-{\bf ppmpat\ -squig\ 300\ 300 $|$ ppmquant\ 128}
-should generate a nice background.
-\shead{OPTIONS}
-\par
-The different flags specify various different pattern types:
-\begin{TPlist}{{\bf -gingham2}}
-\item[{{\bf -gingham2}}]
-A gingham check pattern.  Can be tiled.
-\item[{{\bf -gingham3}}]
-A slightly more complicated gingham.  Can be tiled.
-\item[{{\bf -madras}}]
-A madras plaid.  Can be tiled.
-\item[{{\bf -tartan}}]
-A tartan plaid.  Can be tiled.
-\item[{{\bf -poles}}]
-Color gradients centered on randomly-placed poles.
-May need to be run through
-{\it ppmquant}{\rm .}
-\item[{{\bf -squig}}]
-Squiggley tubular pattern.  Can be tiled.
-May need to be run through
-{\it ppmquant}{\rm .}
-\item[{{\bf -camo}}]
-Camouflage pattern.
-May need to be run through
-{\it ppmquant}{\rm .}
-\item[{{\bf -anticamo}}]
-Anti-camouflage pattern - like -camo, but ultra-bright colors.
-May need to be run through
-{\it ppmquant}{\rm .}
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{REFERENCES}
-Some of the patterns are from ``Designer's Guide to Color 3'' by Jeanne Allen.
-\shead{SEE ALSO}
-pnmtile(1), ppmquant(1), ppm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: ppmpat.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:48 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmquant.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmquant.1
-%
-\phead{ppmquant}{1}{12 January 1991}{}{}
-
-%.IX ppmquant
-\shead{NAME}
-ppmquant - quantize the colors in a portable pixmap down to a specified number
-
-\shead{SYNOPSIS}
-{\bf ppmquant}
-{\rm [}{\bf -floyd}{\rm $|$}{\bf -fs}{\rm ]}
-{\it ncolors}
-{\rm [}{\it ppmfile}{\rm ]}
-\nwl
-{\bf ppmquant}
-{\rm [}{\bf -floyd}{\rm $|$}{\bf -fs}{\rm ]}
-{\bf -map}
-{\it mapfile}
-{\rm [}{\it ppmfile}{\rm ]}
-
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Chooses
-{\it ncolors}
-colors to best represent the image, maps the existing colors
-to the new ones, and writes a portable pixmap as output.
-%.IX "colormap reduction"
-\par
-The quantization method is Heckbert's ``median cut''.
-%.IX "median cut"
-\par
-Alternately, you can skip the color-choosing step by
-specifying your own set of colors with the
-{\bf -map}
-flag.  The
-{\it mapfile}
-is just a
-{\it ppm}
-file; it can be any shape, all that matters is the colors in it.
-For instance, to quantize down to the 8-color IBM TTL color set, you
-might use:
-{\tt\nofill
-    P3
-    8 1
-    255
-      0   0   0
-    255   0   0
-      0 255   0
-      0   0 255
-    255 255   0
-    255   0 255
-      0 255 255
-    255 255 255
-\fill}
-
-If you want to quantize one pixmap to use the colors in another one,
-just use the second one as the mapfile.
-You don't have to reduce it down to only one pixel of each color,
-just use it as is.
-\par
-The
-{\bf -floyd}{\rm /}{\bf -fs}
-flag enables a Floyd-Steinberg error diffusion step.
-%.IX Floyd-Steinberg
-%.IX "error diffusion"
-Floyd-Steinberg gives vastly better results on images where the unmodified
-quantization has banding or other artifacts, especially when going to a
-small number of colors such as the above IBM set.
-However, it does take substantially more CPU time, so the default is off.
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{REFERENCES}
-``Color Image Quantization for Frame Buffer Display'' by Paul Heckbert,
-SIGGRAPH '82 Proceedings, page 297.
-\shead{SEE ALSO}
-ppmquantall(1), pnmdepth(1), ppmdither(1), ppm(5)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: ppmquant.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Mon Nov 29 13:33:41 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmquantall.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmquantall.1
-%
-\phead{ppmquantall}{1}{27 July 1990}{}{}
-
-%.IX ppmquantall
-\shead{NAME}
-ppmquantall - run ppmquant on a bunch of files all at once, so they share a common colormap
-\shead{SYNOPSIS}
-{\bf ppmquantall}
-{\it ncolors ppmfile}
-{\rm ...}
-\shead{DESCRIPTION}
-Takes a bunch of portable pixmap as input.
-Chooses
-{\it ncolors}
-colors to best represent all of the images, maps the
-existing colors to the new ones, and
-{\bf overwrites the input files}
-with the new quantized versions.
-%.IX "colormap reduction"
-\par
-Verbose explanation: Let's say you've got a dozen pixmaps that you want
-to display on the screen all at the same time.  Your screen can only
-display 256 different colors, but the pixmaps have a total of a thousand
-or so different colors.  For a single pixmap you solve this problem with
-{\it ppmquant}{\rm ;}
-%.IX ppmquant
-this script solves it for multiple pixmaps.  All it does is
-concatenate them together into one big pixmap, run
-{\it ppmquant}
-on that, and then split it up into little pixmaps again.
-\par
-(Note that another way to solve this problem is to pre-select a set of
-colors and then use
-{\it ppmquant}{\rm 's}
-{\bf -map}
-option to separately quantize each pixmap to that set.)
-\shead{SEE ALSO}
-ppmquant(1), ppm(5)
-\shead{BUGS}
-It's a csh script.
-Csh scripts are not portable to System V.
-Scripts in general are not portable to non-Unix environments.
-\shead{AUTHOR}
-Copyright (C) 1991 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.
-%
-% end of input file: ppmquantall.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:09 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmqvga.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmqvga.1
-%
-\phead{PPMQVGA}{1}{local}{}{}
-
-'% Heading: name(sect)    center (paren)    name(sect)
-\shead{NAME}
-ppmqvga - 8 plane quantization
-'% name [, name] ... \- brief description on a single line of \s-1INPUT\s+1
-\shead{SYNOPSIS}
-ppmqvga [ options ] [ input file ]
-'% foo [ options ] files1 [ file2 [ ... ] ]
-\shead{DESCRIPTION}
-{\bf ppmqvga}
-quantizes PPM files to 8 planes, with optional Floyd-Steinberg dithering.
-Input is a PPM file from the file named, or standard input of no file is
-provided.
-%.SS Options
-{\bf -d}
-dither. Apply Floyd-Steinberg dithering to the data
-
-{\bf -q}
-quiet. Produces no progress reporting, and no terminal output unless
-and error occurs.
-
-{\bf -v}
-verbose. Produces additional output describing the number of colors found,
-and some information on the resulting mapping. May be repeated to generate
-loads of internal table output, but generally only useful once.
-\shead{EXAMPLES}
-ppmqvga -d my\_image.ppm $|$ ppmtogif $>$my\_image.gif
-
-tgatoppm zombie.tga $|$ ppmqvga $|$ ppmtotif $>$ zombie.tif
-\shead{SEE ALSO}
-ppmquant
-\shead{DIAGNOSTICS}
-Error messages if problems, various levels of optional progress reporting.
-\shead{LIMITATIONS}
-none known.
-\shead{AUTHOR}
-Original by Lyle Rains (lrains@netcom.com) as ppmq256 and ppmq256fs
-combined, documented, and enhanced by Bill Davidsen (davidsen@crd.ge.com)
-\shead{Copyright}
-Copyright 1991,1992 by Bill Davidsen, all rights reserved.
-The program and documentation may be freely distributed by anyone in source
-or binary format. Please clearly note any changes.
-%
-% end of input file: ppmqvga.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:49 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmrelief.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmrelief.1
-%
-\phead{ppmrelief}{1}{11 January 1991}{}{}
-
-%.IX ppmrelief
-\shead{NAME}
-ppmrelief - run a Laplacian relief filter on a portable pixmap
-\shead{SYNOPSIS}
-{\bf ppmrelief}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Does a Laplacian relief filter, and writes a portable pixmap as output.
-%.IX "Laplacian relief"
-\par
-The Laplacian relief filter is described in ``Beyond Photography'' by Holzmann,
-equation 3.19.
-It's a sort of edge-detection.
-%.IX "edge detection"
-\shead{SEE ALSO}
-pgmbentley(1), pgmoil(1), ppm(5)
-\shead{AUTHOR}
-\copyright 1990 by Wilson Bent (whb@hoh-2.att.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.
-%
-% end of input file: ppmrelief.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Mon Nov 29 13:24:20 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmshift.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmshift.1
-%
-\phead{ppmshift}{1}{16 November 1993}{}{}
-
-%.IX ppmshift
-\shead{NAME}
-ppmshift - shift lines of a portable pixmap left or right by a random amount
-\shead{SYNOPSIS}
-ppmshift 
-{\it shift}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input. Shifts every row of image data to the
-left or right by a certain amount. The 'shift' parameter determines by how 
-many pixels a row is to be shifted at most.
-\par
-Another one of those effects I intended to use for MPEG tests.
-Unfortunately, this program will not help me here - it creates too random
-patterns to be used for animations. Still, it might give interesting
-results on still images.
-\shead{EXAMPLE}
-Check this out: Save your favourite model's picture from something like
-alt.binaries.pictures.supermodels (ok, or from any other picture source),
-convert it to ppm, and process it e.g. like this, assuming the picture is 
-800x600 pixels:
-\nofill\ind{1\parindent}\tt{}  \# take the upper half, and leave it like it is
-  pnmcut 0 0 800 300 cs.ppm $>$upper.ppm
-
-  \# take the lower half, flip it upside down, dim it and distort it a little
-  pnmcut 0 300 800 300 cs.ppm $|$ pnmflip -tb $|$ ppmdim 0.7 $|$ 
-     ppmshift 10 $>$lower.ppm
-
-  \# and concatenate the two pieces
-  pnmcat -tb upper.ppm lower.ppm $>$newpic.ppm
-\fill 
-The resulting picture looks like the image being reflected on a water 
-surface with slight ripples.
-\shead{SEE ALSO}
-ppm(5), pnmcut(1), pnmflip(1), ppmdim(1), pnmcat(1)
-\shead{AUTHOR}
-\copyright 1993 by Frank Neumann
-% 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.
-%
-% end of input file: ppmshift.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Mon Nov 29 13:24:10 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmspread.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmspread.1
-%
-\phead{ppmspread}{1}{16 November 1993}{}{}
-
-%.IX ppmspread
-\shead{NAME}
-ppmspread - displace a portable pixmap's pixels by a random amount
-\shead{SYNOPSIS}
-ppmspread
-{\it amount}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input. Moves every pixel around a bit
-relative to its original position. amount determines by how many
-pixels a pixel is to be moved around at most.
-\par
-Pictures processed with this filter will seem to be somewhat 
-dissolved or unfocussed (although they appear more coarse than
-images processed by something like
-{\it pnmconvol}
-).
-\shead{SEE ALSO}
-ppm(5), pnmconvol(1)
-\shead{AUTHOR}
-\copyright 1993 by Frank Neumann
-% 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
-%
-% end of input file: ppmspread.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:50 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtoacad.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtoacad.1
-%
-\phead{ppmtoacad}{1}{10 October 1991}{}{}
-
-%.IX ppmtoacad
-%.IX AutoCAD
-\shead{NAME}
-ppmtoacad - convert portable pixmap to AutoCAD database or slide
-\shead{SYNOPSIS}
-\raggedright
-{\bf ppmtoacad}
-'in 15n
-{\rm [}{\bf -dxb}{\rm ]}
-{\rm [}{\bf -poly}{\rm ]}
-{\rm [}{\bf -background}
-{\it colour}{\rm ]}
-{\rm [}{\bf -white}{\rm ]}
-{\rm [}{\bf -aspect}
-{\it ratio}{\rm ]}
-{\rm [}{\bf -8}{\rm ]}
-{\rm [}{\it ppmfile}{\rm ]}
-\ind{-7.5em}
-%.ad
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.  Produces an AutoCAD{l}ide file or
-binary database import (.dxb) file as output.
-If no
-{\it ppmfile}
-is specified, input is read from standard input.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -dxb}}
-\item[{{\bf -dxb}}]
-An AutoCAD binary database import (.dxb) file is written.  This file
-is read with the DXBIN command and, once loaded, becomes part of
-the AutoCAD geometrical database and can be viewed and edited like
-any other object.  Each sequence of identical pixels becomes a
-separate object in the database; this can result in very large AutoCAD
-drawing files.  However, if you want to trace over a bitmap, it lets
-you zoom and pan around the bitmap as you wish.
-\item[{{\bf -poly}}]
-If the
-{\bf -dxb}
-option is not specified, the output of
-{\bf ppmtoacad}
-is an AutoCAD slide file.  Normally each row of pixels is
-represented by an AutoCAD line entity.  If
-{\bf -poly}
-is selected, the pixels are rendered as filled polygons.  If the
-slide is viewed on a display with higher resolution than the source
-pixmap, this will cause the pixels to expand instead of appearing as
-discrete lines against the screen background colour.  Regrettably,
-this representation yields slide files which occupy more disc space
-and take longer to display.
-\item[{{\bf -background}{\it \ colour}
-}]
-Most AutoCAD display drivers can be configured to use any available
-colour as the screen background.  Some users perfer a black screen
-background, others white, while splinter groups advocate burnt ocher,
-tawny puce, and shocking grey.  Discarding pixels whose closest
-AutoCAD colour representation is equal to the background colour can
-substantially reduce the size of the AutoCAD database or slide file
-needed to represent a bitmap.  If no
-{\bf -background}
-colour is specified, the screen background colour is assumed to be
-black.  Any AutoCAD colour number may be specified as the screen
-background; colour numbers are assumed to specify the hues defined
-in the standard AutoCAD 256 colour palette.
-\item[{{\bf -white}}]
-Since many AutoCAD users choose a white screen background, this option
-is provided as a short-cut.  Specifying
-{\bf -white}
-is identical in effect to
-{\bf -background\ 7}{\rm .}
-\item[{{\bf -aspect}{\it \ ratio}
-}]
-If the source pixmap had non-square pixels, the ratio of the pixel
-width to pixel height should be specified as
-{\it ratio}{\rm .}
-The resulting slide or .dxb file will be corrected so that pixels on
-the AutoCAD screen will be square.  For example, to correct an image made
-for a 320x200 VGA/MCGA screen, specify
-{\bf -aspect\ 0.8333}{\rm .}
-\item[{{\bf -8}}]
-Restricts the colours in the output file to the 8 RGB shades.
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{BUGS}
-AutoCAD has a fixed palette of 256 colours, distributed along the hue,
-lightness, and saturation axes.  Pixmaps which contain many
-nearly-identical colours, or colours not closely approximated by
-AutoCAD's palette, may be poorly rendered.
-\par
-{\bf ppmtoacad}
-works best if the system displaying its output supports the full 256
-colour AutoCAD palette.  Monochrome, 8 colour, and 16 colour
-configurations will produce less than optimal results.
-\par
-When creating a .dxb file or a slide file with the
-{\bf -poly}
-option,
-{\bf ppmtoacad}
-finds both vertical and horizontal runs of identical pixels and
-consolidates them into rectangular regions to reduce the size of the
-output file.  This is effective for images with large areas of
-constant colour but it's no substitute for true raster to vector
-conversion.  In particular, thin diagonal lines are not optimised at
-all by this process.
-\par
-Output files can be huge.
-\shead{SEE ALSO}
-AutoCAD Reference Manual:
-{\it Slide File Format}
-and
-{\it Binary\ Drawing\ Interchange\ (DXB)\ Files}{\rm ,}
-{\bf ppm}{\rm (5)}
-\shead{AUTHOR}
-\ind{1\parindent}{\nofill
-    John Walker
-    Autodesk SA
-    Avenue des Champs-Montants 14b
-    CH-2074 MARIN
-    Suisse/Schweiz/Svizzera/Svizra/Switzerland
-\fill}
-\begin{TPlist}{Usenet:}
-\item[{Usenet:}]
-kelvin@Autodesk.com
-\item[{Fax:}]
-038/33 88 15
-\item[{Voice:}]
-038/33 76 33
-\end{TPlist}
-
-\par
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose and without fee is hereby granted,
-without any conditions or restrictions.  This software is provided ``as
-is'' without express or implied warranty.
-\par
-AutoCAD and Autodesk are registered trademarks of Autodesk, Inc.
-%
-% end of input file: ppmtoacad.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:09 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtobmp.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtobmp.1
-%
-\phead{ppmtobmp}{1}{26 Oct 1992}{}{}
-
-%.IX ppmtobmp
-\shead{NAME}
-ppmtobmp -- convert a portable pixmap into a BMP file
-\shead{SYNOPSIS}
-{\bf ppmtobmp}
-{\rm [}{\it --windows}{\rm ]}
-{\rm [}{\it --os2}{\rm ]}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces a Microsoft Windows or OS/2 BMP file as output.
-%.IX BMP
-\shead{OPTIONS}
-\begin{TPlist}{{\bf --windows}}
-\item[{{\bf --windows}}]
-Tells the program to produce a Microsoft Windows BMP file.
-\item[{{\bf --os2}}]
-Tells the program to produce an OS/2 BMP file.
-(This is the default.)
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-bmptoppm(1),
-ppm(5)
-\shead{AUTHOR}
-\copyright 1992 by David W. Sanderson.
-% 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.
-%
-% end of input file: ppmtobmp.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:50 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtogif.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtogif.1
-%
-\phead{ppmtogif}{1}{30 June 1993}{}{}
-
-%.IX ppmtogif
-\shead{NAME}
-ppmtogif - convert a portable pixmap into a GIF file
-\shead{SYNOPSIS}
-{\bf ppmtogif}
-{\rm [}{\bf -interlace}{\rm ]}
-{\rm [}{\bf -sort}{\rm ]}
-{\rm [}{\bf -map}
-{\it mapfile ]}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces a GIF file as output.
-%.IX GIF
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -interlace}}
-\item[{{\bf -interlace}}]
-Tells the program to produce an interlaced GIF file.
-\item[{{\bf -sort}}]
-Produces a GIF file with a sorted color map.
-\item[{{\bf -map}}]
-{\it mapfile}
-
-Uses the colors found in the
-{\it mapfile}
-to create the colormap in the GIF file, instead of the colors from
-{\it ppmfile.}
-The
-{\it mapfile}
-can be any
-{\it ppm}
-file; all that matters is the colors in it. If the colors in
-{\it ppmfile}
-do not match those in
-{\it mapfile}
-, they are matched to a ``best match''. A (much) better result can be obtained by
-using the following filter in advance:
-
-{\it ppmquant}
--floyd -map
-{\it mapfile}
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-giftoppm(1), ppmquant(1), ppm(5)
-\shead{AUTHOR}
-Based on GIFENCOD by David Rowley (mgardi@watdcsu.waterloo.edu).
-Lempel-Ziv compression based on ``compress''.
-
-\copyright 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.
-%
-% end of input file: ppmtogif.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:51 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtoicr.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtoicr.1
-%
-\phead{ppmtoicr}{1}{30 July 1990}{}{}
-
-%.IX ppmtoicr
-\shead{NAME}
-ppmtoicr - convert a portable pixmap into NCSA ICR format 
-\shead{SYNOPSIS}
-{\bf ppmtoicr}
-{\rm [}{\bf -windowname}
-{\it name}{\rm ]}
-{\rm [}{\bf -expand}
-{\it expand}{\rm ]}
-{\rm [}{\bf -display}
-{\it display}{\rm ]}
-{\rm [}{\bf -rle}{\rm ]}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap file as input.
-Produces an NCSA Telnet Interactive Color Raster graphic file as output.
-%.IX "NCSA ICR"
-If
-{\it ppmfile}
-is not supplied, 
-{\it ppmtoicr}
-will read from standard input.
-\par
-Interactive Color Raster (ICR) is a protocol for displaying raster
-graphics on workstation screens. The protocol is implemented in NCSA
-Telnet for the Macintosh version 2.3.
-%.IX Macintosh
-The ICR protocol shares
-characteristics of the Tektronix graphics terminal emulation protocol.
-%.IX Tektronix
-For example, escape sequences are used to control the display.
-\par
-{\it ppmtoicr}
-will output the appropriate sequences to create a window of the
-dimensions of the input pixmap,
-create a colormap of up to 256
-colors on the display, then load the picture data into the window.
-\par
-Note that there is no icrtoppm tool - this transformation is one way.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -windowname}{\it name}
-}
-\item[{{\bf -windowname}{\it name}
-}]
-Output will be displayed in
-{\it name}
-(Default is to use
-{\it ppmfile}
-or ``untitled'' if standard input is read.)
-\item[{{\bf -expand}{\it expand}
-}]
-Output will be expanded on display by factor 
-{\it expand}
-(For example, a value of 2 will cause four pixels to be displayed for
-every input pixel.)
-\item[{{\bf -display}{\it display}
-}]
-Output will be displayed on screen numbered 
-{\it display}
-\item[{{\bf -rle}}]
-Use run-length encoded format for display. (This will nearly always
-result in a quicker display, but may skew the colormap.)
-\end{TPlist}
-
-\shead{EXAMPLES}
-To display a
-{\it ppm}
-file using the protocol:
-\nofill
-    ppmtoicr ppmfile
-\fill
-This will create a window named 
-{\it ppmfile}
-on the display with the correct dimensions for
-{\it ppmfile,}
-create and download a colormap of up
-to 256 colors, and download the picture into the window. The same effect
-may be achieved by the following sequence:
-\nofill
-    ppmtoicr ppmfile $>$ filename
-    cat filename
-\fill
-To display a GIF 
-file using the protocol in a window titled after the input file, zoom
-the displayed image by a factor of 2, and
-run-length encode the data:
-\nofill
-    giftoppm giffile $|$ ppmtoicr -w giffile -r -e 2
-\fill
-\shead{BUGS}
-\par
-The protocol uses frequent 
-{\it fflush}
-calls to speed up display. If the
-output is saved to a file for later display via
-{\it cat,}
-drawing will be
-much slower. In either case, increasing the Blocksize limit on the
-display will speed up transmission substantially.
-\shead{SEE ALSO}
-{\bf ppm(5)}
-\par\noindent
-{\it NCSA\ Telnet\ for\ the\ Macintosh}{\rm ,}
-University of Illinois at Urbana-Champaign (1989)
-\shead{AUTHOR}
-\copyright 1990 by Kanthan Pillay (svpillay@Princeton.EDU),
-Princeton University Computing and Information Technology.
-% 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.
-%
-% end of input file: ppmtoicr.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:52 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtoilbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtoilbm.1
-%
-\phead{ppmtoilbm}{1}{29 August 1993}{}{}
-
-%.IX ppmtoilbm
-\shead{NAME}
-ppmtoilbm - convert a portable pixmap into an ILBM file
-\shead{SYNOPSIS}
-{\bf ppmtoilbm}
-{\rm [}{\bf -maxplanes}{\rm $|$}{\bf -mp}
-{\it N}{\rm ]}
-{\rm [}{\bf -fixplanes}{\rm $|$}{\bf -fp}
-{\it N}{\rm ]}
-{\rm [}{\bf -ham6}{\rm $|$}{\bf -ham8}{\rm ]}
-{\rm [}{\bf -dcbits}{\rm $|$}{\bf -dcplanes}{\rm r}{\bf g}{\rm b}{\bf ]}
-{\rm [}{\bf -normal}{\rm $|$}{\bf -hamif}{\rm $|$}{\bf -hamforce}{\rm $|$}{\bf -24if}{\rm $|$}{\bf -24force}{\rm $|$}
-{\rm -dcif}{\bf $|$}{\rm -dcforce}{\bf $|$}{\rm -cmaponly}{\bf ]}
-{\rm [}{\bf -ecs}{\rm $|$}{\bf -aga}{\rm ]}
-{\rm [}{\bf -map}{\rm ppmfile}{\bf ]}
-{\rm [}{\bf ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-%.IX "ILBM"
-Produces an ILBM file as output.
-Supported ILBM types are:
-\begin{TPlist}{Normal ILBMs with 1-16 planes.}
-\item[{Normal ILBMs with 1-16 planes.}]
-\item[{Amiga Hold-and-Modify (HAM) with 3-16 planes.}]
-%.IX "Amiga"
-%.IX "HAM"
-\item[{24 bit.}]
-\item[{Color map (BMHD + CMAP chunk only, nPlanes = 0).}]
-\item[{Unofficial direct color.}]
-1-16 planes for each color component.
-\item[{Chunks written:}]
-BMHD, CMAP, CAMG (only for HAM), BODY (not for colormap files)
-unofficial DCOL chunk for direct color ILBM
-\item[{Chunks ignored:}]
-GRAB, DEST, SPRT, CRNG, CCRT, CLUT, DPPV, DRNG, EPSF
-\item[{Other chunks (ignored but displayed in verbose mode):}]
-NAME, AUTH, (c), ANNO, DPI
-\end{TPlist}
-
-\shead{OPTIONS}
-Options marked with (*) can be prefixed with a ``no'',
-{\it e.g.}, ``-nohamif''. All options can be abbreviated to
-their shortest unique prefix.
-\begin{TPlist}{{\bf -maxplanes $|$ -mp n}}
-\item[{{\bf -maxplanes $|$ -mp n}}]
-(default 5, minimum 1, maximum 16)
-Maximum planes to write in a normal ILBM.  If the pixmap
-does not fit into $<$n$>$ planes, ppmtoilbm writes a HAM file
-(if -hamif is used), a 24bit file (if -24if is used) or a
-direct color file (if -dcif is used) or aborts with an error.
-\item[{{\bf -fixplanes $|$ -fp n}}]
-(min 1, max 16)
-If a normal ILBM is written, it will have exactly $<$n$>$ planes.
-\item[{{\bf -hambits $|$ -hamplanes n}}]
-(default 6, min 3, max 16)
-Select number of planes for HAM picture.  The current Amiga
-hardware supports 6 and 8 planes, so for now you should
-only use this values.
-\item[{{\bf -normal (default)}}]
-Turns off -hamif/-24if/-dcif, -hamforce/-24force/-dcforce and
--cmaponly.
-\item[{{\bf -hamif (*)}}]
-\item[{{\bf -24if (*)}}]
-\item[{{\bf -dcif (*)}}]
-Write a HAM/24bit/direct color file if the pixmap does not
-fit into $<$maxplanes$>$ planes.
-\item[{{\bf -hamforce (*)}}]
-\item[{{\bf -24force (*)}}]
-\item[{{\bf -dcforce (*)}}]
-Write a HAM/24bit/direct color file.
-\item[{{\bf -dcbits $|$ -dcplanes r g b}}]
-(default 5, min 1, max 16).
-Select number of bits for red, green \& blue in a direct
-color ILBM.
-\item[{{\bf -ecs (default)}}]
-Shortcut for: -hamplanes 6 -maxplanes 5
-\item[{{\bf -aga}}]
-\item[{{\bf Shortcut for: -hamplanes 8 -maxplanes 8}}]
-\item[{{\bf -ham6}}]
-\item[{{\bf Shortcut for: -hamplanes 6 -hamforce}}]
-\item[{{\bf -ham8}}]
-Shortcut for: -hamplanes 8 -hamforce
-\item[{{\bf -map ppmfile}}]
-Write a normal ILBM using the colors in $<$ppmfile$>$ as the
-colormap. The colormap file also determines the number of
-planes, a -maxplanes or -fixplanes option is ignored.
-\item[{{\bf -cmaponly}}]
-Write a colormap file: only BMHD and CMAP chunks, no BODY
-chunk, nPlanes = 0.
-\end{TPlist}
-
-\shead{BUGS}
-Needs a real colormap selection algorithm for HAM pictures,
-instead of using a grayscale colormap.
-\shead{REFERENCES}
-Amiga ROM Kernel Reference Manual - Devices (3rd Ed.)
-Addison Wesley, ISBN 0--201--56775--X
-\shead{SEE ALSO}
-ppm(5), ilbmtoppm(1)
-\shead{AUTHORS}
-\copyright 1989 by Jef Poskanzer.
-\nwl
-Modified August 1993 by Ingo Wilken
-(Ingo.Wilken@informatik.uni-oldenburg.de).
-% 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.
-%
-% end of input file: ppmtoilbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Tue Nov 30 08:30:37 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtomitsu.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtomitsu.1
-%
-\phead{ppmtomitsu}{1}{29 Jan 1992}{}{}
-
-%.IX ppmtomitsu
-\shead{NAME}
-ppmtomitsu - convert a portable pixmap to a Mitsubishi S340-10 file
-\shead{SYNOPSIS}
-{\bf ppmtomitsu}
-{\rm [-sharpness}
-{\it val}{\rm ]}
-{\rm [}{\bf -enlarge}
-{\it val}{\rm ]}
-{\rm [}{\bf -media}
-{\it string}{\rm ]}
-{\rm [}{\bf -copy}
-{\it val}{\rm ]}
-{\rm [}{\bf -dpi300}{\rm ]}
-{\rm [}{\bf -tiny}{\rm ]}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input and converts it into a format suitable
-to be printed by a Mitsubishi S340-10 printer, or any other Mitsubishi
-color sublimation printer.
-\par
-The Mitsubishi S340-10 Color Sublimation printer supports 24bit color.
-Images of the available sizes take so long to transfer that there is a
-fast method, employing a lookuptable, that ppmtomitsu will use if there
-is a maximum of 256 colors in the pixmap.
-ppmtomitsu will try to position your image to the center of the paper,
-and will rotate your image for you if xsize is larger than ysize.
-If your image is larger than the media allows, ppmtomitsu will quit
-with an error message. (We decided that the media were too expensive
-to have careless users produce misprints.) 
-Once data transmission has started, the job can't be stopped in a
-sane way without resetting the printer.
-The printer understands putting together images in the printers memory;
-ppmtomitsu doesn't utilize this as pnmcat etc provide the same functionality
-and let you view the result on-screen, too.
-The S340-10 is the lowest common denominator printer; for higher
-resolution printers there's the dpi300 option. The other printers also
-support higher values for enlarge eg, but I don't think that's essential
-enough to warrant a change in the program.
-\begin{TPlist}{{\bf -sharpness}{\it \ 1-4}
-}
-\item[{{\bf -sharpness}{\it \ 1-4}
-}]
- 'sharpness' designation. Default is to use the current sharpness.
-\item[{{\bf -enlarge}{\it \ 1-3}
-}]
-Enlarge by a factor; Default is 1 (no enlarge)
-\item[{{\bf -media}{\it \ A,\ A4,\ AS,\ A4S}
-}]
-Designate the media you're using. Default is 1184 x 1350, which will fit
-on any media. A is 1216 x 1350, A4 is 1184 x 1452, AS is 1216 x 1650 and 
-A4S is 1184 x 1754. A warning: If you specify a different media than the
-printer currently has, the printer will wait until you put in the correct
-media or switch it off.
-\item[{{\bf -copy}{\it \ 1-9}
-}]
-The number of copies to produce. Default is 1.
-\item[{{\bf -dpi300}
-}]
-Double the number of allowed pixels for a S3600-30 Printer in S340-10
-compatibility mode. (The S3600-30 has 300 dpi).
-\item[{{\bf -tiny}
-}]
-Memory-safing, but always slow. The printer will get the data line-by-line
-in 24bit. It's probably a good idea to use this if your machine starts
-paging a lot without this option.
-\end{TPlist}
-
-\shead{REFERENCES}
-Mitsubishi Sublimation Full Color Printer S340-10 Specifications of
-Parallel Interface LSP-F0232F
-\shead{SEE ALSO}
-ppmquant(1), pnmscale(1), ppm(5)
-\shead{BUGS}
-We didn't find any - yet. (Besides, they're called features anyway :-)
-If you should find one, my email-adress is below.
-\shead{AUTHOR}
-\copyright 1992, 93 by S.Petra Zeidler, MPIfR Bonn, Germany.
-(spz@specklec.mpifr-bonn.mpg.de)
-% 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.
-%
-% end of input file: ppmtomitsu.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:10 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtomap.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtomap.1
-%
-\phead{ppmtomap}{1}{11 August 1993}{}{}
-
-%.IX ppmtomap
-\shead{NAME}
-ppmtomap - extract all colors from a portable pixmap
-\shead{SYNOPSIS}
-{\bf ppmtomap}
-{\rm [}{\bf -sort}{\rm ]}
-{\rm [}{\bf -square}{\rm ]}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces a portable pixmap as output, representing a color map of the
-input file. All N different colors found are put in an Nx1 portable
-pixmap.
-This color map file can be used as a mapfile for
-{\it ppmquant}
-or
-{\it ppmtogif.}
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -sort}}
-\item[{{\bf -sort}}]
-Produces a portable pixmap with the colors in some sorted order.
-\item[{{\bf -square}}]
-Produces a (more or less) square output file, instead of putting all
-colors on the top row.
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{WARNING}
-If you want to use the output file as a mapfile for
-{\it ppmtogif,}
-you first have to do a
-{\it ppmquant 256,}
-since
-{\it ppmtomap}
-is not limited to 256 colors (but to 65536).
-\shead{SEE ALSO}
-ppmtogif(1), ppmquant(1), ppm(5)
-\shead{AUTHOR}
-Marcel Wijkstra (wijkstra@fwi.uva.nl).
-
-\copyright 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.
-%
-% end of input file: ppmtomap.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:53 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtopcx.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtopcx.1
-%
-\phead{ppmtopcx}{1}{09 April 1990}{}{}
-
-%.IX ppmtopcx
-\shead{NAME}
-ppmtopcx - convert a portable pixmap into a PCX file
-\shead{SYNOPSIS}
-{\bf ppmtopcx}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces a PCX file as output.
-%.IX PCX
-\shead{SEE ALSO}
-pcxtoppm(1), ppm(5)
-\shead{AUTHOR}
-\copyright 1990 by Michael Davidson.
-% 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.
-%
-% end of input file: ppmtopcx.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:53 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtopgm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtopgm.1
-%
-\phead{ppmtopgm}{1}{23 December 1988}{}{}
-
-%.IX ppmtopgm
-\shead{NAME}
-ppmtopgm - convert a portable pixmap into a portable graymap
-\shead{SYNOPSIS}
-{\bf ppmtopgm}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces a portable graymap as output.
-The quantization formula used is .299 r + .587 g + .114 b.
-\par
-Note that although there is a
-{\it pgmtoppm}
-program, it is not necessary
-for simple conversions from
-{\it pgm}
-to
-{\it ppm}{\rm ,}
-because any ppm program can
-read
-{\it pgm}
-(and
-{\it pbm}
-) files automagically.
-{\it pgmtoppm}
-is for colorizing a
-{\it pgm}
-file.  Also, see
-{\it ppmtorgb3}
-%.IX ppmtorgb3
-for a different way of converting color to gray.
-\shead{QUOTE}
-\nofill
-Cold-hearted orb that rules the night
-Removes the colors from our sight
-Red is gray, and yellow white
-But we decide which is right
-And which is a quantization error.
-\fill
-\shead{SEE ALSO}
-pgmtoppm(1), ppmtorgb3(1), rgb3toppm(1), ppm(5), pgm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: ppmtopgm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:54 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtopi1.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtopi1.1
-%
-\phead{ppmtopi1}{1}{19 July 1990}{}{}
-
-%.IX ppmtopi1
-\shead{NAME}
-ppmtopi1 - convert a portable pixmap into an Atari Degas .pi1 file
-\shead{SYNOPSIS}
-{\bf ppmtopi1}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces an Atari Degas .pi1 file as output.
-%.IX Atari
-%.IX "Degas .pi1"
-\shead{SEE ALSO}
-pi1toppm(1), ppm(5), pbmtopi3(1), pi3topbm(1)
-\shead{AUTHOR}
-\copyright 1991 by Steve Belczyk (seb3@gte.com) and 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.
-%
-% end of input file: ppmtopi1.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:54 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtopict.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtopict.1
-%
-\phead{ppmtopict}{1}{15 April 1990}{}{}
-
-%.IX ppmtopict
-\shead{NAME}
-ppmtopict - convert a portable pixmap into a Macintosh PICT file
-\shead{SYNOPSIS}
-{\bf ppmtopict}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces a Macintosh PICT file as output.
-%.IX PICT
-%.IX Macintosh
-\par
-The generated file is only the data fork of a picture.
-You will need a program such as
-{\it mcvert}
-to generate a Macbinary or a BinHex file that contains the necessary
-information to identify the file as a PICT file to MacOS.
-\par
-Even though PICT supports 2 and 4 bits per pixel,
-{\it ppmtopict}
-always generates an 8 bits per pixel file.
-\shead{BUGS}
-The picture size field is only correct if the output is to a file
-since writing into this field requires seeking backwards on a file.
-However the PICT documentation seems to suggest that this field is
-not critical anyway since it is only the lower 16 bits of the picture size.
-\shead{SEE ALSO}
-picttoppm(1), ppm(5), mcvert(1)
-\shead{AUTHOR}
-\copyright 1990 by Ken Yap (ken@cs.rocester.edu).
-% 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.
-%
-% end of input file: ppmtopict.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:55 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtopj.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtopj.1
-%
-\phead{ppmtopj}{1}{13 July 1991}{}{}
-
-%.IX ppmtopj
-\shead{NAME}
-ppmtopj - convert a portable pixmap to an HP PaintJet file
-\shead{SYNOPSIS}
-{\bf ppmtopj}
-{\rm [-gamma}
-{\it val}{\rm ]}
-{\rm [}{\bf -xpos}
-{\it val}{\rm ]}
-{\rm [}{\bf -ypos}
-{\it val}{\rm ]}
-{\rm [}{\bf -back}
-{\bf dark}{\rm $|$}{\bf lite}{\rm ]}
-{\rm [}{\bf -rle}{\rm ]}
-{\rm [}{\bf -center}{\rm ]}
-{\rm [}{\bf -render}
-{\bf none$|$snap$|$bw$|$dither$|$diffuse$|$monodither$|$monodiffuse$|$clusterdither$|$monoclusterdither}{\rm ]}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input and converts it into a format suitable
-to be printed by an HP PaintJet printer.
-\par
-For best results, the input file should be in 8-color RGB form;
-{\it i.e.}, it should have only
-the 8 binary combinations of full-on and full-off primaries.
-You could get this by sending the input file through
-{\it ppmquant -map}
-with a map file such as:
-\nofill
-    P3
-    8 1
-    255
-    0 0 0      255 0 0    0 255 0    0 0 255
-    255 255 0  255 0 255  0 255 255  255 255 255
-\fill
-Or else you could use use
-{\it ppmdither -red 2 -green 2 -blue 2.}
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -rle}}
-\item[{{\bf -rle}}]
-Run length encode the image.
-(This can result in larger images)
-\item[{{\bf -back}}]
-Enhance the foreground by indicating if the background is light or
-dark compated to the foreground.
-\item[{{\bf -render}{\it \ alg}
-}]
-Use an internal rendering algorithm (default dither).
-\item[{{\bf -gamma}{\it \ int}
-}]
-Gamma correct the image using the integet parameter as a gamma (default 0).
-\item[{{\bf -center}}]
-Center the image to an 8.5 by 11 page
-\item[{{\bf -xpos}{\it \ pos}
-}]
-Move by pos pixels in the x direction.
-\item[{{\bf -ypos}{\it \ pos}
-}]
-Move by pos pixels in the y direction.
-\end{TPlist}
-
-\shead{REFERENCES}
-HP PaintJet XL Color Graphics Printer User's Guide
-\shead{SEE ALSO}
-pnmdepth(1), ppmquant(1), ppmdither(1), ppm(5)
-\shead{BUGS}
-Most of the options have not been tested because of the price of the paper.
-\shead{AUTHOR}
-\copyright 1991 by Christos Zoulas.
-% 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.
-%
-% end of input file: ppmtopj.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:10 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtopjxl.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtopjxl.1
-%
-\phead{ppmtopjxl}{1}{14 March 1991}{}{}
-
-\shead{NAME}
-ppmtopjxl - convert a portable pixmap into an HP PaintJet XL PCL file
-\shead{SYNOPSIS}
-ppmtopjxl [-nopack] [-gamma
-{\it $<$n$>$}
-] [-presentation] [-dark] [-diffuse] [-cluster] [-dither] [-xshift
-{\it $<$s$>$}
-] [-yshift
-{\it $<$s$>$}
-] [-xshift
-{\it $<$s$>$}
-] [-yshift
-{\it $<$s$>$}
-] [-xsize$|$-width$|$-xscale
-{\it $<$s$>$}
-] [-ysize$|$-height$|$-yscale
-{\it $<$s$>$}
-] [ppmfile]
-
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces a PCL file suitable for printing on an HP PaintJet XL printer as
-output.
-\par
-The generated file is not suitable for printing on a normal PrintJet printer.
-The
-{\bf --nopack}
-option generates a file which does not use the normal TIFF 4.0 compression
-method. This file might be printable on a normal PaintJet printer (not an XL).
-\par
-The
-{\bf --gamma}
-option sets the gamma correction for the image. The useful range for the
-PaintJet XL is approximately 0.6 to 1.5.
-\par
-The rendering algorithm used for images can be altered with the
-{\bf -dither,}
-{\bf -cluster,}
-and
-{\bf -diffuse}
-options. These options select ordered dithering, clustered ordered dithering,
-or error diffusion respectively.
-The
-{\bf --dark}
-option can be used to enhance images with a dark background when they are
-reduced in size.
-The
-{\bf --presentation}
-option turns on presentation mode, in which two passes are made over the paper
-to increase ink density. This should be used only for images where quality is
-critical.
-
-\par
-The image can be resized by setting the 
-{\bf --xsize}
-and 
-{\bf --ysize}
-options. The parameter to either of these options is interpreted as the
-number of dots to set the width or height to, but an optional dimension of
-`%
-\bf pt%
-\rm ' (points), `%
-\bf dp%
-\rm ' (decipoints), `%
-\bf in%
-\rm ' (inches), or
-`%
-\bf cm%
-\rm ' (centimetres) may be appended.
-If only one dimension is specified, the other will be scaled appropriately.
-
-The options
-{\bf --width}
-and
-{\bf --height}
-are synonyms of
-{\bf --xsize}
-and
-{\bf --ysize.}
-
-The
-{\bf --xscale}
-and
-{\bf --yscale}
-options can alternatively be used to scale the image by a simple factor.
-
-\par
-The image can be shifted on the page by using the
-{\bf --xshift}
-and
-{\bf --yshift}
-options. These move the image the specified dimensions right and down.
-
-\shead{SEE ALSO}
-ppm(5)
-\shead{AUTHOR}
-Angus Duggan
-%
-% end of input file: ppmtopjxl.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:56 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtopuzz.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtopuzz.1
-%
-\phead{ppmtopuzz}{1}{22 August 1990}{}{}
-
-%.IX ppmtopuzz
-\shead{NAME}
-ppmtopuzz - convert a portable pixmap into an X11 ``puzzle'' file
-\shead{SYNOPSIS}
-{\bf ppmtopuzz}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces an X11 ``puzzle'' file as output.
-A ``puzzle'' file is for use with the
-{\it puzzle}
-%.IX puzzle
-%.IX "X window system"
-program included with the X11 distribution -
-{\it puzzle}{\rm 's}
-{\bf -picture}
-flag lets you specify an image file.
-\shead{SEE ALSO}
-ppm(5), puzzle(1)
-\shead{AUTHOR}
-\copyright 1991 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.
-%
-% end of input file: ppmtopuzz.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:57 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtorgb3.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtorgb3.1
-%
-\phead{ppmtorgb3}{1}{10 January 1991}{}{}
-
-%.IX ppmtorgb3
-\shead{NAME}
-ppmtorgb3 - separate a portable pixmap into three portable graymaps
-\shead{SYNOPSIS}
-{\bf ppmtorgb3}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Writes three portable graymaps as output, one each for red, green, and blue.
-%.IX "color separation"
-\par
-The output filenames are constructed by taking the input filename,
-stripping off any extension, and appending ``.red'', ``.grn'', and
-``.blu''.
-For example, separating lenna.ppm would result in lenna.red, lenna.grn,
-and lenna.blu.
-If the input comes from stdin, the names are noname.red, noname.grn,
-and noname.blu.
-\shead{SEE ALSO}
-rgb3toppm(1), ppmtopgm(1), pgmtoppm(1), ppm(5), pgm(5)
-\shead{AUTHOR}
-\copyright 1991 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.
-%
-% end of input file: ppmtorgb3.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:57 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtosixel.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtosixel.1
-%
-\phead{ppmtosixel}{1}{26 April 1991}{}{}
-
-%.IX ppmtosixel
-\shead{NAME}
-ppmtosixel - convert a portable pixmap into DEC sixel format
-\shead{SYNOPSIS}
-{\bf ppmtosixel}
-{\rm [}{\bf -raw}{\rm ]}
-{\rm [}{\bf -margin}{\rm ]}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces sixel commands (SIX) as output.
-The output is formatted for color printing, {\it e.g.}, for a DEC LJ250 color
-inkjet printer. 
-\par
-If RGB values from the PPM file do not have maxval=100,
-the RGB values are rescaled.
-A printer control header and a color assignment table begin the SIX file.
-Image data is written in a compressed format by default.
-A printer control footer ends the image file.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -raw}}
-\item[{{\bf -raw}}]
-If specified, each pixel will be explicitly described in the image file.
-If
-{\bf -raw}
-is not specified, output will default to compressed format in which
-identical adjacent pixels are replaced by ``repeat pixel'' commands.
-A raw file is often an order of magnitude larger than a compressed
-file and prints much slower.
-\item[{{\bf -margin}}]
-If
-{\bf -margin}
-is not specified, the image will be start at the left margin
-(of the window, paper, or whatever).
-If
-{\bf -margin}
-is specified, a 1.5 inch left margin will offset the image.
-\end{TPlist}
-
-\shead{PRINTING}
-Generally, sixel files must reach the printer unfiltered.
-Use the lpr -x option or cat filename $>$ /dev/tty0?.
-\shead{BUGS}
-Upon rescaling, truncation of the least significant bits of RGB values
-may result in poor color conversion.
-If the original PPM maxval was greater than 100, rescaling also
-reduces the image depth.
-While the actual RGB values from the ppm file are more or less
-retained, the color palette of the LJ250 may not match the colors
-on your screen.
-This seems to be a printer limitation.
-\shead{SEE ALSO}
-ppm(5)
-\shead{AUTHOR}
-\copyright 1991 by Rick Vinci.
-% 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.
-%
-% end of input file: ppmtosixel.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:58 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtotga.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtotga.1
-%
-\phead{ppmtotga}{1}{28 October 1991}{}{}
-
-%.IX ppmtotga
-\shead{NAME}
-ppmtotga - convert portable pixmap into a TrueVision Targa file
-\shead{SYNOPSIS}
-{\bf ppmtotga}
-{\rm [}{\bf -mono$|$-cmap$|$-rgb}{\rm ]}
-{\rm [}{\bf -norle}{\rm ]}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces a TrueVision Targa file as output.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -mono}}
-\item[{{\bf -mono}}]
-Forces Targa file to be of type 8 bit monochrome.  Input must be a portable
-bitmap or a portable graymap.
-\item[{{\bf -cmap}}]
-Forces Targa file to be of type 24 bit colormapped.  Input must be a portable
-bitmap, a portable graymap or a portable pixmap containing no more than
-256 distinct colors.
-\item[{{\bf -rgb}}]
-Forces Targa file to be of type 24 bit unmapped color.
-\item[{{\bf -norle}}]
-Disables run-length encoding, in case you have a Targa reader which
-can't read run-length encoded files.
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.  If no
-file type is specified the most highly constained compatible type is
-used, where monochrome is more constained than colormapped which is in
-turn more constained than unmapped.
-\shead{BUGS}
-Does not support all possible Targa file types.
-Should really be in PNM, not PPM.
-\shead{SEE ALSO}
-tgatoppm(1), ppm(5)
-\shead{AUTHOR}
-\copyright 1989, 1991 by Mark Shand and 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.
-%
-% end of input file: ppmtotga.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:59 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtouil.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtouil.1
-%
-\phead{ppmtouil}{1}{31 August 1990}{}{}
-
-%.IX ppmtouil
-\shead{NAME}
-ppmtouil - convert a portable pixmap into a Motif UIL icon file
-\shead{SYNOPSIS}
-{\bf ppmtouil}
-{\rm [}{\bf -name}
-{\it uilname}{\rm ]}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces a Motif UIL icon file as output.
-%.IX Motif
-%.IX UIL
-%.IX "X window system"
-\par
-If the program was compiled with an rgb database specified, and
-a RGB value from the ppm input matches a RGB value from the database,
-then the corresponding color name mnemonic is printed in the UIL's colormap.
-If no rgb database was compiled in, or if the RGB values don't match,
-then the color
-will be printed with the \#RGB, \#RRGGBB, \#RRRGGGBBB, or \#RRRRGGGGBBBB
-hexadecimal format.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -name}}
-\item[{{\bf -name}}]
-Allows you to specify the prefix string which is printed
-in the resulting UIL output.  If not specified, will default to the
-filename (without extension) of the ppmfile argument.
-If
-{\bf -name}
-is not specified and no ppmfile
-is specified ({\it i.e.}, piped input), the prefix string will default to
-the string ``noname''.
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-ppm(5)
-\shead{AUTHOR}
-Converted by Jef Poskanzer from ppmtoxpm.c, which is
-\copyright 1990 by Mark W. Snitily.
-% 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.
-%
-% end of input file: ppmtouil.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:59 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtoxpm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtoxpm.1
-%
-\phead{ppmtoxpm}{1}{Tue Apr 9 1991}{}{}
-
-\shead{NAME}
-ppmtoxpm - convert a portable pixmap into an X11 pixmap
-\shead{SYNOPSIS}
-ppmtoxpm [-name $<$xpmname$>$] [-rgb $<$rgb-textfile$>$] [$<$ppmfile$>$]
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces X11 pixmap  (version 3) as output which
-can be loaded directly by the XPM library.
-\par
-The {\bf -nameoption} allows one to specify the prefix string which is printed
-in the resulting XPM output.  If not specified, will default to the
-filename (without extension) of the $<$ppmfile$>$ argument.
-If {\bf -nameis} not specified and $<$ppmfile$>$
-is not specified ({\it i.e.}, piped input), the prefix string will default to
-the string ``noname''.
-\par
-The {\bf -rgboption} allows one to specify an X11 rgb text file for the
-lookup of color name mnemonics.  This rgb text file is typically the
-/usr/lib/X11/rgb.txt of the MIT X11 distribution, but any file using the
-same format may be used.  When specified and
-a RGB value from the ppm input matches a RGB value from the $<$rgb-textfile$>$,
-then the corresponding color name mnemonic is printed in the XPM's colormap.
-If {\bf -rgbis} not specified, or if the RGB values don't match, then the color
-will be printed with the \#RGB, \#RRGGBB, \#RRRGGGBBB, or \#RRRRGGGGBBBB
-hexadecimal format.
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\par
-For example, to convert the file ``dot'' (found in /usr/include/X11/bitmaps),
-from xbm to xpm one could specify
-\begin{IPlist}
-\IPitem{{}}
-xbmtopbm dot $|$ ppmtoxpm -name dot
-\end{IPlist}
-
-\par
-or, with a rgb text file (in the local directory)
-\begin{IPlist}
-\IPitem{{}}
-xbmtopbm dot $|$ ppmtoxpm -name dot -rgb rgb.txt
-\end{IPlist}
-
-\shead{BUGS}
-An option to match the closest (rather than exact) color name mnemonic
-from the rgb text would be a desirable enhancement.
-\par
-Truncation of the least significant bits of a RGB value may result in
-nonexact matches when performing color name mnemonic lookups.
-\shead{SEE ALSO}
-ppm(5)
-\nwl
-XPM Manual by Arnaud Le Hors (lehors@mirsa.inria.fr).
-\shead{AUTHOR}
-\copyright 1990 by Mark W. Snitily.
-
-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.
-
-This tool was developed for Schlumberger Technologies, ATE Division, and
-with their permission is being made available to the public with the above
-copyright notice and permission notice.
-
-Upgraded to XPM2 by
-   Paul Breslaw, Mecasoft SA, Zurich, Switzerland (paul@mecazh.uu.ch)
-   Thu Nov  8 16:01:17 1990
-
-Upgraded to XPM version 3 by
-   Arnaud Le Hors (lehors@mirsa.inria.fr)
-   Tue Apr 9 1991
-
-
-%
-% end of input file: ppmtoxpm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:00 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtoyuv.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtoyuv.1
-%
-\phead{ppmtoyuv}{1}{25 March 91}{}{}
-
-%.IX ppmtoyuv
-\shead{NAME}
-ppmtoyuv - convert a portable pixmap into an Abekas YUV file
-\shead{SYNOPSIS}
-{\bf ppmtoyuv}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces an Abekas YUV file as output.
-%.IX Abekas
-\shead{SEE ALSO}
-yuvtoppm(1), ppm(5)
-\shead{AUTHOR}
-Marc Boucher (marc@PostImage.COM),
-based on Example Conversion Program, A60/A64 Digital Video Interface
-Manual, page 69.
-\par
-\copyright 1991 by DHD PostImage Inc.
-\par
-\copyright 1987 by Abekas Video Systems Inc.
-% 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.
-%
-% end of input file: ppmtoyuv.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:11 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ppmtoyuvsplit.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ppmtoyuvsplit.1
-%
-\phead{ppmtoyuvsplit}{1}{9 September 1993}{}{}
-
-%.IX ppmtoyuvsplit
-\shead{NAME}
-ppmtoyuvsplit - convert a portable pixmap into 3 subsampled raw YUV files
-\shead{SYNOPSIS}
-{\bf ppmtoyuvsplit}
-{\it basename}
-{\rm [}{\it ppmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a portable pixmap as input.
-Produces 3 raw files basename.Y, basename.U and basename.V as output.
-These files are the subsampled raw YUV representation of the input
-pixmap, as required by the Stanford MPEG codec. The subsampling is done
-by arithmetic mean of 4 pixels colors into one. The YUV values are scaled
-according to CCIR.601, as assumed by MPEG.
-\shead{SEE ALSO}
-mpeg(1), ppm(5)
-\shead{AUTHOR}
-\copyright 1993 by Andre Beck. (Andre\_Beck@IRS.Inf.TU-Dresden.de).
-\par
-Based on ppmtoyuv.c.
-% 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.
-%
-% end of input file: ppmtoyuvsplit.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:33 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: psidtopgm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: psidtopgm.1
-%
-\phead{psidtopgm}{1}{02 August 89}{}{}
-
-%.IX psidtopgm
-\shead{NAME}
-psidtopgm - convert PostScript ``image'' data into a portable graymap
-\shead{SYNOPSIS}
-{\bf psidtopgm}
-{\it width height bits/sample}
-{\rm [}{\it imagedata}{\rm ]}
-\shead{DESCRIPTION}
-Reads the ``image'' data from a PostScript file as input.
-%.IX PostScript
-Produces a portable graymap as output.
-\par
-This is a very simple and limited program, and is here only because
-so many people have asked for it.
-To use it you have to
-{\bf manually}
-extract the readhexstring data portion from your PostScript file, and then
-give the width, height, and bits/sample on the command line.
-Before you attempt this, you should
-{\bf at least}
-read the description of the ``image'' operator in the PostScript Language
-Reference Manual.
-\par
-It would probably not be too hard to write a script that uses this filter
-to read a specific variety of PostScript image, but the variation is too
-great to make a general-purpose reader.
-Unless, of course, you want to write a full-fledged PostScript interpreter...
-\shead{SEE ALSO}
-pnmtops(1), pgm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: psidtopgm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:00 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: qrttoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: qrttoppm.1
-%
-\phead{qrttoppm}{1}{25 August 1989}{}{}
-
-%.IX qrttoppm
-\shead{NAME}
-qrttoppm - convert output from the QRT ray tracer into a portable pixmap
-\shead{SYNOPSIS}
-{\bf qrttoppm}
-{\rm [}{\it qrtfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a QRT file as input.
-%.IX "QRT raytracer"
-Produces a portable pixmap as output.
-\shead{SEE ALSO}
-ppm(5)
-\shead{AUTHOR}
-\copyright 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.
-%
-% end of input file: qrttoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:28 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: rasttopnm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: rasttopnm.1
-%
-\phead{rasttopnm}{1}{13 January 1991}{}{}
-
-%.IX rasttopnm
-\shead{NAME}
-rasttopnm - convert a Sun rasterfile into a portable anymap
-\shead{SYNOPSIS}
-{\bf rasttopnm}
-{\rm [}{\it rastfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a Sun rasterfile as input.
-%.IX Sun
-%.IX rasterfile
-Produces a portable anymap as output.
-The type of the output file depends on the input file - if it's
-black \& white, a
-{\it pbm}
-file is written, else if it's grayscale a
-{\it pgm}
-file, else a
-{\it ppm}
-file.  The program tells you which type it is writing.
-\shead{SEE ALSO}
-pnmtorast(1), pnm(5)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: rasttopnm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:34 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: rawtopgm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: rawtopgm.1
-%
-\phead{rawtopgm}{1}{15 June 1993}{}{}
-
-%.IX rawtopgm
-\shead{NAME}
-rawtopgm - convert raw grayscale bytes into a portable graymap
-\shead{SYNOPSIS}
-{\bf rawtopgm}
-{\rm [}{\bf -headerskip}
-{\it N}{\rm ]}
-{\rm [}{\bf -rowskip}
-{\it N}{\rm ]}
-{\rm [}{\bf -tb}{\rm $|$}{\bf -topbottom}{\rm ]}
-{\rm [}{\it width}
-{\it height}{\rm ]}
-{\rm [}{\it imagedata}{\rm ]}
-\shead{DESCRIPTION}
-Reads raw grayscale bytes as input.
-%.IX "raw grayscale"
-Produces a portable graymap as output.
-The input file is just grayscale bytes.
-If you don't specify the width and height on the command line,
-the program will check the size of the image and try to make
-a quadratic image of it. It is an error to supply a non
-quadratic image without specifying width and height.
-The maxval is assumed to be 255.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -headerskip}}
-\item[{{\bf -headerskip}}]
-If the file has a header, you can use this flag to
-skip over it.
-\item[{{\bf -rowskip}}]
-If there is padding at the ends of the rows, you can skip it with this flag.
-Note that rowskip can be a real number.
-Amazingly, I once had an image with 0.376 bytes of padding per row.
-This turned out to be due to a file-transfer problem, but I was still
-able to read the image.
-\item[{{\bf -tb -topbottom}}]
-Flips the image upside down.
-The first pixel in a pgm file is in the lower left corner of the image.
-For conversion from images with the first pixel in the upper left corner
-({\it e.g.}, the Molecular Dynamics and Leica confocal formats) this flips the
-image right.
-This is equivalent to
-{\bf rawtopgm\ [file]\ $|$\ pnmflip\ -tb .}
-\end{TPlist}
-
-\shead{BUGS}
-If you don't specify the image width and height, the program will
-try to read the entire image to a memory buffer. If you get a
-message that states that you are out of memory, try to specify the width
-and height on the command line. Also, the -tb option consumes much
-memory.
-\shead{SEE ALSO}
-pgm(5), rawtoppm(1), pnmflip(1)
-\shead{AUTHORS}
-\copyright 1989 by Jef Poskanzer.
-\nwl
-Modified June 1993 by Oliver Trepte (oliver@fysik4.kth.se).
-% 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.
-%
-% end of input file: rawtopgm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:01 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: rawtoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: rawtoppm.1
-%
-\phead{rawtoppm}{1}{06 February 1991}{}{}
-
-%.IX rawtoppm
-\shead{NAME}
-rawtoppm - convert raw RGB bytes into a portable pixmap
-\shead{SYNOPSIS}
-{\bf rawtoppm}
-{\rm [}{\bf -headerskip}
-{\it N}{\rm ]}
-{\rm [}{\bf -rowskip}
-{\it N}{\rm ]}
-{\rm [}{\bf -rgb}{\rm $|$}{\bf -rbg}{\rm $|$}{\bf -grb}
-{\rm $|$}{\bf -gbr}{\rm $|$}{\bf -brg}{\rm $|$}{\bf -bgr}
-{\rm ]}
-{\rm [}{\bf -interpixel}{\rm $|$}{\bf -interrow}{\rm ]}
-{\it width height}
-{\rm [}{\it imagedata}{\rm ]}
-\shead{DESCRIPTION}
-Reads raw RGB bytes as input.
-%.IX "raw RGB"
-Produces a portable pixmap as output.
-The input file is just RGB bytes.
-You have to specify the width and height on the command line,
-since the program obviously can't get them from the file.
-The maxval is assumed to be 255.
-If the resulting image is upside down, run it through
-{\bf pnmflip\ -tb .}
-%.IX pnmflip
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -headerskip}}
-\item[{{\bf -headerskip}}]
-If the file has a header, you can use this flag to
-skip over it.
-\item[{{\bf -rowskip}}]
-If there is padding at the ends of the rows, you can skip it with this flag.
-\item[{{\bf -rgb -rbg -grb -gbr -brg -bgr}}]
-These flags let you specify alternate color orders.  The default is
-{\bf -rgb}{\rm .}
-\item[{{\bf -interpixel -interrow}}]
-These flags let you specify how the colors are interleaved.
-The default is
-{\bf -interpixel}{\rm ,}
-meaning interleaved by pixel.
-A byte of red, a byte of green, and a byte
-of blue, or whatever color order you specified.
-{\bf -interrow}
-means interleaved by row - a row of red, a row of green, a row of blue,
-assuming standard rgb color order.
-An
-{\bf -interplane}
-flag  - all the red pixels, then all the green, then all the blue - would
-be an obvious extension, but is not implemented.
-You could get the same effect by splitting the file into three parts
-(perhaps using
-{\it dd}{\rm ),}
-turning each part into a PGM file with rawtopgm, and then combining them
-with rgb3toppm.
-\end{TPlist}
-
-\shead{SEE ALSO}
-ppm(5), rawtopgm(1), rgb3toppm(1), pnmflip(1)
-\shead{AUTHOR}
-\copyright 1991 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.
-%
-% end of input file: rawtoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:02 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: rgb3toppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: rgb3toppm.1
-%
-\phead{rgb3toppm}{1}{15 February 1990}{}{}
-
-%.IX rgb3toppm
-\shead{NAME}
-rgb3toppm - combine three portable graymaps into one portable pixmap
-\shead{SYNOPSIS}
-{\bf rgb3toppm}
-{\it redpgmfile greenpgmfile bluepgmfile}
-\shead{DESCRIPTION}
-Reads three portable graymaps as input.
-Combines them and produces one portable pixmap as output.
-\shead{SEE ALSO}
-ppmtorgb3(1), pgmtoppm(1), ppmtopgm(1), ppm(5), pgm(5)
-\shead{AUTHOR}
-\copyright 1991 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.
-%
-% end of input file: rgb3toppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:32 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: sirtopnm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: sirtopnm.1
-%
-\phead{sirtopnm}{1}{20 March 1991}{}{}
-
-\shead{NAME}
-sirtopnm - convert a Solitaire file into a portable anymap
-\shead{SYNOPSIS}
-{\bf sirtopnm}
-{\rm [}{\it sirfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a Solitaire Image Recorder file as input.
-Produces a portable anymap as output.
-The type of the output file depends on the input file - if it's
-an MGI TYPE 17 file, a
-{\it pgm}
-file is written. If it's an MGI TYPE 11 file, a
-{\it ppm}
-file is written.  The program tells you which type it is writing.
-\shead{BUGS}
-
-\shead{SEE ALSO}
-pnmtosir(1), pnm(5)
-\shead{AUTHOR}
-\copyright 1991 by Marvin Landis.
-% 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.
-
-%
-% end of input file: sirtopnm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:02 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: sldtoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: sldtoppm.1
-%
-\phead{sldtoppm}{1}{10 October 1991}{}{}
-
-%.IX sldtoppm
-%.IX AutoCAD
-\shead{NAME}
-sldtoppm - convert an AutoCAD slide file into a portable pixmap
-\shead{SYNOPSIS}
-\raggedright
-{\bf sldtoppm}
-'in 14n
-{\rm [}{\bf -adjust}{\rm ]}
-{\rm [}{\bf -dir}{\rm ]}
-{\rm [}{\bf -height}{\rm $|$}{\bf -ysize}
-{\it s}{\rm ]}
-{\rm [}{\bf -info}{\rm ]}
-{\rm [}{\bf -lib}{\rm $|$}{\bf -Lib}
-{\it name}{\rm ]}
-{\rm [}{\bf -scale}
-{\it s}{\rm ]}
-{\rm [}{\bf -verbose}{\rm ]}
-{\rm [}{\bf -width}{\rm $|$}{\bf -xsize}
-{\it s}{\rm ]}
-{\rm [}{\it slidefile}{\rm ]}
-\ind{-7.0em}
-%.ad
-\shead{DESCRIPTION}
-Reads an AutoCAD{l}ide file and outputs a portable pixmap.
-If no
-{\it slidefile}
-is specified, input is read from standard input.
-The ppmdraw library is used to convert the vector and polygon
-information in the slide file to a pixmap; see the file ppmdraw.h for
-details on this package.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -adjust}}
-\item[{{\bf -adjust}}]
-If the display on which the slide file was created had non-square
-pixels, when the slide is processed with
-{\bf sldtoppm}
-and the
-{\bf -adjust}
-option is not present, the following warning will appear:
-\ind{-6.8in}
-Warning - pixels on source screen were non-square.
-\nwl
-Specifying
-{\bf -adjust}
-will correct image width to compensate.
-\ind{-7.0in}
-Specifying the
-{\bf -adjust}
-option causes
-{\bf sldtoppm}
-to scale the width of the image so that pixels in the resulting
-portable pixmap are square (and hence circles appear as true circles,
-not ellipses).  The scaling is performed in the vector domain, before
-scan converting the objects.  The results are, therefore, superior in
-appearance to what you'd obtain were you to perform the equivalent
-scaling with
-{\bf pnmscale}
-after the bitmap had been created.
-\item[{{\bf -dir}}]
-The input is assumed to be an AutoCAD slide library file.  A directory
-listing each slide in the library is printed on standard error.
-\item[{{\bf -height}{\it \ size}
-}]
-Scales the image in the vector domain so it is
-{\it size}
-pixels in height.  If no
-{\bf -width}
-or
-{\bf -xsize}
-option is specified, the width will be adjusted to preserve the
-pixel aspect ratio. 
-\item[{{\bf -info}}]
-Dump the slide file header on standard error, displaying the original
-screen size and aspect ratio among other information.
-\item[{{\bf -lib}{\it \ name}
-}]
-Extracts the slide with the given
-{\it name}
-from the slide library given as input.  The specified
-{\it name}
-is converted to upper case.
-\item[{{\bf -Lib}{\it \ name}
-}]
-Extracts the slide with the given
-{\it name}
-from the slide library given as input.  The
-{\it name}
-is used exactly as specified; it is not converted to upper case.
-\item[{{\bf -scale}{\it \ s}
-}]
-Scales the image by factor
-{\it s}{\rm ,}
-which may be any floating point value greater than zero.  Scaling is
-done after aspect ratio adjustment, if any.  Since scaling is
-performed in the vector domain, before rasterisation, the results look
-much better than running the output of
-{\bf sldtoppm}
-through
-{\bf pnmscale}{\rm .}
-\item[{{\bf -verbose}}]
-Dumps the slide file header and lists every vector and polygon in the
-file on standard error.
-\item[{{\bf -width}{\it \ size}
-}]
-Scales the image in the vector domain so it is
-{\it size}
-pixels wide.  If no
-{\bf -height}
-or
-{\bf -ysize}
-option is specified, the height will be adjusted to preserve the
-pixel aspect ratio. 
-\item[{{\bf -xsize}{\it \ size}
-}]
-Scales the image in the vector domain so it is
-{\it size}
-pixels wide.  If no
-{\bf -height}
-or
-{\bf -ysize}
-option is specified, the height will be adjusted to preserve the
-pixel aspect ratio. 
-\item[{{\bf -ysize}{\it \ size}
-}]
-Scales the image in the vector domain so it is
-{\it size}
-pixels in height.  If no
-{\bf -width}
-or
-{\bf -xsize}
-option is specified, the width will be adjusted to preserve the
-pixel aspect ratio. 
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{BUGS}
-Only Level 2 slides are converted.  Level 1 format has been obsolete
-since the advent of AutoCAD Release 9 in 1987, and was not portable
-across machine architectures.
-\par
-Slide library items with names containing 8 bit (such as ISO) or 16
-bit (Kanji, for example) characters may not be found when chosen with the
-{\bf -lib}
-option unless
-{\bf sldtoppm}
-has been built with character set conversion functions appropriate to
-the locale.  You can always retrieve slides from libraries regardless
-of the character set by using the
-{\bf -Lib}
-option and specifying the precise name of library member.  Use the
-{\bf -dir}
-option to list the slides in a library if you're unsure of the
-exact name.
-\shead{SEE ALSO}
-AutoCAD Reference Manual:
-{\it Slide\ File\ Format}{\rm ,}
-{\bf pnmscale}{\rm (1),}
-{\bf ppm}{\rm (5)}
-\shead{AUTHOR}
-\ind{1\parindent}{\nofill
-    John Walker
-    Autodesk SA
-    Avenue des Champs-Montants 14b
-    CH-2074 MARIN
-    Suisse/Schweiz/Svizzera/Svizra/Switzerland
-\fill}
-\begin{TPlist}{Usenet:}
-\item[{Usenet:}]
-kelvin@Autodesk.com
-\item[{Fax:}]
-038/33 88 15
-\item[{Voice:}]
-038/33 76 33
-\end{TPlist}
-
-\par
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose and without fee is hereby granted,
-without any conditions or restrictions.  This software is provided ``as
-is'' without express or implied warranty.
-\par
-AutoCAD and Autodesk are registered trademarks of Autodesk, Inc.
-%
-% end of input file: sldtoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:03 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: spctoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: spctoppm.1
-%
-\phead{spctoppm}{1}{19 July 1990}{}{}
-
-%.IX spctoppm
-\shead{NAME}
-spctoppm - convert an Atari compressed Spectrum file into a portable pixmap
-\shead{SYNOPSIS}
-{\bf spctoppm}
-{\rm [}{\it spcfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads an Atari compressed Spectrum file as input.
-%.IX Atari
-%.IX Spectrum
-Produces a portable pixmap as output.
-\shead{SEE ALSO}
-sputoppm(1), ppm(5)
-\shead{AUTHOR}
-\copyright 1991 by Steve Belczyk (seb3@gte.com) and 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.
-%
-% end of input file: spctoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:37 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: spottopgm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: spottopgm.1
-%
-\phead{spottopgm}{1L}{}{}{}
-
-\shead{NAME}
-spottopgm -- convert SPOT satellite images to Portable Greymap format
-\shead{SYNTAX}
-spottopgm [--1$|$2$|$3] [Firstcol Firstline Lastcol Lastline] inputfile
-\shead{OPTIONS}
-\begin{TPlist}{{\bf --1$|$2$|$3}}
-\item[{{\bf --1$|$2$|$3}}]
-Extract the given colour from the SPOT image. The colours are infra-red,
-visible light and ultra-violet, although I don't know which corresponds
-to which number. If the image is in colour, this will be announced on
-standard error. The default colour is 1.
-\item[{{\bf Firstcol Firstline Lastcol Lastline}}]
-Extract the specified rectangle from the SPOT image. Most SPOT images are
-3000 lines long and 3000 or more columns wide. Unfortunately the SPOT format
-only gives the width and not the length. The width is printed on standard
-error. The default rectangle is the width of the input image by 3000 lines.
-\end{TPlist}
-
-\shead{DESCRIPTION}
-{\it Spottopgm}
-converts the named
-{\bf inputfile}
-into Portable Greymap format, defaulting to the first color and the whole
-SPOT image unless specified by the options.
-\shead{INSTALLATION}
-You
-{\bf must}
-edit the source program and either define BIGENDIAN or LITTLEENDIAN,
-and fix the typedefs for uint32t, uint16t and uint8t appropriately.
-\shead{BUGS}
-Currently
-{\it spottopgm}
-doesn't determine the length of the input file; this would involve two
-passes over the input file. It defaults to 3000 lines instead.
-\par
-{\it Spottopgm}
-could extract a three-color image (ppm), but I didn't feel like making the
-program more complicated than it is now. Besides, there is no one-to-one
-correspondence between red, green, blue and infra-red, visible and
-ultra-violet.
-\par
-I've only had a limited number of SPOT images to play with, and therefore
-wouldn't guarantee that this will work on any other images.
-\shead{AUTHOR}
-Warren Toomey  wkt@csadfa.cs.adfa.oz.au
-\shead{SEE ALSO}
-The rest of the Pbmplus suite.
-%
-% end of input file: spottopgm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:03 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: sputoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: sputoppm.1
-%
-\phead{sputoppm}{1}{19 July 1990}{}{}
-
-%.IX sputoppm
-\shead{NAME}
-sputoppm - convert an Atari uncompressed Spectrum file into a portable pixmap
-\shead{SYNOPSIS}
-{\bf sputoppm}
-{\rm [}{\it spufile}{\rm ]}
-\shead{DESCRIPTION}
-Reads an Atari uncompressed Spectrum file as input.
-%.IX Atari
-%.IX Spectrum
-Produces a portable pixmap as output.
-\shead{SEE ALSO}
-spctoppm(1), ppm(5)
-\shead{AUTHOR}
-\copyright 1991 by Steve Belczyk (seb3@gte.com) and 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.
-%
-% end of input file: sputoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:04 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: tgatoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: tgatoppm.1
-%
-\phead{tgatoppm}{1}{26 August 1989}{}{}
-
-%.IX tgatoppm
-\shead{NAME}
-tgatoppm - convert TrueVision Targa file into a portable pixmap
-\shead{SYNOPSIS}
-{\bf tgatoppm}
-{\rm [}{\bf -debug}{\rm ]}
-{\rm [}{\it tgafile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a TrueVision Targa file as input.
-%.IX TrueVision
-%.IX Targa
-Produces a portable pixmap as output.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -debug}}
-\item[{{\bf -debug}}]
-Causes the header information to be dumped to stderr.
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-%.BUGS
-Should really be in PNM, not PPM.
-\shead{SEE ALSO}
-ppmtotga(1), ppm(5)
-\shead{AUTHOR}
-Partially based on tga2rast, version 1.0, by Ian J. MacPhedran.
-
-\copyright 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.
-%
-% end of input file: tgatoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:36 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: tifftopnm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: tifftopnm.1
-%
-\phead{tifftopnm}{1}{13 January 1991}{}{}
-
-%.IX tifftopnm
-\shead{NAME}
-tifftopnm - convert a TIFF file into a portable anymap
-\shead{SYNOPSIS}
-{\bf tifftopnm}
-{\rm [}{\bf -headerdump}{\rm ]}
-{\rm tifffile}
-\shead{DESCRIPTION}
-Reads a TIFF file as input.
-%.IX TIFF
-Produces a portable anymap as output.
-The type of the output file depends on the input file - if it's
-black \& white, a
-{\it pbm}
-file is written, else if it's grayscale a
-{\it pgm}
-file, else a
-{\it ppm}
-file.  The program tells you which type it is writing.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -headerdump}}
-\item[{{\bf -headerdump}}]
-Dump TIFF file information to stderr.  This information may be useful 
-in debugging TIFF file conversion problems.  
-\end{TPlist}
-
-\par
-All flags can be abbreviated to their shortest unique prefix.
-\shead{SEE ALSO}
-pnmtotiff(1), pnm(5)
-\shead{BUGS}
-This program is not self-contained.  To use it you must fetch the
-TIFF Software package listed in the OTHER.SYSTEMS file and configure
-PBMPLUS to use libtiff.  See PBMPLUS's Makefile for details on this
-configuration.
-\shead{AUTHOR}
-Derived by Jef Poskanzer from tif2ras.c, which is
-\copyright 1990 by Sun Microsystems, Inc.\hfil\break
-Author: Patrick J. Naughton (naughton@wind.sun.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 file is provided AS IS with no warranties of any kind.  The author
-% shall have no liability with respect to the infringement of copyrights,
-% trade secrets or any patents by this file or any part thereof.  In no
-% event will the author be liable for any lost revenue or profits or
-% other special, indirect and consequential damages.
-%
-% end of input file: tifftopnm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:14 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: xbmtopbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: xbmtopbm.1
-%
-\phead{xbmtopbm}{1}{31 August 1988}{}{}
-
-%.IX xbmtopbm
-\shead{NAME}
-xbmtopbm - convert an X11 or X10 bitmap into a portable bitmap
-\shead{SYNOPSIS}
-{\bf xbmtopbm}
-{\rm [}{\it bitmapfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads an X11 or X10 bitmap as input.
-Produces a portable bitmap as output.
-%.IX "X bitmap"
-%.IX "X window system"
-\shead{SEE ALSO}
-pbmtoxbm(1), pbmtox10bm(1), pbm(5)
-\shead{AUTHOR}
-\copyright 1988 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.
-%
-% end of input file: xbmtopbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:05 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ximtoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ximtoppm.1
-%
-\phead{ximtoppm}{1}{25 March 1990}{}{}
-
-%.IX ximtoppm
-\shead{NAME}
-ximtoppm - convert an Xim file into a portable pixmap
-\shead{SYNOPSIS}
-{\bf ximtoppm}
-{\rm [}{\it ximfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads an Xim file as input.
-%.IX Xim
-%.IX "X window system"
-Produces a portable pixmap as output.
-The Xim toolkit is included in the contrib tree of the X.V11R4 release.
-\shead{SEE ALSO}
-ppm(5)
-\shead{AUTHOR}
-\copyright 1991 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.
-%
-% end of input file: ximtoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:05 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: xpmtoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: xpmtoppm.1
-%
-\phead{xpmtoppm}{1}{16 August 1990}{}{}
-
-\shead{NAME}
-xpmtoppm - convert an X11 pixmap into a portable pixmap
-\shead{SYNOPSIS}
-{\bf xpmtoppm}
-{\rm [}{\it xpmfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads an X11 pixmap (XPM version 1 or 3) as input.
-Produces a portable pixmap as output.
-\shead{KNOWN BUGS}
-The support to XPM version 3 is limited. Comments can only be single lines
-and there must be for every pixel a default colorname for a color type visual.
-\shead{SEE ALSO}
-ppmtoxpm(1), ppm(5)
-\nwl
-XPM Manual by Arnaud Le Hors (lehors@mirsa.inria.fr).
-\shead{AUTHOR}
-\copyright 1991 by Jef Poskanzer.
-Upgraded to support XPM version 3 by Arnaud Le Hors
-(lehors@mirsa.inria.fr) Tue Apr 9 1991.
-% 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.
-%
-% end of input file: xpmtoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Mon Feb  7 08:46:26 1994
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: xvminitoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: xvminitoppm.1
-%
-\phead{xvminitoppm}{1}{14 December 1993}{}{}
-
-\shead{NAME}
-xvminitoppm - convert a XV ``thumbnail'' picture to PPM
-\shead{SYNOPSIS}
-{\bf xvminitoppm}
-{\rm [}{\it xvminipic}{\rm ]}
-\shead{DESCRIPTION}
-Reads a XV ``thumbnail'' picture (a miniature picture generated by
-the ``VisualSchnauzer'' browser) as input.
-Produces a portable pixmap as output.
-\shead{SEE ALSO}
-ppm(5), xv(1)
-\shead{AUTHOR}
-Copyright (C) 1993 by Ingo Wilken
-% 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.
-%
-% end of input file: xvminitoppm.1
-%--------------------------------------------------
-
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:28 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: xwdtopnm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: xwdtopnm.1
-%
-\phead{xwdtopnm}{1}{11 January 1991}{}{}
-
-%.IX xwdtopnm
-\shead{NAME}
-xwdtopnm - convert a X11 or X10 window dump file into a portable anymap
-\shead{SYNOPSIS}
-{\bf xwdtopnm}
-{\rm [}{\it xwdfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a X11 or X10 window dump file as input.
-%.IX XWD
-%.IX "X window system"
-Produces a portable anymap as output.
-The type of the output file depends on the input file - if it's
-black \& white, a
-{\it pbm}
-file is written, else if it's grayscale a
-{\it pgm}
-file, else a
-{\it ppm}
-file.  The program tells you which type it is writing.
-\par
-Using this program, you can convert anything on an X workstation's screen
-into an anymap.
-Just display whatever you're interested in, do an xwd, run it through
-xwdtopnm, and then use pnmcut to select the part you want.
-\shead{BUGS}
-I haven't tested this tool with very many configurations, so there are
-probably bugs.
-Please let me know if you find any.
-\shead{SEE ALSO}
-pnmtoxwd(1), pnm(5), xwd(1)
-\shead{AUTHOR}
-\copyright 1989, 1991 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.
-%
-% end of input file: xwdtopnm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:08:14 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: ybmtopbm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: ybmtopbm.1
-%
-\phead{ybmtopbm}{1}{06 March 1990}{}{}
-
-%.IX ybmtopbm
-\shead{NAME}
-ybmtopbm - convert a Bennet Yee ``face'' file into a portable bitmap
-\shead{SYNOPSIS}
-{\bf ybmtopbm}
-{\rm [}{\it facefile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a file acceptable to the
-{\it face}
-and
-{\it xbm}
-programs by Bennet Yee (bsy+@cs.cmu.edu).
-%.IX face
-Writes a portable bitmap as output.
-\shead{SEE ALSO}
-pbmtoybm(1), pbm(5), face(1), face(5), xbm(1)
-\shead{AUTHOR}
-\copyright 1991 by Jamie Zawinski and 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.
-%
-% end of input file: ybmtopbm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:12 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: yuvsplittoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: yuvsplittoppm.1
-%
-\phead{yuvsplittoppm}{1}{26 August 93}{}{}
-
-%.IX yuvsplittoppm
-\shead{NAME}
-yuvplittoppm - convert a Y- an U- and a V-file into a portable pixmap.
-\shead{SYNOPSIS}
-{\bf yuvsplittoppm}
-{\it basename width height}
-[-ccir601]
-\shead{DESCRIPTION}
-Reads three files, containing the YUV components, as input.
-These files are
-{\it basename}
-Y,
-{\it basename}
-U
-and
-{\it basename}
-.V .
-Produces a portable pixmap on stdout.
-
-Since the YUV files are raw files, the dimensions
-{\it width}
-and
-{\it height}
-must be specified on the command line.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -ccir601}}
-\item[{{\bf -ccir601}}]
-Assumes that the YUV triplets are scaled into the smaller range of the
-CCIR 601 (MPEG) standard. Else, the JFIF (JPEG) standard is assumed.
-\end{TPlist}
-
-\shead{SEE ALSO}
-ppmtoyuvsplit(1), yuvtoppm(1), ppm(5)
-\shead{AUTHOR}
-Marcel Wijkstra (wijkstra@fwi.uva.nl), based on {\it ppmtoyuvsplit.}
-%
-% end of input file: yuvsplittoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:06 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: yuvtoppm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: yuvtoppm.1
-%
-\phead{yuvtoppm}{1}{25 March 91}{}{}
-
-%.IX yuvtoppm
-\shead{NAME}
-yuvtoppm - convert Abekas YUV bytes into a portable pixmap
-\shead{SYNOPSIS}
-{\bf yuvtoppm}
-{\it width height}
-{\rm [}{\it imagedata}{\rm ]}
-\shead{DESCRIPTION}
-Reads raw Abekas YUV bytes as input.
-%.IX Abekas
-Produces a portable pixmap as output.
-The input file is just YUV bytes.
-You have to specify the width and height on the command line,
-since the program obviously can't get them from the file.
-The maxval is assumed to be 255.
-\shead{SEE ALSO}
-ppmtoyuv(1), ppm(5)
-\shead{AUTHOR}
-Marc Boucher (marc@PostImage.COM),
-based on Example Conversion Program, A60/A64 Digital Video Interface
-Manual, page 69.
-\par
-\copyright 1991 by DHD PostImage Inc.
-\par
-\copyright 1987 by Abekas Video Systems Inc.
-% 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.
-%
-% end of input file: yuvtoppm.1
-%--------------------------------------------------
- 
-% -*-LaTeX-*-
-% Converted automatically from troff to LaTeX
-% by tr2latex ($Revision: 2.2 $$Date: 1992/04/27 15:13:26 $ by C. Engel)
-% on Fri Oct  8 16:09:34 1993
-% tr2latex was written by Kamal Al-Yahya at Stanford University
-% (Kamal%Hanauma@SU-SCORE.ARPA)
-% and substantially enhanced by Christian Engel at RWTH Aachen
-% (krischan@informatik.rwth-aachen.de).
-%
-% troff input file: zeisstopnm.1
-
-\newpage
-%--------------------------------------------------
-% start of input file: zeisstopnm.1
-%
-\phead{zeisstopnm}{1}{15 June 1993}{}{}
-
-%.IX zeisstopnm
-\shead{NAME}
-zeisstopnm - convert a Zeiss confocal file into a portable anymap
-\shead{SYNOPSIS}
-{\bf zeisstopnm}
-{\rm [}{\it -pgm}
-$|$
-{\it -ppm}{\rm ]}
-{\rm [}{\it zeissfile}{\rm ]}
-\shead{DESCRIPTION}
-Reads a Zeiss confocal file as input.
-Produces a portable anymap as output.
-The type of the output file depends on the input file -
-if it's grayscale a
-{\it pgm}
-file, else a
-{\it ppm}
-file will be produced.
-The program tells you which type it is writing.
-\shead{OPTIONS}
-\begin{TPlist}{{\bf -pgm}}
-\item[{{\bf -pgm}}]
-Force the output to be a
-{\it pgm}
-file.
-\item[{{\bf -ppm}}]
-Force the output to be a
-{\it ppm}
-file.
-\end{TPlist}
-
-\shead{SEE ALSO}
-pnm(5)
-\shead{AUTHOR}
-\copyright 1993 by Oliver Trepte (oliver@fysik4.kth.se).
-% 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.
-%
-% end of input file: zeisstopnm.1
-%--------------------------------------------------
-\end{document}
diff --git a/vms/PBMplus.hlp b/vms/PBMplus.hlp
deleted file mode 100644
index 52ba0f33..00000000
--- a/vms/PBMplus.hlp
+++ /dev/null
@@ -1,6814 +0,0 @@
-1 HELP
-        See PBMplus and OVERVIEW.
-
-1 OVERVIEW
-        Enhanced portable bitmap  toolkit.  The PBMPLUS toolkit allows
-    conversions between image files  of different format.  By means of
-    using common intermediate formats, only  2  * N conversion filters
-    are required to support N distinct  formats,  instead  of the N**2
-    which would be required to convert directly between any one format
-    and  any  other.    The  package also includes  simple  tools  for
-    manipulating portable bitmaps.
-
-        The package consists of four upwardly compatible sections:
-        
-    pbm     Supports monochrome bitmaps (1 bit per pixel).
-
-    pgm     Supports grayscale  images.    Reads  either  PBM  or  PGM
-            formats and writes PGM format.
-
-    ppm     Supports full-color images.  Reads either PBM, PGM, or PPM
-            formats, writes PPM format.
-
-    pnm     Supports content-independent manipulations on any  of  the
-            three formats listed above, as well  as  external  formats
-            having  multiple  types.  Reads either PBM,  PGM,  or  PPM
-            formats,  and  generally writes the same type as  it  read
-            (whenever a PNM tool makes an exception and ``promotes'' a
-            file to a higher format, it informs the user).
-
-        See PBMplus for more infomation.
-
-1 PBMplus
-        Enhanced portable bitmap  toolkit.  The PBMPLUS toolkit allows
-    conversions between image files  of different format.  By means of
-    using common intermediate formats, only  2  * N conversion filters
-    are required to support N distinct  formats,  instead  of the N**2
-    which would be required to convert directly between any one format
-    and  any  other.    The  package also includes  simple  tools  for
-    manipulating portable bitmaps.
-
-        The package consists of four upwardly compatible sections:
-        
-    pbm     Supports monochrome bitmaps (1 bit per pixel).
-
-    pgm     Supports grayscale  images.    Reads  either  PBM  or  PGM
-            formats and writes PGM format.
-
-    ppm     Supports full-color images.  Reads either PBM, PGM, or PPM
-            formats, writes PPM format.
-
-    pnm     Supports content-independent manipulations on any  of  the
-            three formats listed above, as well  as  external  formats
-            having  multiple  types.  Reads either PBM,  PGM,  or  PPM
-            formats,  and  generally writes the same type as  it  read
-            (whenever a PNM tool makes an exception and ``promotes'' a
-            file to a higher format, it informs the user).
-
-2 Description_of_Contents
-        A brief,  one-line  description  of  each  of  the  individual
-    programs in the PBMplus package.  They are sorted by general type.
-
-3 pbm
-    atktopbm    convert Andrew Toolkit raster object to portable bitmap
-    brushtopbm  convert Xerox doodle brushes to portable bitmap
-    cmuwmtopbm  convert CMU window manager format to portable bitmap
-    g3topbm     convert Group 3 FAX to portable bitmap
-    icontopbm   convert Sun icon to portable bitmap
-    gemtopbm    convert GEM .img format to portable bitmap
-    macptopbm   convert MacPaint to portable bitmap
-    mgrtopbm    convert MGR format to portable bitmap
-    pktopbm     convert packed (PK) format font into portable bitmap(s)
-    pbmmerge    merge wrapper routine
-    pbmto10x    convert portable bitmap to Gemini 10x printer graphics
-    pbmto4425   convert portable bitmap to AT&T 4425 terminal
-    pbmtoascii  convert portable bitmap to ASCII graphic form
-    pbmtoatk    convert portable bitmap to Andrew Toolkit raster object
-    pbmtobbnbg  convert portable bitmap to BBN BitGraph graphics
-    pbmtocmuwm  convert portable bitmap to CMU window manager format
-    pbmtoepson  convert portable bitmap to Epson printer graphics
-    pbmtog3     convert portable bitmap to Group 3 FAX
-    pbmtogem    convert portable bitmap into GEM .img file
-    pbmtogo     convert portable bitmap to GraphOn graphics
-    pbmtoicon   convert portable bitmap to Sun icon
-    pbmtolj     convert portable bitmap to HP LaserJet graphics
-    pbmtoln03   convert portable bitmap to DEC LN03+ Laserprinter
-    pbmtolps    convert portable bitmap to PostScript
-    pbmtomacp   convert portable bitmap to MacPaint
-    pbmtomgr    convert portable bitmap to MGR format
-    pbmtopgm    convert portable bitmap to portable graymap by ave. areas
-    pbmtopi3    convert portable bitmap to Atari Degas .pi3
-    pbmtopk     convert portable bitmap into a packed (PK) format font
-    pbmtoplot   convert portable bitmap into Unix plot(5) file
-    pbmtoptx    convert portable bitmap to Printronix graphics
-    pbmtoxbm    convert portable bitmap to X11 bitmap
-    pbmtox10bm  convert portable bitmap to X10 bitmap
-    pbmtoybm    convert portable bitmap into Bennet Yee "face" file
-    pbmtozinc   convert portable bitmap to Zinc Interface Library icon
-    pbmtoepsi   convert portable bitmap into an encapsulated PostScript
-    pi3topbm    convert Atari Degas .pi3 to portable bitmap
-    xbmtopbm    convert X10 or X11 bitmap to portable bitmap
-    ybmtopbm    convert Bennet Yee "face" file into portable bitmap
-
-    pbmclean    flip isolated pixels in portable bitmap
-    pbmlife     apply Conway's rules of Life to a portable bitmap
-    pbmmake     create a blank bitmap of a specified size and color
-    pbmmask     create a mask bitmap from a regular bitmap
-    pbmreduce   reduce a portable bitmap N times, using Floyd-Steinberg
-    pbmspcale   enlarge a portable bitmap with edge smoothing
-    pbmtext     render text into a bitmap
-    pbmupc      create a Universal Product Code bitmap
-
-3 pgm
-    asciitopgm  convert ASCII graphics into a portable graymap
-    fstopgm     convert Usenix FaceSaver format to portable graymap
-    hipstopgm   convert HIPS format to portable graymap
-    lispmtopgm  convert a Lisp Machine bitmap file into pgm format
-    bioradtopgm convert a Biorad confocal file into a portable graymap
-    psidtopgm   convert PostScript "image" data to portable graymap
-    rawtopgm    convert raw grayscale bytes to portable graymap
-    spottopgm   convert SPOT satellite images to Portable Greymap format
-    pgmtofs     convert portable graymap to Usenix FaceSaver format
-    pgmtolispm  convert a portable graymap into Lisp Machine format
-    pgmtopbm    convert portable graymap to portable bitmap
-
-    pgmbentley  Bentleyize a portable graymap
-    pgmcrater   create cratered terrain by fractal forgery
-    pgmedge     edge-detect a portable graymap
-    pgmenhance  edge-enhance a portable graymap
-    pgmhist     print a histogram of the values in a portable graymap
-    pgmkernel   generate a convolution kernel
-    pgmmerge    merge wrapper routine
-    pgmnoise    create a graymap made up of white noise
-    pgmnorm     normalize contrast in a portable graymap
-    pgmoil      turn a portable graymap into an oil painting
-    pgmramp     generate a grayscale ramp
-    pgmtexture  calculate textural features on a portable graymap
-
-3 ppm
-    bmptoppm    convert BMP file to portable pixmap
-    gouldtoppm  convert Gould scanner file to portable pixmap
-    ilbmtoppm   convert IFF ILBM to portable pixmap
-    imgtoppm    convert Img-whatnot to portable pixmap
-    mtvtoppm    convert MTV ray-tracer output to portable pixmap
-    pcxtoppm    convert PC Paintbrush format to portable pixmap
-    pgmtoppm    colorize a portable graymap into a portable pixmap
-    pi1toppm    convert Atari Degas .pi1 to portable pixmap
-    picttoppm   convert Macintosh PICT to portable pixmap
-    pjtoppm     convert HP PaintJet file to portable pixmap
-    ppmtoacad   convert portable pixmap to AutoCAD database or slide
-    ppmtobmp    convert portable pixmap to BMP file
-    ppmtogif    convert portable pixmap to GIF
-    ppmtoicr    convert portable pixmap to NCSA ICR graphics
-    ppmtoilbm   convert portable pixmap to IFF ILBM
-    ppmtomitsu  convert a portable pixmap to a Mitsubishi S340-10 file
-    ppmtomap    extract all colors from a portable pixmap
-    ppmtopcx    convert portable pixmap to PC Paintbrush format
-    ppmtopgm    convert portable pixmap to portable graymap
-    ppmtopi1    convert portable pixmap to Atari Degas .pi1
-    ppmtopict   convert portable pixmap to Macintosh PICT
-    ppmtopj     convert portable pixmap to HP PaintJet file
-    ppmtopjxl   convert portable pixmap to HP PaintJet XL PCL file
-    ppmtopuzz   convert portable pixmap to X11 "puzzle" file
-    ppmtorgb3   separate a portable pixmap to three portable graymaps
-    ppmtosixel  convert portable pixmap to DEC sixel format
-    ppmtotga    convert portable pixmap to TrueVision Targa file
-    ppmtouil    convert portable pixmap to Motif UIL icon file
-    ppmtoxpm    convert portable pixmap to XPM format
-    ppmtoyuv    convert portable pixmap to Abekas YUV format
-    qrttoppm    convert QRT ray-tracer output to portable pixmap
-    rawtoppm    convert raw RGB bytes to portable pixmap
-    rgb3toppm   combine three portable graymaps to one portable pixmap
-    sldtoppm    convert an AutoCAD slide file into a portable pixmap
-    spctoppm    convert Atari compressed Spectrum to portable pixmap
-    sputoppm    convert Atari uncompressed Spectrum to portable pixmap
-    tgatoppm    convert TrueVision Targa file to portable pixmap
-    ximtoppm    convert Xim to portable pixmap
-    xpmtoppm    convert XPM format to portable pixmap
-    xvminitoppm convert a XV "thumbnail" picture to PPM
-    yuvtoppm    convert Abekas YUV format to portable pixmap
-
-    ppm3d       convert 2 portable pixmap to a red/blue 3d glasses pixmap
-    ppmbrighten change images Saturation and Value from an HSV map
-    ppmchange   change pixels of one color to another in a portable pixmap
-    ppmdim      dim a portable pixmap down to total blackness
-    ppmdist     simple grayscale for machine generated, color images
-    ppmdither   ordered dither for color images
-    ppmflash    brighten a picture up to complete white-out
-    ppmforge    fractal forgeries of clouds, planets, and starry skies
-    ppmhist     print a histogram of a portable pixmap
-    ppmmake     create a pixmap of a specified size and color
-    ppmmix      blend together two portable pixmaps
-    ppmpat      create a pretty pixmap
-    ppmquant    quantize colors down to a specified number
-    ppmqvga     8 plane quantization
-    ppmrelief   run a Laplacian Relief filter on a portable pixmap
-    ppmshift    shift lines of a portable pixmap left or right by a
-                random amount
-    ppmspread   displace a portable pixmap's pixels by a random amount
-
-3 pnm
-    pnmtoddif   convert portable anymap to DDIF format
-    pnmtofits   convert a portable anymap into FITS format
-    pnmtops     convert portable anymap to PostScript
-    pnmtorast   convert portable anymap to Sun raster file
-    pnmtotiff   convert portable anymap to TIFF file
-    pnmtoxwd    convert portable anymap to X11 window dump
-    fitstopnm   convert a FITS file into a portable anymap
-    rasttopnm   convert Sun raster file to portable anymap
-    tifftopnm   convert TIFF file to portable anymap
-    xwdtopnm    convert X10 or X11 window dump to portable anymap
-    pnmtosir    convert a portable anymap into a Solitaire format
-    sirtopnm    convert a Solitaire file into a portable anymap
-    zeisstopnm  convert a Zeiss confocal file into a portable anymap
-
-    pnmalias    antialias a portable anyumap.
-    pnmarith    perform arithmetic on two portable anymaps
-    pnmcat      concatenate portable anymaps
-    pnmcomp     composite two portable anymap files together
-    pnmconvol   general MxN convolution on a portable anymap
-    pnmcrop     crop all like-colored borders off a portable anymap
-    pnmcut      select a rectangular region from a portable anymap
-    pnmdepth    change the maxval in a portable anymap
-    pnmenlarge  enlarge a portable anymap N times
-    pnmfile     describe a portable anymap
-    pnmflip     perform one or more flip operations on a portable anymap
-    pnmgamma    perform gamma correction on a portable anymap
-    pnmhistmap  draw a histogram for a PGM or PPM file
-    pnminvert   invert a portable anymap
-    pnmnlfilt   non-linear filters: smooth, alpha trim mean,
-                optimal estimation smoothing, edge enhancement
-    pnmnoraw    force a portable anymap into ASCII format
-    pnmpad      add borders to portable anymap
-    pnmpaste    paste a rectangle into a portable anymap
-    pnmrotate   rotate a portable anymap
-    pnmscale    scale a portable anymap
-    pnmshear    shear a portable anymap
-    pnmtile     replicate a portable anymap into a specified size
-
-2 See_Also
-        There are a number of related image-manipulation tools:
-
-    IM Raster Toolkit
-        A portable and efficient format toolkit.   The format supports
-    pixels  of  arbitrary  channels,  components, and bit  precisions,
-    while  allowing  compression  and machine byte-order independence.
-    Support for  image  manipulation,  digital  halftoning, and format
-    conversion.  Previously  distributed on tape c/o the University of
-    Waterloo (an ftp version is to appear later).  Author:  Alan Paeth
-    (awpaeth@watcgl.uwaterloo.ca).
-
-    Utah RLE Toolkit
-        Conversion  and  manipulation  package,  similar  to  PBMPLUS.
-    Available  via  ftp  as  cs.utah.edu:   pub/toolkit-2.0.tar.Z  and
-    ucsd.edu:  graphics/utah-raster-toolkit.tar.Z.
-
-    Fuzzy Pixmap Manipulation
-        Conversion  and  manipulation  package,  similar  to  PBMPLUS.
-    Version     1.0    available    via    ftp    as    nl.cs.cmu.edu:
-    /usr/mlm/ftp/fbm.tar.Z,  uunet.uu.net:        pub/fbm.tar.Z,   and
-    ucsd.edu:    graphics/fbm.tar.Z.       Author:    Michael  Mauldin
-    (mlm@nl.cs.cmu.edu).
-
-    Img Software Set
-        Reads and  writes  its own image format, displaying results on
-    an X11 screen,  and does some image manipulations.  Version 1.3 is
-    available  via  ftp  as    ftp.x.org:contrib/img_1.3.tar.Z,    and
-    venera.isi.edu:pub/img_1.3.tar.Z, along with a large collection of
-    color images.  Author:  Paul Raveling (raveling@venera.isi.edu).
-
-    Xim
-        Reads and writes its own image  format,  displays  on  an  X11
-    screen,  and  does some image manipulations.   Available  in  your
-    nearest  X11R4  source  tree  as it contrib/clients/xim.   A  more
-    recent version is available via ftp from video.mit.edu.   It  uses
-    X11R4 and the OSF/Motif toolkit to provide basic interactive image
-    manipulation  and  reads/writes GIF, xwd, xbm, tiff, rle, xim, and
-    other formats.  Author:  Philip R.  Thompson.
-
-    xloadimage
-        Reads in images in various formats and displays them on an X11
-    screen.  Available  via  ftp as ftp.x.org:contrib/xloadimage*, and
-    in  your nearest comp.sources.x  archive.    Author:    Jim  Frost
-    (madd@std.com).
-
-    TIFF Software
-        Nice portable library for reading and writing TIFF files, plus
-    a few tools for  manipulating  them  and  reading  other  formats.
-    Available    via    ftp    as   sgi.com:pub/graphics/*.tar.Z    or
-    uunet.uu.net:graphics/tiff.tar.Z. Author: Sam Leffler (sam@sgi.com).
-
-    ALV
-        A  Sun-specific  image  toolkit.    Version  2.0.6  posted  to
-    comp.sources.sun on 11 December 1989.  Also available via email to
-    alv-users-request@cs.bris.ac.uk.
-
-    popi
-        An  image  manipulation  language.    Version  2.1  posted  to
-    comp.sources.misc on 12 December 1989.
-
-    ImageMagick
-        X11  package  for  display  and  interactive  manipulation  of
-    images.  Uses its own format (MIFF), and includes some converters.
-    Available via ftp as ftp.x.org:contrib/ImageMagick.tar.Z.
-
-    Khoros
-        Huge  (~100  meg)  graphical  development environment based on
-    X11R4.    Components  include  a visual programming language, code
-    generators for  extending  the  visual  language  and  adding  new
-    application packages to  the system, an interactive user interface
-    editor, an interactive image display package, an extensive library
-    of  image  and  signal processing  routines,  and  2D/3D  plotting
-    packages.  Available via ftp as pprg.unm.edu:pub/khoros/*.
-
-    JPEG package
-        JPEG is a a standardized compression method for full-color and
-    gray-scale  images  of "real-world"  scenes;    this  experimental
-    package includes programs to compress  gif and ppm format files to
-    JPEG  format  ( cjpeg(1L)), and to  decompress  them  (djpeg(1L)).
-    Available by ftp as uunet.uu.net:graphics/jpeg/jpegsrc.v1.tar.Z.
-
-        libpbm(3L),  libpgm(3L),    libpnm(3L),  libppm(3L),  pbm(5L),
-    pgm(5L), pnm(5L), ppm(5L), rasterfile(1)
-
-2 Author
-        Distribution of 1 December 1991.   Copyright 1989, 1991 by Jef
-    Poskanzer.
-
-        Feedback and questions are welcome.  Please send them to:
-
-                             jef@well.sf.ca.us
-                              apple!well!jef
-
-        When  sending  bug  reports,  always  include  the output from
-    running  any  pbmplus  program  with  the -version flag, including
-    descriptions of  the  type  of system you are on, the compiler you
-    use, and whether you are using Makefiles or Imakefiles.
-
-        When  suggesting  new  formats  or  features,  please  include
-    whatever documentation you have,  and  a  uuencoded  sample.   The
-    response time will depend upon  my  schedule and the complexity of
-    the task;  if you need  it right away, or it is a complicated job,
-    you might consider paying me.
-
-        The  Usenet  newsgroup  alt.graphics.pixutils  is a forum  for
-    discussion  of  image  conversion  and editing packages.   Posting
-    queries  there  may be better than mailing them to  me,  since  it
-    allows other people to help provide answers.
-
-        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.  Thus, you
-    may do what you want with this  software.    Build  it  into  your
-    package, steal code from it, whatever.  Just be sure to let people
-    know where it came from.
-
-1 asciitopgm
-        asciitopgm - convert ASCII graphics into a portable graymap
-
-2 Synopis
-        asciitopgm [-d divisor] height width [asciifile]
-
-2 Description
-        Reads  ASCII data as input.  Produces a portable graymap  with
-    pixel values which are an approximation of the "brightness" of the
-    ASCII  characters,  assuming black-on-white printing.    In  other
-    words, a capital M is very  dark,  a  period  is  ver light, and a
-    space is white.  Input lines which are fewer than width characters
-    are automatically padded with spaces.
-
-        The divisor argument is a floating-point number by  which  the
-    output pixels are divided;  the default value is 1.0.  This can be
-    used to adjust the brightness of the graymap:  for example, if the
-    image is too dim, reduce the divisor.
-
-        In keeping with  (I believe) Fortran line-printer conventions,
-    input lines beginning with  a  +  (plus)  character are assumed to
-    "overstrike" the previous line, allowing  a  larger  range of gray
-    values.
-
-        This tool contradicts the message in  the  pbmtoascii  manual:
-    "Note that there is no asciitopbm tool  -  this  transformation is
-    one-way."
-
-2 Bugs
-        The    table    of    ASCII-to-grey    values  is  subject  to
-    interpretation,  and,  of course, depends on the typeface intended
-    for the input.
-
-2 See_Also
-        pbmtoascii(1), pgm(5)
-
-2 Author
-        Wilson H. Bent. Jr. (whb@usc.edu)
-
-1 atktopbm
-     atktopbm  -  convert  Andrew  Toolkit  raster  object to portable
-     bitmap
-
-2 Synopsis
-     atktopbm [atkfile]
-
-2 Description
-     Reads an Andrew Toolkit raster object as input.  Produces  a
-     portable bitmap as output.
-
-2 See_Also
-     pbmtoatk, pbm
-
-2 Author
-     Copyright (C) 1991 by Bill Janssen.
-
-1 brushtopbm
-     brushtopbm - convert a doodle brush  file  into  a  portable
-     bitmap
-
-2 Synopsis
-     brushtopbm [brushfile]
-
-2 Description
-     Reads a Xerox doodle brush file as input.  Produces a  port-
-     able bitmap as output.
-
-     Note that there is currently no pbmtobrush tool.
-
-2 See_Also
-     pbm
-
-2 Author
-     Copyright (C) 1988 by Jef Poskanzer.
-
-1 cmuwmtopbm
-     cmuwmtopbm - convert a CMU  window  manager  bitmap  into  a
-     portable bitmap
-
-2 Synopsis
-     cmuwmtopbm [cmuwmfile]
-
-2 Description
-     Reads a CMU window manager  bitmap  as  input.   Produces  a
-     portable bitmap as output.
-
-2 See_Also
-     pbmtocmuwm, pbm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 g3topbm
-     g3topbm - convert a Group 3 fax file into a portable bitmap
-
-2 Synopsis
-     g3topbm [-kludge] [-reversebits] [-stretch] [g3file]
-
-2 Description
-     Reads a Group 3 fax file as input.  Produces a portable bit-
-     map as output.
-
-2 Options
-     -kludge
-          Tells g3topbm to ignore the  first  few  lines  of  the
-          file;  sometimes fax files have some junk at the begin-
-          ning.
-
-     -reversebits
-          Tells  g3topbm  to  interpret  bits   least-significant
-          first,  instead  of the default most-significant first.
-          Apparently some fax modems do it one way and others  do
-          it  the  other  way.   If you get a whole bunch of "bad
-          code word" messages, try using this flag.
-
-     -stretch
-          Tells g3topbm to stretch the image vertically by dupli-
-          cating each row.  This is for the low-quality transmis-
-          sion mode.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 References
-     The standard for Group 3 fax is defined in CCITT Recommenda-
-     tion T.4.
-
-2 Bugs
-     Probably.
-
-2 See_Also
-     pbmtog3, pbm
-
-2 Author
-     Copyright (C) 1989 by Paul Haeberli <paul@manray.sgi.com>.
-
-1 icontopbm
-     icontopbm - convert a Sun icon into a portable bitmap
-
-2 Synopsis
-     icontopbm [iconfile]
-
-2 Description
-     Reads a Sun icon as input.  Produces a  portable  bitmap  as
-     output.
-
-2 See_Also
-     pbmtoicon, pbm
-
-2 Author
-     Copyright (C) 1988 by Jef Poskanzer.
-
-1 gemtopbm
-     gemtopbm - convert a GEM .img file into a portable bitmap
-
-2 Synopsis
-     gemtopbm [-d] gemfile
-
-2 Description
-     Reads a GEM .img file as input.  Produces a portable  bitmap
-     as output.
-
-2 Options
-     -d   Produce output describing  the  contents  of  the  .img
-          file.
-
-2 Bugs
-     Does not support file containing more than one plane.  Can't
-     read from standard input.
-
-2 See_Also
-     pbmtogem, pbm
-
-2 Author
-     Copyright (C) 1988 Diomidis D. Spinellis (dds@cc.ic.ac.uk).
-
-1 macptopbm
-     macptopbm - convert a MacPaint file into a portable bitmap
-
-2 Synopsis
-     macptopbm [-extraskip N] [macpfile]
-
-2 Description
-     Reads a MacPaint file as input.  Produces a portable  bitmap
-     as output.
-
-2 Options
-     -extraskip
-          This flag is to get around a problem with some  methods
-          of  transferring  files  from the Mac world to the Unix
-          world.  Most of  these  methods  leave  the  Mac  files
-          alone, but a few of them add the "finderinfo" data onto
-          the front of the Unix file.  This means  an  extra  128
-          bytes  to skip over when reading the file.  The symptom
-          to watch for is  that  the  resulting  PBM  file  looks
-          shifted  to  one side.  If you get this, try -extraskip
-          128, and if that still doesn't look right  try  another
-          value.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     picttoppm, pbmtomacp, pbm
-
-2 Author
-     Copyright (C) 1988 by Jef Poskanzer.   The  MacPaint-reading
-     code   is   copyright   (c)  1987  by  Patrick  J.  Naughton
-     (naughton@wind.sun.com).
-
-1 mgrtopbm
-     mgrtopbm - convert a MGR bitmap into a portable bitmap
-
-2 Synopsis
-     mgrtopbm [mgrfile]
-
-2 Description
-     Reads a MGR bitmap as input.  Produces a portable bitmap  as
-     output.
-
-2 See_Also
-     pbmtomgr, pbm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pbmclean
-     pbmclean - flip isolated pixels in portable bitmap
-
-2 Synopsis
-     pbmclean [-connect] [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input. Outputs a portable  bitmap
-     with  every  pixel  which  has  less  than connect identical
-     neighbours inverted.  Pbmclean  can  be  used  to  clean  up
-     "snow" on bitmap images.
-
-2 See_Also
-     pbm
-
-2 Author
-     Copyright (C) 1990 by Angus Duggan 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 docu-
-     mentation.   This  software  is  provided  "as  is"  without
-     express or implied warranty.
-
-1 pbmlife
-     pbmlife - apply Conway's rules of Life to a portable bitmap
-
-2 Synopsis
-     pbmlife [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Applies the rules of Life
-     to  it for one generation, and produces a portable bitmap as
-     output.
-
-     A white pixel in the image is interpreted as a live beastie,
-     and a black pixel as an empty space.
-
-2 See_Also
-     pbm
-
-2 Author
-     Copyright (C) 1988, 1991 by Jef Poskanzer.
-
-1 pbmmake
-     pbmmake - create a blank bitmap of a specified size
-
-2 Synopsis
-     pbmmake [-white|-black|-gray ] width height
-
-2 Description
-     Produces a  portable  bitmap  of  the  specified  width  and
-     height.  The color defaults to white.
-
-2 Options
-     In addition to the usual -white  and  -black,  this  program
-     implements -gray.  This gives a simple 50% gray pattern with
-     1's and 0's alternating.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     pbm, ppmmake
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pbmmask
-     pbmmask - create a mask bitmap from a regular bitmap
-
-2 Synopsis
-     pbmmask [-expand] [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Creates  a  corresponding
-     mask bitmap and writes it out.
-
-     The color to be interpreted as  "background"  is  determined
-     automatically.  Regardless of which color is background, the
-     mask will be white where the background is and  black  where
-     the figure is.
-
-     This lets you do a masked paste like this, for objects  with
-     a black background:
-         pbmmask obj > objmask
-         pnmpaste < dest -and objmask <x> <y> | pnmpaste -or obj <x> <y>
-     For objects with a white background, you can  either  invert
-     them or add a step:
-         pbmmask obj > objmask
-         pnminvert objmask | pnmpaste -and obj 0 0 > blackback
-         pnmpaste < dest -and objmask <x> <y> | pnmpaste -or blackback <x> <y>
-     Note that this three-step version  works  for  objects  with
-     black  backgrounds  too,  if you don't care about the wasted
-     time.
-
-     You can also use masks with graymaps and pixmaps, using  the
-     pnmarith tool.  For instance:
-         ppmtopgm obj.ppm | pgmtopbm -threshold | pbmmask > objmask.pbm
-         pnmarith -multiply dest.ppm objmask.pbm > t1.ppm
-         pnminvert objmask.pbm | pnmarith -multiply obj.ppm - > t2.ppm
-         pnmarith -add t1.ppm t2.ppm
-     An interesting variation on this is to pipe the mask through
-     the  pnmsmooth script before using it.  This makes the boun-
-     dary between the two images less sharp.
-
-     -expand
-          Expands the mask by one pixel out from the image.  This
-          is useful if you want a little white border around your
-          image.  (A better solution might be to turn the pbmlife
-          tool into a general cellular automaton tool...)
-
-2 See_Also
-     pnmpaste, pnminvert, pbm, pnmarith, pnmsmooth
-
-2 Author
-     Copyright (C) 1988 by Jef Poskanzer.
-
-1 pbmpscale
-     pbmpscale - enlarge a portable bitmap with edge smoothing
-
-2 Synopsis
-     pbmpscale N [ pbmfile ]
-
-2 Description
-     Reads a portable bitmap as input,  and  outputs  a  portable
-     bitmap enlarged N times. Enlargement is done by pixel repli-
-     cation, with some additional smoothing of corners and edges.
-
-2 See_Also
-     pnmenlarge, ppmscale, pbm
-
-2 Author
-     Copyright (C) 1990 by Angus Duggan 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 docu-
-     mentation.   This  software  is  provided  "as  is"  without
-     express or implied warranty.
-
-2 Notes
-     pbmpscale works best for  enlargements  of  2.  Enlargements
-     greater  than  2 should be done by as many enlargements of 2
-     as possible, followed by an  enlargement  by  the  remaining
-     factor.
-
-1 pbmreduce
-     pbmreduce - read a portable bitmap and reduce it N times
-
-2 Synopsis
-     pbmreduce [-floyd|-fs|-threshold ] [-value val] N [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Reduces it by a factor of
-     N, and produces a portable bitmap as output.
-
-     pbmreduce duplicates a lot of the functionality of pgmtopbm;
-     you  could  do  something  like  pnmscale  |  pgmtopbm,  but
-     pbmreduce is a lot faster.
-
-     pbmreduce can be used to "re-halftone" an image.  Let's  say
-     you  have  a  scanner  that  only  produces black&white, not
-     grayscale, and it does a terrible job  of  halftoning  (most
-     b&w  scanners  fit  this  description).   One way to fix the
-     halftoning is to scan at the  highest  possible  resolution,
-     say  300  dpi,  and  then  reduce by a factor of three or so
-     using pbmreduce.  You can even correct the brightness of  an
-     image, by using the -value flag.
-
-2 Options
-     By default, the halftoning after the reduction is  done  via
-     boustrophedonic  Floyd-Steinberg  error  diffusion; however,
-     the -threshold flag can be used to specify simple threshold-
-     ing.  This gives better results when reducing line drawings.
-
-     The -value flag alters the thresholding value for all quant-
-     izations.   It  should  be  a  real  number between 0 and 1.
-     Above 0.5 means darker images; below 0.5 means lighter.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     pnmenlarge, pnmscale, pgmtopbm, pbm
-
-2 Author
-     Copyright (C) 1988 by Jef Poskanzer.
-
-1 pbmtext
-     pbmtext - render text into a bitmap
-
-2 Synopsis
-     pbmtext [-font fontfile] [text]
-
-2 Description
-     Takes the specified text, either a single line from the com-
-     mand line or multiple lines from standard input, and renders
-     it into a bitmap.
-
-2 Options
-     By default, pbmtext uses a  built-in  font.   You  can  also
-     specify  your own font with the -font flag.  The fontfile is
-     a pbm file, created in a very specific way.  In your  window
-     system  of choice, display the following text in the desired
-     (fixed-width) font:
-
-         M ",/^_[`jpqy| M
-
-         /  !"#$%&'()*+ /
-         < ,-./01234567 <
-         > 89:;<=>?@ABC >
-         @ DEFGHIJKLMNO @
-         _ PQRSTUVWXYZ[ _
-         { \]^_`abcdefg {
-         } hijklmnopqrs }
-         ~ tuvwxyz{|}~  ~
-
-         M ",/^_[`jpqy| M
-
-     Do a screen grab or window dump  of  that  text,  using  for
-     instance  xwd,  xgrabsc,  or screendump.  Convert the result
-     into a pbm file.  If necessary, use pnmcut to remove  every-
-     thing  except  the text.  Finally, run it through pnmcrop to
-     make sure the edges are right up against the text.   pbmtext
-     can figure out the sizes and spacings from that.
-
-2 See_Also
-     pbm, pnmcut, pnmcrop
-
-2 Author
-     Copyright (C) 1991 by Jef Poskanzer.
-
-1 pbmto4425
-     pbmto4425 - Display PBM images on an AT&T 4425 terminal
-
-2 Synopsis
-     pbmto4425 [pbmfile]
-
-2 Description
-     Pbmto4425 displays PBM format images on an AT&T  4425  ASCII
-     terminal  using  that  terminal's  mosaic graphics character
-     set.  The program should also  work  with  other  VT100-like
-     terminals with mosaic graphics character sets such as the C.
-     Itoh CIT-101, but it has not yet been  tested  on  terminals
-     other than the 4425.
-
-     Pbmto4425 puts the terminal into 132 column mode to  achieve
-     the  maximum  resolution  of the terminal.  In this mode the
-     terminal has a resolution of 264 columns by  69  rows.   The
-     pixels  have  an  aspect  ratio of 1:2.6, therefore an image
-     should be processed before being displayed in a manner  such
-     as this:
-
-          % pnmscale -xscale 2.6 pnmfile \
-               | pnmscale -xysize 264 69 \
-               | ppmtopgm \
-               | pgmtopbm \
-               | pbmto4425
-
-2 Author
-     Copyright (C) 1993 by Robert Perlberg
-
-1 pbmto10x
-     pbmto10x - convert a portable bitmap into Gemini 10X printer
-     graphics
-
-2 Synopsis
-     pbmto10x [-h] [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces a file of Gemini
-     10X printer graphics as output.  The 10x's printer codes are
-     alleged to be similar to the Epson codes.
-
-     Note that there is no 10xtopbm tool - this transformation is
-     one way.
-
-2 Options
-     The resolution is normally 60H by 72V.  If the  -h  flag  is
-     specified, resolution is 120H by 144V.  You may find it use-
-     ful to rotate landscape images before printing.
-
-2 See_Also
-     pbm
-
-2 Author
-     Copyright (C) 1990 by Ken Yap
-
-1 pbmtoascii
-     pbmtoascii - convert a portable bitmap into ASCII graphics
-
-2 Synopsis
-     pbmtoascii [-1x2|-2x4] [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces a somewhat crude
-     ASCII graphic as output.
-
-     Note that there is no asciitopbm tool - this  transformation
-     is one-way.
-
-2 Options
-     The -1x2 and -2x4 flags give you two alternate ways for  the
-     bits  to  get  mapped to characters.  With 1x2, the default,
-     each character represents a group of 1 bit across by 2  bits
-     down.  With -2x4, each character represents 2 bits across by
-     4 bits down.  With the 1x2 mode you can see  the  individual
-     bits,  so it's useful for previewing small bitmaps on a non-
-     graphics terminal.  The 2x4 mode  lets  you  display  larger
-     bitmaps  on  a  standard  80-column display, but it obscures
-     bit-level details.  2x4 mode is  also  good  for  displaying
-     graymaps  -  "pnmscale  -width  158  |  pgmnorm  |  pgmtopbm
-     -thresh" should give good results.
-
-2 See_Also
-     pbm
-
-2 Author
-     Copyright (C) 1988, 1992 by Jef Poskanzer.
-
-1 pbmtoatk
-     pbmtoatk - convert portable bitmap to Andrew Toolkit  raster
-     object
-
-2 Synopsis
-     pbmtoatk [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces a Andrew Toolkit
-     raster object as output.
-
-2 See_Also
-     atktopbm, pbm
-
-2 Author
-     Copyright (C) 1991 by Bill Janssen.
-
-1 pbmtobg
-     pbmtobg - convert a portable bitmap into BitGraph graphics
-
-2 Synopsis
-     pbmtobg [rasterop] [x y] < pbmfile
-
-2 Description
-     Reads a portable bitmap as  input.   Produces  BBN  BitGraph
-     terminal Display Pixel Data (DPD) sequence as output.
-
-     The rasterop can be specified on the command line.  If  this
-     is  omitted,  3 (replace) will be used.  A position in (x,y)
-     coordinates can also be specified.  If both are  given,  the
-     rasterop  comes  first.  The portable bitmap is always taken
-     from the standard input.
-
-     Note that there is no bgtopbm tool.
-
-2 See_Also
-     pbm
-
-2 Author
-     Copyright 1989 by Mike Parker.
-
-1 pbmtocmuwm
-     pbmtocmuwm - convert a portable bitmap  into  a  CMU  window
-     manager bitmap
-
-2 Synopsis
-     pbmtocmuwm [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.   Produces  a  CMU  window
-     manager bitmap as output.
-
-2 See_Also
-     cmuwmtopbm, pbm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pbmtoepsi
-     pbmtoepsi - convert a portable bitmap into  an  encapsulated
-     PostScript style preview bitmap
-
-2 Synopsis
-     pbmtoepsi [-bbonly] [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produce  an  encapsulated
-     Postscript style bitmap as output. The output is not a stand
-     alone postscript file, it is only a  preview  bitmap,  which
-     can  be  included  in an encapsulated PostScript file.  Note
-     that there is no epsitopbm tool - this transformation is one
-     way.
-
-     This utility is a part of the pstoepsi tool by Doug  Crabill
-     (dgc@cs.purdue.edu).
-
-2 Options
-     -bbonly
-          Only create a boundary box,  don't  fill  it  with  the
-          image.
-
-2 See_Also
-     pbm, pnmtops, psidtopgm
-
-2 Author
-     Copyright (C) 1988 Jef Poskanzer, modified by  Doug  Crabill
-     1992
-
-1 pbmtoepson
-     pbmtoepson - convert a portable bitmap  into  Epson  printer
-     graphics
-
-2 Synopsis
-     pbmtoepson [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces a file of  Epson
-     printer graphics as output.
-
-     Note that there is no epsontopbm tool - this  transformation
-     is one way.
-
-2 See_Also
-     pbm
-
-2 Author
-     Copyright      (C)      1991      by       John       Tiller
-     (tiller@galois.msfc.nasa.gov) and Jef Poskanzer.
-
-1 pbmtog3
-     pbmtog3 - convert a portable bitmap into a Group 3 fax file
-
-2 Synopsis
-     pbmtog3 [pbmfile]
-
-2 Description
-     Reads a portable bitmap as output.  Produces a Group  3  fax
-     file as input.
-
-REFERENCES
-     The standard for Group 3 fax is defined in CCITT Recommenda-
-     tion T.4.
-
-2 Bugs
-     Probably.
-
-2 See_Also
-     g3topbm, pbm
-
-2 Author
-     Copyright (C) 1989 by Paul Haeberli <paul@manray.sgi.com>.
-
-1 pbmtogem
-     pbmtogem - convert a portable bitmap into a GEM .img file
-
-2 Synopsis
-     pbmtogem [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces a GEM .img  file
-     as output.
-
-2 Bugs
-     It does not support compression of the data.
-
-2 See_Also
-     gemtopbm, pbm
-
-2 Author
-     Copyright (C) 1988 by David Beckemeyer (bdt!david)  and  Jef
-     Poskanzer.
-
-1 pbmtogo
-     pbmtogo - convert a portable bitmap into compressed  GraphOn
-     graphics
-
-2 Synopsis
-     pbmtogo [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.   Produces  2D  compressed
-     GraphOn  graphics as output.  Be sure to set up your GraphOn
-     with the following modes: 8  bits  /  no  parity;  obeys  no
-     XON/XOFF;  NULs  are  accepted.   These  are all on the Comm
-     menu.  Also, remember to turn off tty post processing.  Note
-     that there is no gotopbm tool.
-
-2 See_Also
-     pbm
-
-2 Author
-     Copyright (C) 1988, 1989 by Jef Poskanzer, Michael Haberler,
-     and Bo Thide'.
-
-1 pbmtoicon
-     pbmtoicon - convert a portable bitmap into a Sun icon
-
-2 Synopsis
-     pbmtoicon [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces a  Sun  icon  as
-     output.
-
-2 See_Also
-     icontopbm, pbm
-
-2 Author
-     Copyright (C) 1988 by Jef Poskanzer.
-
-1 pbmtolj
-     pbmtolj - convert a portable bitmap into HP LaserJet format
-
-2 Synopsis
-     pbmtolj [-resolution N] [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces HP LaserJet data
-     as output.
-
-     Note that there is no ljtopbm tool.
-
-2 Options
-     -resolution
-          Specifies the resolution of the output device, in  dpi.
-          Typical  values  are 75, 100, 150, 300.  The default is
-          75.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     pbm
-
-2 Author
-     Copyright (C) 1988 by Jef Poskanzer and Michael Haberler.
-
-1 pbmtoln03
-     pbmtoln03 - convert protable bitmap to DEC LN03+ Sixel  out-
-     put
- 
-2 Synopsis
-     pbmtoln03 [-rltbf] pbmfile
- 
-2 Description
-     Reads a portable bitmap as  input.   Produces  a  DEC  LN03+
-     Sixel output file.
- 
-2 Options
-     -l nn
-          Use "nn" as value for left margin (default 0).
- 
-     -r nn
-          Use "nn" as value for right margin (default 2400).
- 
-     -t nn
-          Use "nn" as value for top margin (default 0).
- 
-     -b nn
-          Use "nn" as value for bottom margin (default 3400).
- 
-     -f nn
-          Use "nn" as value for form length (default 3400).
- 
-2 See_Also
-     pbm
- 
-2 Author
-     Tim Cook, 26 Feb 1992
- 
-1 pbmtolps
-     pbmtolps - convert portable bitmap to PostScript
-
-2 Synopsis
-     pbmtolps [ -dpi n ] [ pbmfile ]
-
-2 Description
-     Reads a portable bitmap as input,  and  outputs  PostScript.
-     The output Postscript uses lines instead of the image opera-
-     tor to generate a (device dependent) picture which  will  be
-     imaged much faster.
-
-     The Postscript path length is constrained to  be  less  that
-     1000  points  so  that  no  limits  are overrun on the Apple
-     Laserwriter and (presumably) no other printers.
-
-2 See_Also
-     pgmtops, ppmtops, pbm
-
-2 Author
-     George Phillips <phillips@cs.ubc.ca>
-
-1 pbmtomacp
-     pbmtomacp - convert a portable bitmap into a MacPaint file
-
-2 Synopsis
-     pbmtomacp  [-l  left]  [-r  right]  [-b  bottom]  [-t   top]
-     [pbmfile]
-
-2 Description
-     Reads a portable bitmap  as  input.   If  no  input-file  is
-     given,  standard input is assumed.  Produces a MacPaint file
-     as output.
-
-     The generated file is only the data fork of a picture.   You
-     will  need  a program such as mcvert to generate a Macbinary
-     or a BinHex file that contains the necessary information  to
-     identify the file as a PNTG file to MacOS.
-
-2 Options
-     Left, right, bottom & top let you define a square  into  the
-     pbm  file,  that  must  be  converted.  Default is the whole
-     file.  If the file is too large  for  a  MacPaint-file,  the
-     bitmap is cut to fit from ( left, top ).
-
-2 Bugs
-     The source code contains comments in a language  other  than
-     English.
-
-2 See_Also
-     ppmtopict, macptopbm, pbm, mcvert
-
-2 Author
-     Copyright   (C)   1988   by    Douwe    van    der    Schaaf
-     (...!mcvax!uvapsy!vdschaaf).
-
-1 pbmtomgr
-     pbmtomgr - convert a portable bitmap into a MGR bitmap
-
-2 Synopsis
-     pbmtomgr [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces a MGR bitmap  as
-     output.
-
-2 See_Also
-     mgrtopbm, pbm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pbmtopi3
-     pbmtopi3 - convert a portable bitmap  into  an  Atari  Degas
-     .pi3 file
-
-2 Synopsis
-     pbmtopi3 [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces an  Atari  Degas
-     .pi3 file as output.
-
-2 See_Also
-     pi3topbm, pbm, ppmtopi1, pi1toppm
-
-2 Author
-     Copyright (C) 1988 by David Beckemeyer (bdt!david)  and  Jef
-     Poskanzer.
-
-1 pbmtoplot
-     pbmtoplot - convert a portable bitmap into  a  Unix  plot
-     file
-
-2 Synopsis
-     pbmtoplot [pbmfile]
-
-2 Description
-     Reads a portable bitmap as  input.   Produces  a  Unix  plot
-     file.
-
-     Note that there is no plottopbm tool -  this  transformation
-     is one-way.
-
-2 See_Also
-     pbm, plot
-
-2 Author
-     Copyright (C) 1990 by Arthur David Olson.
-
-1 pbmtoptx
-     pbmtoptx - convert a portable bitmap into Printronix printer
-     graphics
-
-2 Synopsis
-     pbmtoptx [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces a file of  Prin-
-     tronix printer graphics as output.
-
-     Note that there is no ptxtopbm tool - this transformation is
-     one way.
-
-2 See_Also
-     pbm
-
-2 Author
-     Copyright (C) 1988 by Jef Poskanzer.
-
-1 pbmtox10bm
-     pbmtox10bm - convert a portable bitmap into an X10 bitmap
-
-2 Synopsis
-     pbmtox10bm [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces an X10 bitmap as
-     output.  This older format is maintained for compatibility.
-
-     Note that there is no x10bmtopbm tool, because xbmtopbm  can
-     read both X11 and X10 bitmaps.
-
-2 See_Also
-     pbmtoxbm, xbmtopbm, pbm
-
-2 Author
-     Copyright (C) 1988 by Jef Poskanzer.
-
-1 pbmtoxbm
-     pbmtoxbm - convert a portable bitmap into an X11 bitmap
-
-2 Synopsis
-     pbmtoxbm [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces an X11 bitmap as
-     output.
-
-2 See_Also
-     pbmtox10bm, xbmtopbm, pbm
-
-2 Author
-     Copyright (C) 1988 by Jef Poskanzer.
-
-1 pbmtoybm
-     pgmtoybm - convert a  portable  bitmap  into  a  Bennet  Yee
-     "face" file
-
-2 Synopsis
-     pbmtoybm [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces as output a file
-     acceptable  to  the  face  and  xbm  programs  by Bennet Yee
-     (bsy+@cs.cmu.edu).
-
-2 See_Also
-     ybmtopbm, pbm, face, face, xbm
-
-2 Author
-     Copyright (C) 1991 by Jamie Zawinski and Jef Poskanzer.
-
-1 pbmtozinc
-     pbmtozinc - convert a portable bitmap into a Zinc bitmap
-
-2 Synopsis
-     pbmtozinc [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input.  Produces a bitmap in  the
-     format  used by the Zinc Interface Library (ZIL) Version 1.0
-     as output.
-
-2 See_Also
-     pbm
-
-2 Author
-     Copyright   (C)   1988    by    James    Darrell    McCauley
-     (jdm5548@diamond.tamu.edu) and Jef Poskanzer.
-
-1 pbmupc
-     pbmupc - create a Universal Product Code bitmap
-
-2 Synopsis
-     pbmupc [-s1|-s2] type manufac product
-
-2 Description
-     Generates a Universal Product Code symbol.  The three  argu-
-     ments  are:  a one digit product type, a five digit manufac-
-     turer code, and a five digit product code.  For example,  "0
-     72890 00011" is the code for Heineken.
-
-     As presently configured, pbmupc produces a bitmap  230  bits
-     wide and 175 bits high.  The size can be altered by changing
-     the defines at the beginning of the program, or  by  running
-     the output through pnmenlarge or pnmscale.
-
-2 Options
-     The -s1 and -s2 flags select the style of UPC  to  generate.
-     The default, -s1, looks more or less like this:
-      ||||||||||||||||
-      ||||||||||||||||
-      ||||||||||||||||
-      ||||||||||||||||
-     0||12345||67890||5
-     The other style, -s2, puts the product type digit higher up,
-     and doesn't display the checksum digit:
-      ||||||||||||||||
-      ||||||||||||||||
-     0||||||||||||||||
-      ||||||||||||||||
-      ||12345||67890||
-
-2 See_Also
-     pbm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pi3topbm
-     pi3topbm - convert an Atari Degas .pi3 file into a  portable
-     bitmap
-
-2 Synopsis
-     pi3topbm [pi3file]
-
-2 Description
-     Reads an Atari Degas .pi3 file as input.  Produces  a  port-
-     able bitmap as output.
-
-2 See_Also
-     pbmtopi3, pbm, pi1toppm, ppmtopi1
-
-2 Author
-     Copyright (C)  1988  by  David  Beckemeyer  (bdt!david)  and
-     Diomidis D. Spinellis.
-
-1 pktopbm
-     pktopbm - convert packed  (PK)  format  font  into  portable
-     bitmap(s)
-
-2 Synopsis
-     pktopbm pkfile[.pk] [-c num] pbmfile ...
-
-2 Description
-     Reads a packed (PK) font file as input, and  produces  port-
-     able  bitmaps as output. If the filename "-" is used for any
-     of the filenames, the standard  input  stream  (or  standard
-     output where appropriate) will be used.
-
-2 Options
-     -c num
-          Sets the character number of the next bitmap written to
-          num.
-
-2 See_Also
-     pbmtopk, pbm
-
-2 Author
-     Adapted  from  Tom  Rokicki's   pxtopk   by   Angus   Duggan
-     <ajcd@uk.ac.ed.lfcs>.
-
-1 xbmtopbm
-     xbmtopbm - convert an X11 or X10 bitmap into a portable bit-
-     map
-
-2 Synopsis
-     xbmtopbm [bitmapfile]
-
-2 Description
-     Reads an X11 or X10 bitmap as input.   Produces  a  portable
-     bitmap as output.
-
-2 See_Also
-     pbmtoxbm, pbmtox10bm, pbm
-
-2 Author
-     Copyright (C) 1988 by Jef Poskanzer.
-
-1 ybmtopbm
-     ybmtopbm - convert a Bennet Yee "face" file into a  portable
-     bitmap
-
-2 Synopsis
-     ybmtopbm [facefile]
-
-2 Description
-     Reads a file acceptable to the face and xbm programs by Ben-
-     net Yee (bsy+@cs.cmu.edu).  Writes a portable bitmap as out-
-     put.
-
-2 See_Also
-     pbmtoybm, pbm, face, face, xbm
-
-2 Author
-     Copyright (C) 1991 by Jamie Zawinski and Jef Poskanzer.
-
-1 pbmtopk
-     pbmtopk - convert a portable bitmap into a packed (PK)  for-
-     mat font
-
-2 Synopsis
-     pbmtopk pkfile[.pk] tfmfile[.tfm] resolution [-s designsize]
-     [-p num param...] [-C codingscheme] [-F family] [-f optfile]
-     [-c num] [-W width] [-H height] [-D  depth]  [-I  ital]  [-h
-     horiz] [-v vert] [-x xoff] [-y yoff] [pbmfile]...
-
-2 Description
-     Reads portable bitmaps as input, and produces a packed  (PK)
-     font  file  and  a TFM (TeX font metric) file as output. The
-     resolution parameter indicates the resolution of  the  font,
-     in dots per inch. If the filename "-" is used for any of the
-     filenames, the standard input  stream  (or  standard  output
-     where appropriate) will be used.
-
-2 Options
-     -s designsize
-          Sets the design size  of  the  font,  in  TeX's  points
-          (72.27pt  to  the  inch). The default design size is 1.
-          The TFM parameters are given as multiples of the design
-          size.
-
-     -p num param...
-          Sets the first num font parameters for  the  font.  The
-          first  seven  parameters are the slant, interword spac-
-          ing, interword space  stretchability,  interword  space
-          shrinkability,  x-height, quad width, and post-sentence
-          extra space of the font. Math and symbol fonts may have
-          more  parameters;  see The TeXbook for a list of these.
-          Reasonable default values  are  chosen  for  parameters
-          which are not specified.
-
-     -C codingscheme
-          Sets the coding scheme comment in the TFM file.
-
-     -F family
-          Sets the font family comment in the TFM file.
-
-     -f optfile
-          Reads the file optfile, which should contain a lines of
-          the form:
-
-             filename xoff yoff horiz vert width height depth ital
-
-          The pbm files specified by the filename parameters  are
-          inserted  consecutively  in the font with the specified
-          attributes. If any of the attributes  are  omitted,  or
-          replaced  with  "*", a default value will be calculated
-          from the size of the bitmap. The settings  of  the  -W,
-          -H,  -D, -I, -h, -v, -x, and -y options do not affected
-          characters created in this way.  The  character  number
-          can  be  changed by including a line starting with "=",
-          followed by the new number.  Lines beginning  with  "%"
-          or "#" are ignored.
-
-     -c num
-          Sets the character number of the  next  bitmap  encoun-
-          tered to num.
-
-     -W width
-          Sets the TFM width of the next character to  width  (in
-          design size multiples).
-
-     -H height
-          Sets the TFM height of the next character to height (in
-          design size multiples).
-
-     -D depth
-          Sets the TFM depth of the next character to  depth  (in
-          design size multiples).
-
-     -I ital
-          Sets the italic correction of  the  next  character  to
-          ital (in design size multiples).
-
-     -h horiz
-          Sets the horizontal escapement of the next character to
-          horiz (in pixels).
-
-     -v vert
-          Sets the vertical escapement of the next  character  to
-          vert (in pixels).
-
-     -x xoff
-          Sets the horizontal offset of  the  next  character  to
-          xoff (in pixels).
-
-     -y yoff
-          Sets the vertical offset of the next character to  yoff
-          (in pixels, from the top row).
-
-2 See_Also
-     pktopbm, pbm
-
-2 Author
-     Adapted  from  Tom  Rokicki's   pxtopk   by   Angus   Duggan
-     <ajcd@uk.ac.ed.lfcs>.
-
-1 libpbm              C LIBRARY FUNCTIONS              libpbm
-     libpbm - functions to support portable bitmap programs
-
-2 Synopsis
-     #include <pbm.h>
-     cc ... libpbm.a
-
-
-2 Description - PACKAGE-WIDE ROUTINES
-  KEYWORD MATCHING
-     int pm_keymatch( char* str, char* keyword, int minchars )
-
-     Does a case-insensitive match of str against  keyword.   str
-     can be a leading sunstring of keyword, but at least minchars
-     must be present.
-
-  LOG BASE TWO
-     int pm_maxvaltobits( int maxval )
-     int pm_bitstomaxval( int bits )
-
-     Convert between a maxval and  the  minimum  number  of  bits
-     required to hold it.
-
-  MESSAGES AND ERRORS
-     void pm_message( char* fmt, ... )
-
-     printf() style routine to write an informational message.
-
-     void pm_error( char* fmt, ... )
-
-     printf() style routine to write an error message and abort.
-
-     void pm_usage( char* usage )
-
-     Write a usage message.   The  string  should  indicate  what
-     arguments are to be provided to the program.
-
-  GENERIC FILE MANAGEMENT
-     FILE* pm_openr( char* name )
-
-     Open the given file  for  reading,  with  appropriate  error
-     checking.   A  filename  of  "-"  is  taken as equivalent to
-     stdin.
-
-     FILE* pm_openw( char* name )
-
-     Open the given file  for  writing,  with  appropriate  error
-     checking.
-
-     void pm_close( FILE* fp )
-
-     Close the file descriptor, with appropriate error checking.
-
-  ENDIAN I/O
-     int pm_readbigshort( FILE* in, short* sP )
-     int pm_writebigshort( FILE* out, short s )
-     int pm_readbiglong( FILE* in, long* lP )
-     int pm_writebiglong( FILE* out, long l )
-     int pm_readlittleshort( FILE* in, short* sP )
-     int pm_writelittleshort( FILE* out, short s )
-     int pm_readlittlelong( FILE* in, long* lP )
-     int pm_writelittlelong( FILE* out, long l )
-
-     Routines to read and write short and  long  ints  in  either
-     big- or little-endian byte order.
-
-2 Description - PBM-SPECIFIC ROUTINES
-  TYPES AND CONSTANTS
-     typedef ... bit;
-     #define PBM_WHITE ...
-     #define PBM_BLACK ...
-
-     each bit should contain only  the  values  of  PBM_WHITE  or
-     PBM_BLACK.
-
-     #define PBM_FORMAT ...
-     #define RPBM_FORMAT ...
-     #define PBM_TYPE PBM_FORMAT
-     #define PBM_FORMAT_TYPE(f) ...
-
-     For distinguishing different file formats and types.
-
-  INITIALIZATION
-     void pbm_init( int* argcP, char* argv[] )
-
-     All PBM programs must call this routine.
-
-  MEMORY MANAGEMENT
-     bit** pbm_allocarray( int cols, int rows )
-
-     Allocate an array of bits.
-
-     bit* pbm_allocrow( int cols )
-
-     Allocate a row of the given number of bits.
-
-     void pbm_freearray( bit** bits, int rows )
-
-     Free the array allocated  with  pbm_allocarray()  containing
-     the given number of rows.
-
-     void pbm_freerow( bit* bitrow )
-
-     Free a row of bits.
-
-  READING FILES
-     void pbm_readpbminit( FILE* fp, int* colsP, int* rowsP, int* formatP )
-
-     Read the header from a PBM file, filling in the  rows,  cols
-     and format variables.
-
-     void pbm_readpbmrow( FILE* fp, bit* bitrow, int cols, int format )
-
-     Read a row of bits into the bitrow array.  Format  and  cols
-     were filled in by pbm_readpbminit().
-
-     bit** pbm_readpbm( FILE* fp, int* colsP, int* rowsP )
-
-     Read an entire bitmap file into memory, returning the  allo-
-     cated  array  and  filling  in  the rows and cols variables.
-     This function combines  pbm_readpbminit(),  pbm_allocarray()
-     and pbm_readpbmrow().
-
-     char* pm_read_unknown_size( FILE* fp, long* nread )
-
-     Read an entire file or input stream of  unknown  size  to  a
-     buffer.   Allocate memory more memory as needed. The calling
-     routine has  to  free  the  allocated  buffer  with  free().
-     pm_read_unknown_size()  returns  a  pointer to the allocated
-     buffer. The nread argument returns the number of bytes read.
-
-  WRITING FILES
-     void pbm_writepbminit( FILE* fp, int cols, int rows, int forceplain )
-
-     Write the header for a portable bitmap file.  The forceplain
-     flag forces a plain-format file to be written, as opposed to
-     a raw-format one.
-
-     void pbm_writepbmrow( FILE* fp, bit* bitrow, int cols, int forceplain )
-
-     Write a row from a portable bitmap.
-
-     void pbm_writepbm( FILE* fp, bit** bits, int cols, int rows, int forceplain )
-
-     Write the header and all data for a portable  bitmap.   This
-     function combines pbm_writepbminit() and pbm_writepbmrow().
-
-2 See_Also
-     libpgm, libppm, libpnm
-
-2 Author
-     Copyright (C) 1989, 1991 by Tony Hansen and Jef Poskanzer.
-
-1 pbm
-     pbm - portable bitmap file format
-
-2 Description
-     The portable bitmap format is a  lowest  common  denominator
-     monochrome  file format.  It was originally designed to make
-     it reasonable to mail bitmaps  between  different  types  of
-     machines  using  the  typical stupid network mailers we have
-     today.  Now it serves as the common language of a large fam-
-     ily of bitmap conversion filters.  The definition is as fol-
-     lows:
-
-     - A "magic number" for identifying the  file  type.   A  pbm
-       file's magic number is the two characters "P1".
-
-     - Whitespace (blanks, TABs, CRs, LFs).
-
-     - A width, formatted as ASCII characters in decimal.
-
-     - Whitespace.
-
-     - A height, again in ASCII decimal.
-
-     - Whitespace.
-
-     - Width * height bits, each either '1' or '0',  starting  at
-       the  top-left  corner  of the bitmap, proceeding in normal
-       English reading order.
-
-     - The character '1' means black, '0' means white.
-
-     - Whitespace in the bits section is ignored.
-
-     - Characters from a "#" to the next end-of-line are  ignored
-       (comments).
-
-     - No line should be longer than 70 characters.
-
-     Here is an example of a small bitmap in this format:
-     P1
-     # feep.pbm
-     24 7
-     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 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 1 1 1 0
-     0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0
-     0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 1 0
-     0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0
-     0 1 0 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 0 1 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
-
-     Programs that read this format should be as lenient as  pos-
-     sible, accepting anything that looks remotely like a bitmap.
-     There is also a variant on the format, available by  setting
-     the  RAWBITS  option  at compile time.  This variant is dif-
-     ferent in the following ways:
-
-     - The "magic number" is "P4" instead of "P1".
-
-     - The bits are stored eight per byte, high bit first low bit
-       last.
-
-     - No whitespace is allowed in the bits section, and  only  a
-       single  character  of  whitespace (typically a newline) is
-       allowed after the height.
-
-     - The files are eight times smaller and many times faster to
-       read and write.
-
-2 See_Also
-    atktopbm, brushtopbm,  cmuwmtopbm,  g3topbm,  gemtopbm, icontopbm,
-    macptopbm,  mgrtopbm,  pi3topbm,   xbmtopbm,  ybmtopbm,  pbmto10x,
-    pnmtoascii, pbmtoatk, pbmtobbnbg, pbmtocmuwm, pbmtoepson, pbmtog3,
-    pbmtogem,  pbmtogo,  pbmtoicon,  pbmtolj,  pbmtomacp,    pbmtomgr,
-    pbmtopi3,  pbmtoplot,  pbmtoptx,  pbmtox10bm,  pbmtoxbm, pbmtoybm,
-    pbmtozinc,  pbmlife, pbmmake, pbmmask, pbmreduce, pbmtext, pbmupc,
-    pnm, pgm, ppm
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 bioradtopgm
-     bioradtopgm - convert a Biorad confocal file into a portable
-     graymap
-
-2 Synopsis
-     bioradtopgm [-image#] [imagedata]
-
-2 Description
-     Reads a Biorad confocal file as input.  Produces a  portable
-     graymap  as  output.  If the resulting image is upside down,
-     run it through pnmflip -tb .
-
-2 Options
-     -image#
-          A Biorad image file may contain more  than  one  image.
-          With  this flag, you can specify which image to extract
-          (only one at a time). The first image in the  file  has
-          number  zero.  If  no  image  number  is supplied, only
-          information about the image  size  and  the  number  of
-          images  in  the input is printed out. No output is pro-
-          duced.
-
-2 Bugs
-     A Biorad image may be in word format. If PbmPlus is not com-
-     piled  with  the "BIGGRAYS" flag, word files can not be con-
-     verted. See the Makefile.
-
-2 See_Also
-     pgm, pnmflip
-
-2 Authors
-     Copyright (C) 1993 by Oliver Trepte
-
-1 fitstopgm
-     fitstopgm - convert a FITS file into a portable graymap
-
-2 Synopsis
-     fitstopgm [-image N] [FITSfile]
-
-2 Description
-     Reads a FITS file as input.  Produces a portable graymap  as
-     output.   The results may need to be flipped top for bottom;
-     if so, just pipe the output through pnmflip -tb.
-
-2 Options
-     The -image option is for FITS files with  three  axes.   The
-     assumption  is  that  the third axis is for multiple images,
-     and this option lets you select which one you want.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 References
-     FITS stands for Flexible Image  Transport  System.   A  full
-     description can be found in Astronomy & Astrophysics Supple-
-     ment Series 44 (1981), page 363.
-
-2 See_Also
-     pgmtofits, pgm, pnmflip
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 fstopgm
-     fstopgm - convert a Usenix FaceSaver(tm) file into  a  port-
-     able graymap
-
-2 Synopsis
-     fstopgm [fsfile]
-
-2 Description
-     Reads a Usenix FaceSaver(tm)  file  as  input.   Produces  a
-     portable graymap as output.
-
-     FaceSaver(tm)  files  sometimes  have  rectangular   pixels.
-     While  fstopgm  won't  re-scale  them into square pixels for
-     you, it will give you the precise pnmscale command that will
-     do  the job.  Because of this, reading a FaceSaver(tm) image
-     is a two-step process.  First you do:
-       fstopgm > /dev/null
-     This will tell you whether you need to  use  pnmscale.  Then
-     use one of the following pipelines:
-       fstopgm | pgmnorm
-       fstopgm | pnmscale -whatever | pgmnorm
-     To go to PBM, you want something more like one of these:
-       fstopgm | pnmenlarge 3 | pgmnorm | pgmtopbm
-       fstopgm | pnmenlarge 3 | pnmscale <whatever> | pgmnorm | pgmtopbm
-     You want to enlarge when going to a bitmap because otherwise
-     you  lose information; but enlarging by more than 3 does not
-     look good.
-
-     FaceSaver is a registered trademark of  Metron  Computerware
-     Ltd. of Oakland, CA.
-
-2 See_Also
-     pgmtofs, pgm, pgmnorm, pnmenlarge,  pnmscale,
-     pgmtopbm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 hipstopgm
-     hipstopgm - convert a HIPS file into a portable graymap
-
-2 Synopsis
-     hipstopgm [hipsfile]
-
-2 Description
-     Reads a HIPS file as input.  Produces a portable graymap  as
-     output.
-
-     If the HIPS file contains more than one frame  in  sequence,
-     hipstopgm will concatenate all the frames vertically.
-
-     HIPS is a format developed at the Human Information Process-
-     ing Laboratory, NYU.
-
-2 See_Also
-     pgm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 lispmtopgm
-     lispmtopgm - convert a Lisp Machine  bitmap  file  into  pgm
-     format
-
-2 Synopsis
-     lispmtopgm [lispmfile]
-
-2 Description
-     Reads a Lisp Machine bitmap as input.  Produces  a  portable
-     graymap as output.
-
-     This is the file format written by  the  tv:write-bit-array-
-     file function on TI Explorer and Symbolics lisp machines.
-
-     Multi-plane bitmaps on lisp  machines  are  color;  but  the
-     lispm  image file format does not include a color map, so we
-     must treat it as a graymap instead.  This is unfortunate.
-
-2 See_Also
-     pgmtolispm, pgm
-
-2 Bugs
-     The Lispm bitmap file format is a bit quirky;   Usually  the
-     image  in  the  file  has  its  width rounded up to the next
-     higher multiple of 32, but not always.  If the width is  not
-     a  multiple  of  32,  we  don't  deal  with it properly, but
-     because of the Lispm microcode, such arrays are probably not
-     image data anyway.
-
-     Also, the lispm code for saving bitmaps has a bug,  in  that
-     if  you  are writing a bitmap which is not mod32 across, the
-     file may be up to 7 bits too short!  They round down instead
-     of up, and we don't handle this bug gracefully.
-
-     No color.
-
-2 Author
-     Copyright (C) 1991 by Jamie Zawinski and Jef Poskanzer.
-
-1 pbmtopgm
-     pbmtopgm - convert portable bitmap to  portable  graymap  by
-     averaging areas
-
-2 Synopsis
-     pbmtopgm <width> <height> [pbmfile]
-
-2 Description
-     Reads a portable bitmap as input. Outputs a portable graymap
-     created  by  averaging  the number of pixels within a sample
-     area of width by height around each point. Pbmtopgm is simi-
-     lar  to a special case of ppmconvol. A ppmsmooth step may be
-     needed after pbmtopgm.
-
-     Pbmtopgm has the effect of anti-aliasing bitmaps which  con-
-     tain distinct line features.
-
-2 See_Also
-     pbm
-
-2 Author
-     Copyright (C) 1990 by Angus Duggan 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 docu-
-     mentation.   This  software  is  provided  "as  is"  without
-     express or implied warranty.
-
-2 Notes
-     Pbmtopgm works best with odd sample width and heights.
-
-1 pgmbentley
-     pgmbentley - Bentleyize a portable graymap
-
-2 Synopsis
-     pgmbentley [pgmfile]
-
-2 Description
-     Reads a portable graymap as  input.   Performs  The  Bentley
-     Effect, and writes a portable graymap as output.
-
-     The Bentley Effect is described in "Beyond  Photography"  by
-     Holzmann,  chapter  4,  photo  4.   It's a vertical smearing
-     based on brightness.
-
-2 See_Also
-     pgmoil, ppmrelief, pgm
-
-2 Author
-     Copyright (C) 1990 by Wilson Bent (whb@hoh-2.att.com)
-
-1 pgmenhance
-     pgmenhance - edge-enhance a portable graymap
-
-2 Synopsis
-     pgmenhance [-N] [pgmfile]
-
-2 Description
-     Reads a portable graymap as input.  Enhances the edges,  and
-     writes a portable graymap as output.
-
-     The  edge  enhancing  technique  is  taken  from  Philip  R.
-     Thompson's "xim" program, which in turn took it from section
-     6 of "Digital Halftones by Dot Diffusion", D. E. Knuth,  ACM
-     Transaction  on  Graphics Vol. 6, No. 4, October 1987, which
-     in turn got it from two 1976 papers by J. F. Jarvis et. al.
-
-2 Options
-     The optional -N flag should be a digit from 1 to  9.   1  is
-     the  lowest  level  of  enhancement,  9  is the highest, The
-     default is 9.
-
-2 See_Also
-     pgmedge, pgm, pbm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pgmhist
-     pgmhist - print a histogram of  the  values  in  a  portable
-     graymap
-
-2 Synopsis
-     pgmhist [pgmfile]
-
-2 Description
-     Reads a portable graymap as input.  Prints  a  histogram  of
-     the gray values.
-
-2 See_Also
-     pgmnorm, pgm, ppmhist
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pgmnoise
-     pgmnoise - create a graymap made up of white noise
-
-2 Synopsis
-     pgmnoise width height
-
-2 Description
-     Creates a portable graymap that is made up of random  pixels
-     with gray values in the range of 0 to PGM_MAXMAXVAL (depends
-     on the compilation, either 255 or 65535). The graymap has  a
-     size of width * height pixels.
-
-2 See_Also
-     pgm(5)
-
-2 Author
-     Copyright (C) 1993 by Frank Neumann
-
-1 pgmnorm
-     pgmnorm - normalize the contrast in a portable graymap
-
-2 Synopsis
-     pgmnorm [-bpercent N | -bvalue N] [-wpercent N | -wvalue  N]
-     [pgmfile]
-
-2 Description
-     Reads a portable graymap as input.  Normalizes the  contrast
-     by  forcing the lightest pixels to white, the darkest pixels
-     to black, and linearly rescaling the ones  in  between;  and
-     produces a portable graymap as output.
-
-2 Options
-     By default, the darkest 2 percent of all pixels  are  mapped
-     to  black,  and  the lightest 1 percent are mapped to white.
-     You can override these percentages by  using  the  -bpercent
-     and  -wpercent  flags,  or  you  can specify the exact pixel
-     values to be mapped by using the -bvalue and -wvalue  flags.
-     Appropriate  numbers  for  the  flags can be gotten from the
-     pgmhist tool.  If you just want  to  enhance  the  contrast,
-     then choose values at elbows in the histogram; e.g. if value
-     29 represents 3% of the image but value 30  represents  20%,
-     choose  30  for  bvalue.   If you want to lighten the image,
-     then set bvalue to 0 and just fiddle with wvalue; similarly,
-     to  darken  the  image,  set  wvalue to maxval and play with
-     bvalue.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     pgmhist, pgm
-
-2 Author
-     Partially based on the fbnorm filter  in  Michael  Mauldin's
-     "Fuzzy Pixmap" package.
-
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pgmoil
-     pgmoil - turn a portable graymap into an oil painting
-
-2 Synopsis
-     pgmoil [-n N] [pgmfile]
-
-2 Description
-     Reads a portable graymap as input.  Does an "oil  transfer",
-     and writes a portable graymap as output.
-
-     The oil transfer is described  in  "Beyond  Photography"  by
-     Holzmann,  chapter  4,  photo  7.   It's a sort of localized
-     smearing.
-
-2 Options
-     The optional -n flag controls the size of the area  smeared.
-     The default value is 3.
-
-2 Bugs
-     Takes a long time to run.
-
-2 See_Also
-     pgmbentley, ppmrelief, pgm
-
-2 Author
-     Copyright (C) 1990 by Wilson Bent (whb@hoh-2.att.com)
-
-1 pgmramp
-     pgmramp - generate a grayscale ramp
-
-2 Synopsis
-     pgmramp -lr|-tb | -rectangle|-ellipse width height
-
-2 Description
-     Generates a graymap  of  the  specified  size  containing  a
-     black-to-white ramp.  These ramps are useful for multiplying
-     with other images, using the pnmarith tool.
-
-2 Options
-     -lr  A left to right ramp.
-
-     -tb  A top to bottom ramp.
-
-     -rectangle
-          A rectangular ramp.
-
-     -ellipse
-          An elliptical ramp.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     pnmarith, pgm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pgmtofits
-     pgmtofits - convert a portable graymap into FITS format
-
-2 Synopsis
-     pgmtofits [pgmfile]
-
-2 Description
-     Reads a portable graymap as input.  Produces a FITS file  as
-     output.
-
-     FITS stands for Flexible Image  Transport  System.   A  full
-     description can be found in Astronomy & Astrophysics Supple-
-     ment Series 44 (1981), page 363.
-
-2 See_Also
-     fitstopgm, pgm
-
-2 Author
-     Copyright (C) 1989 by Wilson H. Bent (whb@hoh-2.att.com).
-
-1 pgmtofs
-     pgmtofs - convert portable graymap to  Usenix  FaceSaver(tm)
-     format
-
-2 Synopsis
-     pgmtofs [pgmfile]
-
-2 Description
-     Reads  a  portable  graymap  as  input.    Produces   Usenix
-     FaceSaver(tm) format as output.
-
-     FaceSaver is a registered trademark of  Metron  Computerware
-     Ltd. of Oakland, CA.
-
-2 See_Also
-     fstopgm, pgm
-
-2 Author
-     Copyright (C) 1991 by Jef Poskanzer.
-
-1 pgmtolispm
-     pgmtolispm - convert a portable graymap  into  Lisp  Machine
-     format
-
-2 Synopsis
-     pgmtolispm [pgmfile]
-
-2 Description
-     Reads a portable graymap as input.  Produces a Lisp  Machine
-     bitmap as output.
-
-     This is the file format read by  the  tv:read-bit-array-file
-     function on TI Explorer and Symbolics lisp machines.
-
-     Given a pgm (instead of a pbm) a multi-plane image  will  be
-     output.  This is probably not useful unless you have a color
-     lisp machine.
-
-     Multi-plane bitmaps on lisp  machines  are  color;  but  the
-     lispm  image file format does not include a color map, so we
-     must treat it as a graymap instead.  This is unfortunate.
-
-2 See_Also
-     lispmtopgm, pgm
-
-2 Bugs
-     Output width is always rounded up to the nearest multiple of
-     32;  this might not always be what you want, but it probably
-     is (arrays which are not modulo 32 cannot be passed  to  the
-     Lispm  BITBLT  function, and thus cannot easily be displayed
-     on the screen).
-
-     No color.
-
-2 Author
-     Copyright (C) 1991 by Jamie Zawinski and Jef Poskanzer.
-
-1 pgmtopbm
-     pgmtopbm - convert a portable graymap into a portable bitmap
-
-2 Synopsis
-     pgmtopbm [-floyd|-fs|-threshold  |-dither8|-d8|-cluster3  |-
-     c3|-cluster4|-c4 |-cluster8|-c8] [-value val] [pgmfile]
-
-2 Description
-     Reads a portable graymap as input.  Produces a portable bit-
-     map as output.
-
-     Note that there is no pbmtopgm converter,  because  any  pgm
-     program can read pbm files automagically.
-
-2 Options
-     The default quantization method  is  boustrophedonic  Floyd-
-     Steinberg  error  diffusion (-floyd or -fs).  Also available
-     are simple thresholding (-threshold); Bayer's ordered dither
-     (-dither8) with a 16x16 matrix; and three different sizes of
-     45-degree  clustered-dot  dither  (-cluster3,  -cluster4,  -
-     cluster8).
-
-     Floyd-Steinberg will almost always  give  the  best  looking
-     results;  however, looking good is not always what you want.
-     For instance, thresholding can be used in  a  pipeline  with
-     the  pnmconvol tool, for tasks like edge and peak detection.
-     And clustered-dot dithering gives a  newspaper-ish  look,  a
-     useful special effect.
-
-     The -value flag alters the  thresholding  value  for  Floyd-
-     Steinberg  and  simple  thresholding.   It  should be a real
-     number between 0 and 1.   Above  0.5  means  darker  images;
-     below 0.5 means lighter.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 References
-     The only reference you need for this stuff is "Digital Half-
-     toning" by Robert Ulichney, MIT Press, ISBN 0-262-21009-6.
-
-2 See_Also
-     pbmreduce, pgm, pbm, pnmconvol
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 psidtopgm
-     psidtopgm - convert PostScript "image" data into a  portable
-     graymap
-
-2 Synopsis
-     psidtopgm width height bits/sample [imagedata]
-
-2 Description
-     Reads the "image" data from  a  PostScript  file  as  input.
-     Produces a portable graymap as output.
-
-     This is a very simple and limited program, and is here  only
-     because  so  many  people  have asked for it.  To use it you
-     have to manually extract the readhexstring data portion from
-     your  PostScript  file, and then give the width, height, and
-     bits/sample on the command line.  Before you  attempt  this,
-     you  should  at  least  read  the description of the "image"
-     operator in the PostScript Language Reference Manual.
-
-     It would probably not be too hard to  write  a  script  that
-     uses  this  filter  to read a specific variety of PostScript
-     image, but the variation is too great  to  make  a  general-
-     purpose  reader.   Unless,  of  course,  you want to write a
-     full-fledged PostScript interpreter...
-
-2 See_Also
-     pnmtops, pgm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 spottopgm
-     spottopgm - convert SPOT satellite images to Portable  Grey-
-     map format
-
-2 Synopis
-     spottopgm [-1|2|3]  [Firstcol  Firstline  Lastcol  Lastline]
-     inputfile
-
-2 Options
-     -1|2|3  Extract the given colour from the  SPOT  image.  The
-             colours  are  infra-red,  visible  light  and ultra-
-             violet, although I don't know which  corresponds  to
-             which  number.  If the image is in colour, this will
-             be announced on standard error. The  default  colour
-             is 1.
-
-     Firstcol Firstline Lastcol Lastline
-             Extract the specified rectangle from the SPOT image.
-             Most  SPOT  images  are  3000 lines long and 3000 or
-             more columns wide.  Unfortunately  the  SPOT  format
-             only  gives  the width and not the length. The width
-             is printed on standard error. The default  rectangle
-             is the width of the input image by 3000 lines.
-
-2 Description
-     Spottopgm converts the named inputfile into Portable Greymap
-     format,  defaulting  to  the  first color and the whole SPOT
-     image unless specified by the options.
-
-2 INSTALLATION
-     You  must  edit  the  source  program  and   either   define
-     BIGNDIAN  or  LITTLENDIAN,  and  fix  the  typedefs  for
-     uint32, uint16 and uint8 appropriately.
-
-2 Bugs
-     Currently spottopgm doesn't  determine  the  length  of  the
-     input  file;  this  would  involve two passes over the input
-     file. It defaults to 3000 lines instead.
-
-     Spottopgm could extract a three-color  image  (ppm),  but  I
-     didn't feel like making the program more complicated than it
-     is now.  Besides,  there  is  no  one-to-one  correspondence
-     between  red,  green, blue and infra-red, visible and ultra-
-     violet.
-
-     I've only had a limited number of SPOT images to play  with,
-     and  therefore wouldn't guarantee that this will work on any
-     other images.
-
-2 Author
-     Warren Toomey  wkt@csadfa.cs.adfa.oz.au
-
-1 pgmcrater
-     pgmcrater - create cratered terrain by fractal forgery
-
-2 Synopsis
-     pgmcrater [-number n] [-height|-ysize s] [-width|-xsize s]
-               [-gamma g]
-
-2 Description
-     pgmcrater creates a portable graymap which  mimics  cratered
-     terrain.  The graymap is created by simulating the impact of
-     a given number of craters with  random  position  and  size,
-     then  rendering  the resulting terrain elevations based on a
-     light source shining from one side of the screen.  The  size
-     distribution  of  the  craters is based on a power law which
-     results in many more small craters  than  large  ones.   The
-     number  of  craters of a given size varies as the reciprocal
-     of the area as described on pages 31 and 32 of  Peitgen  and
-     Saupe[1];  cratered  bodies in the Solar System are observed
-     to obey this  relationship.   The  formula  used  to  obtain
-     crater  radii  governed by this law from a uniformly distri-
-     buted pseudorandom sequence was developed by Rudy Rucker.
-
-     High resolution images with large numbers of  craters  often
-     benefit  from  being piped through pnmsmooth.  The averaging
-     performed by this process eliminates some of the jagged pix-
-     els  and  lends  a  mellow  ``telescopic image'' feel to the
-     overall picture.
-
-2 Options
-     -number n Causes n craters to be generated.  If  no  -number
-               specification is given, 50000 craters will be gen-
-               erated.  Don't expect to see them all!  For  every
-               large  crater  there are many, many more tiny ones
-               which tend simply to erode the landscape.  In gen-
-               eral,  the more craters you specify the more real-
-               istic the result; ideally you want the entire ter-
-               rain  to  have  been extensively turned over again
-               and again by cratering.   High  resolution  images
-               containing  five  to ten million craters are stun-
-               ning but take quite a while to create.
-
-     -height height
-               Sets the height of the generated image  to  height
-               pixels.  The default height is 256 pixels.
-
-     -width width
-               Sets the width of the  generated  image  to  width
-               pixels.  The default width is 256 pixels.
-
-     -xsize width
-               Sets the width of the  generated  image  to  width
-               pixels.  The default width is 256 pixels.
-
-
-     -ysize height
-               Sets the height of the generated image  to  height
-               pixels.  The default height is 256 pixels.
-
-     -gamma factor
-               The specified factor is used to gamma correct  the
-               graymap   in  the  same  manner  as  performed  by
-               pnmgamma.  The default value is 1.0, which results
-               in  a medium contrast image.  Values larger than 1
-               lighten  the  image  and  reduce  contrast,  while
-               values  less  than  1 darken the image, increasing
-               contrast.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 Bugs
-     The -gamma option  isn't  really  necessary  since  you  can
-     achieve  the same effect by piping the output from pgmcrater
-     through pnmgamma.  However, pgmcrater performs  an  internal
-     gamma  map  anyway in the process of rendering the elevation
-     array into a graymap, so there's no additional  overhead  in
-     allowing a user-specified gamma.
-
-     Real craters  have  two  distinct  morphologies.   pgmcrater
-     simulates  only  small  craters,  which are hemispherical in
-     shape (regardless of the incidence angle  of  the  impacting
-     body,  as long as the velocity is sufficiently high).  Large
-     craters, such as Copernicus and Tycho on the  Moon,  have  a
-     ``walled plain'' shape with a cross-section more like:
-                /\                            /\
-          _____/  \____________/\____________/  \_____
-     Larger craters should really use this profile, including the
-     central  peak,  and totally obliterate the pre-existing ter-
-     rain.
-
-2 See_Also
-     pgm, pnmgamma, pnmsmooth
-
-     [1]  Peitgen, H.-O., and Saupe,  D.  eds.,  The  Science  Of
-          Fractal Images, New York: Springer Verlag, 1988.
-
-2 Author
-          John Walker
-          Autodesk SA
-          Avenue des Champs-Montants 14b
-          CH-2074 MARIN
-          Suisse/Schweiz/Svizzera/Svizra/Switzerland
-          Usenet:  kelvin@Autodesk.com
-          Fax:     038/33 88 15
-          Voice:   038/33 76 33
-
-     software  and  its documentation for any purpose and without
-     fee is hereby granted, without any  conditions  or  restric-
-     tions.   This software is provided ``as is'' without express
-     or implied warranty.
-
-     PLUGWARE! If you like this kind of stuff, you may also enjoy
-     ``James Gleick's Chaos--The Software'' for MS-DOS, available
-     for $59.95 from your local software store or  directly  from
-     Autodesk,  Inc.,  Attn:  Science Series, 2320 Marinship Way,
-     Sausalito, CA 94965, USA.  Telephone: (800)  688-2344  toll-
-     free  or,  outside  the  U.S. (415) 332-2344 Ext 4886.  Fax:
-     (415) 289-4718.  ``Chaos--The  Software''  includes  a  more
-     comprehensive   fractal   forgery  generator  which  creates
-     three-dimensional landscapes as well as clouds and  planets,
-     plus five more modules which explore other aspects of Chaos.
-     The user guide of more than 200 pages includes an  introduc-
-     tion  by  James  Gleick  and  detailed  explanations by Rudy
-     Rucker of the mathematics and algorithms used by  each  pro-
-     gram.
-
-
-1 pgmedge
-     pgmedge - edge-detect a portable graymap
-
-2 Synopsis
-     pgmedge [pgmfile]
-
-2 Description
-     Reads a portable graymap as input.  Outlines the edges,  and
-     writes  a  portable  graymap  as  output.  Piping the result
-     through pgmtopbm -threshold and playing with  the  threshold
-     value will give a bitmap of the edges.
-
-     The edge detection technique used is to take the Pythagorean
-     sum  of  two  Sobel gradient operators at 90 degrees to each
-     other.  For more details see "Digital Image  Processing"  by
-     Gonzalez and Wintz, chapter 7.
-
-2 See_Also
-     pgmenhance, pgmtopbm, pgm, pbm
-
-2 Author
-     Copyright (C) 1991 by Jef Poskanzer.
-
-1 pgmtexture
-     pgmtexture - calculate textural features on a portable gray-
-     map
-
-2 Synopsis
-     pgmtexture [-d d] [pgmfile]
-
-2 Description
-     Reads a portable  graymap  as  input.   Calculates  textural
-     features  based on spatial dependence matrices at 0, 45, 90,
-     and 135 degrees for a distance d (default  =  1).   Textural
-     features include:
-
-          (1) Angular Second Moment,
-          (2) Contrast,
-          (3) Correlation,
-          (4) Variance,
-          (5) Inverse Difference Moment,
-          (6) Sum Average,
-          (7) Sum Variance,
-          (8) Sum Entropy,
-          (9) Entropy,
-          (10) Difference Variance,
-          (11) Difference Entropy,
-          (12, 13) Information Measures of Correlation, and
-          (14) Maximal Correlation Coefficient.
-
-     Algorithm taken from:
-     Haralick, R.M., K. Shanmugam, and I.  Dinstein.  1973.  Tex-
-     tural  features  for image classification. IEEE Transactions
-     on Systems, Man, and Cybertinetics, SMC-3(6):610-621.
-
-2 Bugs
-     The program can run incredibly slow for large images (larger
-     than  64  x  64)  and command line options are limited.  The
-     method for finding (14) the maximal correlation coefficient,
-     which  requires  finding  the second largest eigenvalue of a
-     matrix Q, does not always converge.
-
-2 References
-     IEEE Transactions on Systems, Man, and  Cybertinetics,  SMC-
-     3(6):610-621.
-
-2 See_Also
-     pgm, pnmcut
-
-2 Author
-     Copyright (C) 1991 by Texas Agricultural Experiment Station,
-     employer for hire of James Darrell McCauley.
-
-1 rawtopgm
-     rawtopgm - convert raw grayscale bytes into a portable gray-
-     map
-
-2 Synopsis
-     rawtopgm  [-headerskip  N]  [-rowskip  N]   [-tb|-topbottom]
-     [width height] [imagedata]
-
-2 Description
-     Reads raw grayscale bytes as  input.   Produces  a  portable
-     graymap  as output.  The input file is just grayscale bytes.
-     If you don't specify the width and  height  on  the  command
-     line,  the  program will check the size of the image and try
-     to make a quadratic image of it. It is an error to supply  a
-     non  quadratic  image  without  specifying width and height.
-     The maxval is assumed to be 255.
-
-2 Options
-     -headerskip
-          If the file has a header, you can use this flag to skip
-          over it.
-
-     -rowskip
-          If there is padding at the ends of the  rows,  you  can
-          skip  it  with  this  flag.  Note that rowskip can be a
-          real number.  Amazingly, I once had an image with 0.376
-          bytes of padding per row.  This turned out to be due to
-          a file-transfer problem, but I was still able  to  read
-          the image.
-
-     -tb -topbottom
-          Flips the image upside down.  The first pixel in a  pgm
-          file  is  in  the  lower left corner of the image.  For
-          conversion from images with  the  first  pixel  in  the
-          upper  left  corner  (e.g.  the  Molecular Dynamics and
-          Leica confocal formats) this  flips  the  image  right.
-          This is equivalent to rawtopgm [file] | pnmflip -tb .
-
-2 Bugs
-     If you don't specify the image width and height, the program
-     will try to read the entire image to a memory buffer. If you
-     get a message that states that you are out of memory, try to
-     specify  the width and height on the command line. Also, the
-     -tb option consumes much memory.
-
-2 See_Also
-     pgm, rawtoppm, pnmflip
-
-2 Authors
-     Copyright (C) 1989 by Jef Poskanzer.
-     Modified June 1993 by Oliver Trepte, oliver@fysik4.kth.se
-
-1 pnmarith              
-     pnmarith - perform arithmetic on two portable anymaps
-
-2 Synopsis
-     pnmarith -add|-subtract|-multiply| pnmfile1 pnmfile2
-
-2 Description
-     Reads two portable anymaps as input.  Performs the specified
-     arithmetic operation, and produces a portable anymap as out-
-     put.  The two input anymaps  must  be  the  same  width  and
-     height.
-
-     The arithmetic is performed between corresponding pixels  in
-     the  two anymaps, as if maxval was 1.0, black was 0.0, and a
-     linear scale in  between.   Results  that  fall  outside  of
-     [0..1) are truncated.
-
-     The operator -difference calculates the  absolute  value  of
-     pnmarith  -subtract pnmfile1 pnmfile2, i.e. no truncation is
-     done.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     pbmmask, pnmpaste, pnminvert, pnm
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.  Lightly modified
-     by Marcel Wijkstra <wijkstra@fwi.uva.nl>
-
-1 pnmcat                
-     pnmcat - concatenate portable anymaps
-
-2 Synopsis
-     pnmcat   [-white|-black]   -leftright|-lr   [-jtop|-jbottom]
-     pnmfile pnmfile ...
-     pnmcat   [-white|-black]   -topbottom|-tb   [-jleft|-jright]
-     pnmfile pnmfile ...
-
-2 Description
-     Reads portable anymaps as input.  Concatenates  them  either
-     left  to  right  or  top  to bottom, and produces a portable
-     anymap as output.
-
-2 Options
-     If the anymaps are not all the same height  (left-right)  or
-     width  (top-bottom),  the  smaller ones have to be justified
-     with the largest.  By default, they get  centered,  but  you
-     can specify one side or the other with one of the -j* flags.
-     So, -topbottom -jleft would stack the anymaps on top of each
-     other, flush with the left edge.
-
-     The -white and -black flags specify what  color  to  use  to
-     fill  in  the extra space when doing this justification.  If
-     neither is specified, the program makes a guess.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     pnm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pnmcomp
-     pnmcomp - composite two portable anymap files together
-
-2 Synopsis
-     pnmcomp [-invert] [-xoffN] [-yoffN] [-alphapgmfile]  overlay
-     [pnm-input] [pnm-output]
-
-2 Description
-     Reads in a portable any map image and put a overlay upon it,
-     with  optional alpha mask.  The -alpha pgmfile allows you to
-     also add an alpha mask file to the compositing process,  the
-     range  of  max  and  min can be swapped by using the -invert
-     option.  The -xoff and  -yoff  arguments  can  be  negative,
-     allowing  you to shift the overlay off the top corner of the
-     screen.
-
-2 See_Also
-     pnm
-
-2 Author
-     Copyright (C) 1992 by David Koblas (koblas@mips.com).
-
-1 pnmconvol             
-     pnmconvol - general MxN convolution on a portable anymap
-
-2 Synopsis
-     pnmconvol convolutionfile [pnmfile]
-
-2 Description
-     Reads two portable anymaps as input.  Convolves  the  second
-     using the first, and writes a portable anymap as output.
-
-     Convolution means replacing each pixel with a weighted aver-
-     age of the nearby pixels.  The weights and the area to aver-
-     age are determined by the convolution matrix.  The  unsigned
-     numbers  in  the convolution file are offset by -maxval/2 to
-     make signed numbers, and  then  normalized,  so  the  actual
-     values in the convolution file are only relative.
-
-     Here is a sample convolution file; it does a simple  average
-     of  the  nine  immediate  neighbors, resulting in a smoothed
-     image:
-         P2
-         3 3
-         18
-         10 10 10
-         10 10 10
-         10 10 10
-
-     To see how this works, do the above-mentioned offset:  10  -
-     18/2 gives 1.  The possible range of values is from 0 to 18,
-     and after the offset that's -9 to 9.  The normalization step
-     makes  the  range  -1  to  1,  and  the  values  get  scaled
-     correspondingly so they become 1/9 - exactly what you  want.
-     The equivalent matrix for 5x5 smoothing would have maxval 50
-     and be filled with 26.
-
-     The convolution file will usually be a graymap, so that  the
-     same convolution gets applied to each color component.  How-
-     ever, if you want to use a pixmap and do a different  convo-
-     lution to different colors, you can certainly do that.
-
-2 See_Also
-     pnmsmooth, pnm
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 pnmcrop               
-     pnmcrop - crop a portable anymap
-
-2 Synopsis
-     pnmcrop [-white|-black] [pnmfile]
-
-2 Description
-     Reads a portable anymap as input.  Removes  edges  that  are
-     the background color, and produces a portable anymap as out-
-     put.
-
-2 Options
-     By default, it makes a guess as to what the background color
-     is.  You can override the default with the -white and -black
-     flags.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     pnmcut, pnm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pnmcut                
-     pnmcut - cut a rectangle out of a portable anymap
-
-2 Synopsis
-     pnmcut x y width height [pnmfile]
-
-2 Description
-     Reads a portable anymap as input.   Extracts  the  specified
-     rectangle,  and produces a portable anymap as output.  The x
-     and y can be negative, in which case  they  are  interpreted
-     relative  to  the  right  and  bottom of the anymap, respec-
-     tively.
-
-2 See_Also
-     pnm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pnmdepth              
-     pnmdepth - change the maxval in a portable anymap
-
-2 Synopsis
-     pnmdepth newmaxval [pnmfile]
-
-2 Description
-     Reads a portable anymap as  input.   Scales  all  the  pixel
-     values, and writes out the image with the new maxval.  Scal-
-     ing the colors down to a smaller maxval will result in  some
-     loss of information.
-
-     Be careful of off-by-one errors when choosing the  new  max-
-     val.   For instance, if you want the color values to be five
-     bits wide, use a maxval of 31, not 32.
-
-2 See_Also
-     pnm, ppmquant, ppmdither
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 pnmenlarge            
-     pnmenlarge - read a portable anymap and enlarge it N times
-
-2 Synopsis
-     pnmenlarge N [pnmfile]
-
-2 Description
-     Reads a portable anymap as input.  Replicates its  pixels  N
-     times, and produces a portable anymap as output.
-
-     pnmenlarge can only enlarge by integer factors.  The  slower
-     but more general pnmscale can enlarge or reduce by arbitrary
-     factors, and pbmreduce can reduce by  integer  factors,  but
-     only for bitmaps.
-
-     If you enlarge by a factor of 3 or more, you should probably
-     add  a  pnmsmooth  step; otherwise, you can see the original
-     pixels in the resulting image.
-
-2 See_Also
-     pbmreduce, pnmscale, pnmsmooth, pnm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pnmfile               
-     pnmfile - describe a portable anymap
-
-2 Synopsis
-     pnmfile [pnmfile] ...
-
-2 Description
-     Reads one or more portable anymaps  as  input.   Writes  out
-     short  descriptions  of  the image type, size, etc.  This is
-     mostly for use in shell scripts, so the format is  not  par-
-     ticularly pretty.
-
-2 See_Also
-     pnm, file
-
-2 Author
-     Copyright (C) 1991 by Jef Poskanzer.
-
-1 pnmflip               
-     pnmflip - perform one or more flip operations on a  portable
-     anymap
-
-2 Synopsis
-     pnmflip [-leftright|-lr]  [-topbottom|-tb]  [-transpose|-xy]
-     [-rotate90|-r90|-ccw    ]    [-rotate270|-r270|-cw    ]   [-
-     rotate180|-r180] [pnmfile]
-
-2 Description
-     Reads a portable anymap as input.  Performs one or more flip
-     operations,  in  the order specified, and writes out a port-
-     able anymap.
-
-2 Options
-     The  flip  operations  available  are:  left  for  right  (-
-     leftright  or  -lr); top for bottom (-topbottom or -tb); and
-     transposition (-transpose or -xy).  In addition, some canned
-     concatenations   are   available:   -rotate90   or  -ccw  is
-     equivalent to -transpose -topbottom; -rotate270  or  -cw  is
-     equivalent  to  -transpose  -leftright;  and  -rotate180  is
-     equivalent to -leftright -topbottom.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     pnmrotate, pnm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pnminvert             
-     pnminvert - invert a portable anymap
-
-2 Synopsis
-     pnminvert [pnmfile]
-
-2 Description
-     Reads a portable anymap as  input.   Inverts  it  black  for
-     white and produces a portable anymap as output.
-
-2 See_Also
-     pnm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pnmnoraw              
-     pnmnoraw - force a portable anymap into plain format
-
-2 Synopsis
-     pnmnoraw [pnmfile]
-
-2 Description
-     Reads a portable anymap as input.  Writes it  out  in  plain
-     (non-raw)  format.   This  is  fairly useless if you haven't
-     defined the PBMPLUSAWBITS compile-time option.
-
-2 See_Also
-     pnm
-
-2 Author
-     Copyright (C) 1991 by Jef Poskanzer.
-
-1 pnmpad
-     pnmpad - add borders to portable anymap
-
-2 Synopsis
-     pnmpad [-white|-black] [-l#] [-r#] [-t#] [-b#] [pnmfile]
-
-2 Description
-     Reads a portable anymap as input. Outputs a portable  anymap
-     with extra borders of the sizes specified. The colour of the
-     borders can be set to black or white (default black).
-
-
-2 See_Also
-     pbmmake, pnmpaste, pbm
-
-2 Author
-     Copyright (C) 1990 by Angus Duggan 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 docu-
-     mentation.   This  software  is  provided  "as  is"  without
-     express or implied warranty.
-
-1 pnmpaste              
-     pnmpaste - paste a rectangle into a portable anymap
-
-2 Synopsis
-     pnmpaste   [-replace|-or|-and   |-xor]   frompnmfile   x   y
-     [intopnmfile]
-
-2 Description
-     Reads two portable anymaps  as  input.   Inserts  the  first
-     anymap  into  the second at the specified location, and pro-
-     duces a portable anymap the same size as the second as  out-
-     put.  If the second anymap is not specified, it is read from
-     stdin.  The x and y can be negative, in which case they  are
-     interpreted  relative to the right and bottom of the anymap,
-     respectively.
-
-     This tool is most useful in combination  with  pnmcut.   For
-     instance,  if  you  want  to edit a small segment of a large
-     image, and your image editor cannot edit  the  large  image,
-     you  can cut out the segment you are interested in, edit it,
-     and then paste it back in.
-
-     Another useful companion tool is pbmmask.
-
-     The optional flag specifies the operation to use when  doing
-     the  paste.   The  default  is -replace.  The other, logical
-     operations are only allowed if both input  images  are  bit-
-     maps.  These operations act as if white is TRUE and black is
-     FALSE.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     pnmcut, pnminvert, pnmarith, pnm, pbmmask
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 pnmscale              
-     pnmscale - scale a portable anymap
-
-2 Synopsis
-     pnmscale s [pnmfile]
-     pnmscale -xsize|-width|-ysize| -height s [pnmfile]
-     pnmscale -xscale|-yscale s [pnmfile]
-     pnmscale -xscale|-xsize|-width  s  -yscale|-ysize|-height  s
-     [pnmfile]
-     pnmscale -xysize x y [pnmfile]
-
-2 Description
-     Reads a portable anymap as input.  Scales it by  the  speci-
-     fied  factor  or  factors  and produces a portable anymap as
-     output.  If the input file is in color, the output  will  be
-     too,  otherwise  it will be grayscale.  You can both enlarge
-     (scale factor > 1) and reduce (scale factor < 1).
-
-     You can specify one dimension as a pixel size, and the other
-     dimension will be scaled correspondingly.
-
-     You can specify one dimension as  a  scale,  and  the  other
-     dimension will not be scaled.
-
-     You can specify different sizes or scales for each axis.
-
-     Or, you can use the special -xysize  flag,  which  fits  the
-     image  into  the  specified size without changing the aspect
-     ratio.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-     If you enlarge by a factor of 3 or more, you should probably
-     add  a  pnmsmooth  step; otherwise, you can see the original
-     pixels in the resulting image.
-
-2 See_Also
-     pbmreduce, pnmenlarge, pnmsmooth, pnm
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 pnmtile               
-     pnmtile - replicate a portable anymap into a specified size
-
-2 Synopsis
-     pnmtile width height [pnmfile]
-
-2 Description
-     Reads a portable anymap as input.  Replicates it until it is
-     the  specified  size, and produces a portable anymap as out-
-     put.
-
-2 See_Also
-     pnm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pnmtoddif
-     pnmtoddif - Convert a portable anymap to DDIF format
-
-2 Synopis
-     pnmtoddif pnmtoddif [-resolution x y] [pnmfile [ddiffile]]
-
-2 Description
-     pnmtoddif takes a portable anymap from  standard  input  and
-     converts it into a DDIF image file on standard output or the
-     specified DDIF file.
-
-     pbm format (bitmap) data is written as 1 bit DDIF, pgm  for-
-     mat data (greyscale) as 8 bit greyscale DDIF, and ppm format
-     data is written as 8,8,8 bit  color  DDIF.  All  DDIF  image
-     files  are written as uncompressed. The data plane organiza-
-     tion is interleaved by pixel.
-
-     In addition to the number of pixels in the width and  height
-     dimension, DDIF images also carry information about the size
-     that the image should have, that is, the physical space that
-     a  pixel occupies. PBMPLUS images do not carry this informa-
-     tion, hence it has to be externally supplied.   The  default
-     of  78  dpi  has  the  beneficial  property of not causing a
-     resize on most Digital Equipment Corporation color monitors.
-
-2 Options
-     resolution
-          The horizontal and vertical resolution  of  the  output
-          image in dots per inch. Defaults to 78 dpi.
-
-     pnmfile        The filename for the image file in  pnm  for-
-                    mat.   If  this argument is omitted, input is
-                    read from stdin.
-
-     ddiffile       The filename for the image file to be created
-                    in DDIF format.  If this argument is omitted,
-                    the ddiffile is written to  standard  output.
-                    It  can  only  specified if a pnmfile is also
-                    specified.
-
-2 Author
-     Burkhard Neidecker-Lutz
-     Digital Equipment Corporation, CEC Karlsruhe
-     neideck@nestvx.enet.dec.com
-
-1 pnmtops               
-     pnmtops - convert portable anymap to PostScript
-
-2 Synopsis
-     pnmtops [-scale s] [-turn|-noturn]  [-rle|-runlength]  [-dpi
-     n] [-width n] [-height n] [pnmfile]
-
-2 Description
-     Reads a portable anymap  as  input.   Produces  Encapsulated
-     PostScript as output.
-
-     If the input file is in color (PPM), a color PostScript file
-     gets  written.   Some  PostScript  interpreters can't handle
-     color PostScript.  If you have one of these you will need to
-     run your image through ppmtopgm first.
-
-     Note that there is no pstopnm tool - this transformation  is
-     one-way,  because  a  pstopnm  tool  would be a full-fledged
-     PostScript interpreter, which is beyond the  scope  of  this
-     package.   However,  see  the psidtopgm tool, which can read
-     grayscale non-runlength PostScript  image  data.   Also,  if
-     you're willing to install the fairly large GhostScript pack-
-     age, it comes with a pstoppm script.
-
-2 Options
-     The -scale flag controls  the  scale  of  the  result.   The
-     default  scale  is 1, which on a 300 dpi printer such as the
-     Apple LaserWriter makes the output look about the same  size
-     as  the  input would if it was displayed on a typical 72 dpi
-     screen.  To get one PNM pixel per 300 dpi printer pixel, use
-     "-scale 0.25".
-
-     The -turn and -noturn flags control whether the  image  gets
-     turned  90  degrees.  Normally, if an image is wider than it
-     is tall, it gets turned  automatically  to  better  fit  the
-     page.   If the -turn flag is specified, it will be turned no
-     matter what its shape; and if the -noturn flag is specified,
-     it will not be turned no matter what its shape.
-
-     The -rle or -runlength flag  specifies  run-length  compres-
-     sion.   This  may  save  time if the host-to-printer link is
-     slow; but normally the printer's processing time  dominates,
-     so -rle makes things slower.
-
-     The -dpi flag lets you specify the dots  per  inch  of  your
-     output   device.    The  default  is  300  dpi.   In  theory
-     PostScript is device-independent and you don't have to worry
-     about  this,  but  in practice its raster rendering can have
-     unsightly bands if the device pixels and  the  image  pixels
-     aren't in sync.
-
-     The -width and -height flags let you specify the size of the
-     page.  The default is 8.5 inches by 11 inches.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     pnm, psidtopgm
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 pnmtorast             
-     pnmtorast - convert a portable pixmap into a Sun rasterfile
-
-2 Synopsis
-     pnmtorast [-standard|-rle] [pnmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces a Sun rasterfile
-     as output.
-
-     Color values in Sun rasterfiles  are  eight  bits  wide,  so
-     pnmtorast  will  automatically scale colors to have a maxval
-     of 255.  An extra pnmdepth step is not necessary.
-
-2 Options
-     The -standard flag forces the result to  be  in  RT_STANDARD
-     form;  the -rle flag, RT_BYTE_ENCODED, which is smaller but,
-     well, less standard.  The default is -rle.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     rasttopnm, pnm
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 pnmtoxwd              
-     pnmtoxwd - convert a portable anymap into an X11 window dump
-
-2 Synopsis
-     pnmtoxwd [-pseudodepth n] [-directcolor] [pnmfile]
-
-2 Description
-     Reads a portable anymap as input.  Produces  an  X11  window
-     dump as output.  This window dump can be displayed using the
-     xwud tool.
-
-     Normally, pnmtoxwd produces a StaticGray dump file  for  pbm
-     and  pgm  files.  For ppm, it writes a PseudoColor dump file
-     if  there  are  up  to  256  colors  in  the  input,  and  a
-     DirectColor  dump file otherwise.  The -directcolor flag can
-     be used to force a DirectColor dump.  And  the  -pseudodepth
-     flag  can  be  used to change the depth of PseudoColor dumps
-     from the default of 8 bits / 256 colors.
-
-2 See_Also
-     xwdtopnm, pnm, xwud
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 rasttopnm             
-     rasttopnm - convert a Sun rasterfile into a portable anymap
-
-2 Synopsis
-     rasttopnm [rastfile]
-
-2 Description
-     Reads a Sun rasterfile as input.  Produces a portable anymap
-     as output.  The type of the output file depends on the input
-     file - if it's black & white, a pbm file is written, else if
-     it's  grayscale  a  pgm  file, else a ppm file.  The program
-     tells you which type it is writing.
-
-2 See_Also
-     pnmtorast, pnm
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 xwdtopnm              
-     xwdtopnm - convert a X11 or X10  window  dump  file  into  a
-     portable anymap
-
-2 Synopsis
-     xwdtopnm [xwdfile]
-
-2 Description
-     Reads a X11 or X10 window dump file as  input.   Produces  a
-     portable  anymap  as  output.   The  type of the output file
-     depends on the input file - if it's black  &  white,  a  pbm
-     file  is  written, else if it's grayscale a pgm file, else a
-     ppm file.  The program tells you which type it is writing.
-
-     Using this  program,  you  can  convert  anything  on  an  X
-     workstation's  screen into an anymap.  Just display whatever
-     you're interested in, do an xwd, run  it  through  xwdtopnm,
-     and then use pnmcut to select the part you want.
-
-2 Bugs
-     I haven't tested this tool with very many configurations, so
-     there  are  probably  bugs.   Please let me know if you find
-     any.
-
-2 See_Also
-     pnmtoxwd, pnm, xwd
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 zeisstopnm            
-     zeisstopnm - convert a Zeiss confocal file into  a  portable
-     anymap
-
-2 Synopsis
-     zeisstopnm [-pgm | -ppm] [zeissfile]
-
-2 Description
-     Reads a Zeiss confocal file as input.  Produces  a  portable
-     anymap  as  output.   The type of the output file depends on
-     the input file - if it's grayscale a pgm file,  else  a  ppm
-     file  will be produced.  The program tells you which type it
-     is writing.
-
-2 Options
-     -pgm Force the output to be a pgm file.
-
-     -ppm Force the output to be a ppm file.
-
-2 See_Also
-     pnm
-
-2 Author
-     Copyright (C) 1993 by Oliver Trepte
-
-1 pnmgamma              
-     pnmgamma - perform gamma correction on a portable anymap
-
-2 Synopsis
-     pnmgamma value [pnmfile]
-     pnmgamma redvalue greenvalue bluevalue [pnmfile]
-
-2 Description
-     Reads a portable anymap as input.   Performs  gamma  correc-
-     tion, and produces a portable anymap as output.
-
-     The arguments specify what gamma value(s) to use.   A  value
-     of 1.0 leaves the image alone, less than one darkens it, and
-     greater than one lightens it.
-
-2 See_Also
-     pnm
-
-2 Author
-     Copyright (C) 1991 by Bill Davidson and Jef Poskanzer.
-
-1 pnmhistmap
-     pnmhistmap - draw a histogram for a PGM or PPM file
-
-2 Synopsis
-     pnmhistmap [-black] [-white] [-max N] [-verbose] [pnmfile]
-
-2 Description
-     Reads a portable anymap  as  input,  although  bitmap  (PBM)
-     input  produces  an error message and no image.  Produces an
-     image showing a histogram of the color (or gray)  values  in
-     the  input.  A graymap (PGM) input produces a bitmap output.
-     A pixmap (PPM) input produces pixmap output with three over-
-     laid  histograms:  a  red one for the red input, a green one
-     for the green input, and a blue one for the blue input.  The
-     output is fixed in size: 256 pixels wide by 200 pixels high.
-
-2 Options
-     -black
-          Ignores the count of black pixels when scaling the his-
-          togram.
-
-     -white
-          Ignores the count of white pixels when scaling the his-
-          togram.
-
-     The -black and -white options, which can be used  seperately
-     or  together,  are useful for images with a large percentage
-     of pixels whose value is zero or 255, which  can  cause  the
-     remaining  histogram  data to become unreadbaly small.  Note
-     that, for pixmap inputs, these options apply to all  colors;
-     if,  for example, the input has a large number of bright-red
-     areas, you will probably want to use the -white option.
-
-     -max N
-          Force the scaling of the histogram  to  use  N  as  the
-          largest-count  value.  This is useful for inputs with a
-          large percentage of single-color pixels which  are  not
-          black or white.
-
-     -verbose
-          Report the progress of making the histogram,  including
-          the largest-count value used to scale the output.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 Bugs
-     Assumes maxval is always 255.  Images with a smaller  maxval
-     will  only  use the lower-value side of the histogram.  This
-     can be overcome either by piping the input through "pnmdepth
-     255"  or  by cutting and scaling the lower-value side of the
-     histogram.  Neither is a particularly elegant solution.
-     Should allow the output size to be specified.
-
-2 See_Also
-     pgmhist(1), ppmhist(1), pgm(5), ppm(5)
-
-2 Author
-     Wilson H. Bent. Jr. (whb@usc.edu).
-
-1 pnmnlfilt             
-     pnmnlfilt - non-linear filters:  smooth,  alpha  trim  mean,
-     optimal estimation smoothing, edge enhancement.
-
-2 Synopsis
-     pnmnlfilt alpha radius [pnmfile]
-
-2 Description
-     This is something of a swiss army knife  filter.  It  has  3
-     distinct  operating modes. In all of the modes each pixel in
-     the image is examined and processed according to it and  its
-     surrounding pixels values. Rather than using the 9 pixels in
-     a 3x3 block, 7 hexagonal area samples are taken, the size of
-     the  hexagons  being  controlled  by the radius parameter. A
-     radius value of 0.3333 means that the 7 hexagons exactly fit
-     into  the  center  pixel  (ie.   there  will be no filtering
-     effect). A radius value of 1.0 means  that  the  7  hexagons
-     exactly fit a 3x3 pixel array.
-
-        Alpha trimmed mean filter.    (0.0 <= alpha
-     The value of the center pixel will be replaced by  the  mean
-     of the 7 hexagon values, but the 7 values are sorted by size
-     and the top and bottom alpha portion of the 7  are  excluded
-     from  the  mean.   This  implies  that an alpha value of 0.0
-     gives the same sort of output as a normal  convolution  (ie.
-     averaging  or smoothing filter), where radius will determine
-     the "strength" of the filter. A good value to start from for
-     subtle  filtering  is  alpha = 0.0, radius = 0.55 For a more
-     blatant effect, try alpha 0.0 and radius 1.0
-
-     An alpha value of 0.5 will cause the median value of  the  7
-     hexagons  to be used to replace the center pixel value. This
-     sort of filter is good for eliminating "pop" or single pixel
-     noise  from  an  image  without  spreading  the noise out or
-     smudging features on the image. Judicious use of the  radius
-     parameter  will fine tune the filtering. Intermediate values
-     of alpha give effects somewhere between smoothing and  "pop"
-     noise  reduction.  For  subtle  filtering  try starting with
-     values of alpha = 0.4, radius =  0.6   For  a  more  blatant
-     effect try alpha = 0.5, radius = 1.0
-
-        Optimal estimation smoothing. (1.0 <= alpha
-     This type of filter applies a  smoothing  filter  adaptively
-     over  the  image.   For  each pixel the variance of the sur-
-     rounding hexagon values is calculated,  and  the  amount  of
-     smoothing  is made inversely proportional to it. The idea is
-     that if the variance is small then it is due to noise in the
-     image,  while  if  the  variance  is large, it is because of
-     "wanted" image features. As usual the radius parameter  con-
-     trols  the  effective  radius,  but it probably advisable to
-     leave the radius between 0.8 and 1.0 for the variance calcu-
-     lation to be meaningful.  The alpha parameter sets the noise
-     threshold, over which less smoothing  will  be  done.   This
-     means  that  small values of alpha will give the most subtle
-     filtering effect, while large values will tend to smooth all
-     parts of the image. You could start with values like alpha =
-     1.2, radius = 1.0 and try increasing or decreasing the alpha
-     parameter  to get the desired effect. This type of filter is
-     best for filtering out dithering noise in  both  bitmap  and
-     color images.
-
-        Edge enhancement. (-0.1 >= alpha >=
-     This is the opposite type of filter to the smoothing filter.
-     It  enhances  edges. The alpha parameter controls the amount
-     of edge enhancement, from subtle (-0.1) to  blatant  (-0.9).
-     The radius parameter controls the effective radius as usual,
-     but useful values are between 0.5 and 0.9. Try starting with
-     values of alpha = 0.3, radius = 0.8
-
-        Combination use.
-     The various modes of pnmnlfilt can be  used  one  after  the
-     other  to  get  the  desired  result. For instance to turn a
-     monochrome dithered image into a grayscale image  you  could
-     try one or two passes of the smoothing filter, followed by a
-     pass of the optimal estimation filter, then some subtle edge
-     enhancement. Note that using edge enhancement is only likely
-     to be useful after one  of  the  non-linear  filters  (alpha
-     trimmed mean or optimal estimation filter), as edge enhance-
-     ment is the direct opposite of smoothing.
-
-     For reducing color quantization noise in images (ie. turning
-     .gif  files  back into 24 bit files) you could try a pass of
-     the optimal estimation filter (alpha  1.2,  radius  1.0),  a
-     pass of the median filter (alpha 0.5, radius 0.55), and pos-
-     sibly a pass of the edge enhancement filter.  Several passes
-     of the optimal estimation filter with declining alpha values
-     are more effective than a single pass  with  a  large  alpha
-     value.   As  usual,  there  is  a tradeoff between filtering
-     effectiveness  and  loosing   detail.   Experimentation   is
-     encouraged.
-
-2 References
-     The alpha-trimmed mean filter is based on the description in
-     IEEE  CG&A  May  1990  Page 23 by Mark E. Lee and Richard A.
-     Redner, and has been  enhanced  to  allow  continuous  alpha
-     adjustment.
-
-     The optimal estimation filter is taken from an article "Con-
-     verting  Dithered  Images  Back  to  Gray  Scale"  by  Allen
-     Stenger, Dr Dobb's Journal, November 1992, and this  article
-     references "Digital Image Enhancement and Noise Filtering by
-     Use of Local Statistics", Jong-Sen Lee, IEEE Transactions on
-     Pattern Analysis and Machine Intelligence, March 1980.
-     The edge enhancement details are from  pgmenhance,  which
-     is  taken  from Philip R. Thompson's "xim" program, which in
-     turn took it from section 6 of  "Digital  Halftones  by  Dot
-     Diffusion", D. E. Knuth, ACM Transaction on Graphics Vol. 6,
-     No. 4, October 1987, which in turn  got  it  from  two  1976
-     papers by J. F. Jarvis et. al.
-
-2 See_Also
-     pgmenhance, pnmconvol, pnm
-
-2 Bugs
-     Integers and tables may overflow if PPM_MAXMAXVAL is greater
-     than 255.
-
-2 Author
-     Graeme W. Gill    graeme@labtam.oz.au
-
-1 pnmrotate             
-     pnmrotate - rotate a portable anymap by some angle
-
-2 Synopsis
-     pnmrotate [-noantialias] angle [pnmfile]
-
-2 Description
-     Reads a portable anymap as input.  Rotates it by the  speci-
-     fied angle and produces a portable anymap as output.  If the
-     input file is in color, the output will be too, otherwise it
-     will  be  grayscale.   The  angle  is  in  degrees (floating
-     point), measured counter-clockwise.  It can be negative, but
-     it  should  be  between  -90  and  90.   Also, for rotations
-     greater than 45 degrees you may get better  results  if  you
-     first use pnmflip to do a 90 degree rotation and then pnmro-
-     tate less than 45 degrees back the other direction
-
-     The rotation algorithm is Alan Paeth's  three-shear  method.
-     Each  shear is implemented by looping over the source pixels
-     and distributing fractions to each of the  destination  pix-
-     els.   This has an "anti-aliasing" effect - it avoids jagged
-     edges and similar artifacts.  However, it  also  means  that
-     the  original  colors  or gray levels in the image are modi-
-     fied.  If you need to keep precisely the same set of colors,
-     you  can  use the -noantialias flag.  This does the shearing
-     by moving pixels without changing their values.  If you want
-     anti-aliasing  and  don't care about the precise colors, but
-     still need a limited *number* of colors,  you  can  run  the
-     result through ppmquant.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 References
-     "A Fast Algorithm  for  General  Raster  Rotation"  by  Alan
-     Paeth, Graphics Interface '86, pp. 77-81.
-
-2 See_Also
-     pnmshear, pnmflip, pnm, ppmquant
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 pnmshear              
-     pnmshear - shear a portable anymap by some angle
-
-2 Synopsis
-     pnmshear [-noantialias] angle [pnmfile]
-
-2 Description
-     Reads a portable anymap as input.  Shears it by  the  speci-
-     fied angle and produces a portable anymap as output.  If the
-     input file is in color, the output will be too, otherwise it
-     will  be  grayscale.   The  angle  is  in  degrees (floating
-     point), and measures this:
-         +-------+  +-------+
-         |       |  |\       \
-         |  OLD  |  | \  NEW  \
-         |       |  |an\       \
-         +-------+  |gle+-------+
-     If the angle is negative, it shears the other way:
-         +-------+  |-an+-------+
-         |       |  |gl/       /
-         |  OLD  |  |e/  NEW  /
-         |       |  |/       /
-         +-------+  +-------+
-     The angle should not get too close to  90  or  -90,  or  the
-     resulting anymap will be unreasonably wide.
-
-     The shearing is implemented by looping over the source  pix-
-     els  and  distributing  fractions to each of the destination
-     pixels.  This has an  "anti-aliasing"  effect  -  it  avoids
-     jagged  edges and similar artifacts.  However, it also means
-     that the original colors or gray levels  in  the  image  are
-     modified.   If  you  need  to keep precisely the same set of
-     colors, you can use the -noantialias flag.   This  does  the
-     shearing by moving pixels without changing their values.  If
-     you want anti-aliasing and  don't  care  about  the  precise
-     colors, but still need a limited *number* of colors, you can
-     run the result through ppmquant.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     pnmrotate, pnmflip, pnm, ppmquant
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 tifftopnm             
-     tifftopnm - convert a TIFF file into a portable anymap
-
-2 Synopsis
-     tifftopnm [-headerdump] tifffile
-
-2 Description
-     Reads a TIFF file as input.  Produces a portable  anymap  as
-     output.   The  type  of the output file depends on the input
-     file - if it's black & white, a pbm file is written, else if
-     it's  grayscale  a  pgm  file, else a ppm file.  The program
-     tells you which type it is writing.
-
-2 Options
-     -headerdump
-          Dump TIFF file information to stderr.  This information
-          may  be  useful in debugging TIFF file conversion prob-
-          lems.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     pnmtotiff, pnm
-
-2 Bugs
-     This program is not self-contained.   To  use  it  you  must
-     fetch  the TIFF Software package listed in the OTHER.SYSTEMS
-     file and configure PBMPLUS to use  libtiff.   See  PBMPLUS's
-     Makefile for details on this configuration.
-
-2 Author
-     Derived by Jef Poskanzer from tif2ras.c, which is  Copyright
-     (c)  1990  by  Sun  Microsystems,  Inc.   Author: Patrick J.
-     Naughton (naughton@wind.sun.com).
-
-1 pnmtotiff             
-     pnmtotiff - convert a a portable anymap into a TIFF file
-
-2 Synopsis
-     pnmtotiff [-none|-packbits| -lzw|-g3|-g4] [-2d]  [-fill]  [-
-     predictor n] [-msb2lsb|-lsb2msb] [-rowsperstrip n] [pnmfile]
-
-2 Description
-     Reads a portable anymap as input.  Produces a TIFF  file  as
-     output.
-
-2 Options
-     By default, pnmtotiff creates a TIFF file with LZW  compres-
-     sion.   This  is  your  best bet most of the time.  However,
-     some TIFF readers can't deal with it.  If you  want  to  try
-     another  compression  scheme or tweak some of the other even
-     more obscure output options, there are a number of flags  to
-     play with.
-
-     The -none, -packbits, -lzw, -g3, and -g4 options are used to
-     override  the default and set the compression scheme used in
-     creating the output file.  The CCITT Group  3  and  Group  4
-     compression  algorithms  can only be used with bilevel data.
-     The -2d and -fill options are meaningful only with  Group  3
-     compression:  -2d  requests  2-dimensional encoding, while -
-     fill requests that each encoded scanline be zero-filled to a
-     byte boundry.  The -predictor option is only meaningful with
-     LZW compression: a predictor value of 2 causes each scanline
-     of  the  output  image  to  undergo  horizontal differencing
-     before it is encoded; a value of 1 forces each  scanline  to
-     be  encoded  without  differencing.   By  default, pnmtotiff
-     creates a TIFF file  with  msb-to-lsb  fill  order.   The  -
-     msb2lsb  and  -lsb2msb  options  are  used  to  override the
-     default and set the fill order used in  creating  the  file.
-     The  -rowsperstrip  option  can be used to set the number of
-     rows (scanlines) in each strip of data in the  output  file.
-     By default, the output file has the number of rows per strip
-     set to a value that will ensure each strip is no more than 8
-     kilobytes long.
-
-2 Bugs
-     This program is not self-contained.   To  use  it  you  must
-     fetch  the TIFF Software package listed in the OTHER.SYSTEMS
-     file and configure PBMPLUS to use  libtiff.   See  PBMPLUS's
-     Makefile for details on this configuration.
-
-2 See_Also
-     tifftopnm, pnm
-
-2 Author
-     Derived by Jef Poskanzer from ras2tiff.c, which is Copyright
-     (c)  1990  by  Sun  Microsystems,  Inc.   Author: Patrick J.
-     Naughton (naughton@wind.sun.com).
-
-1 libpnm
-     libpnm - functions to support portable anymap programs
-
-2 Synopsis
-     #include <pnm.h>
-     cc ... libpnm.a libppm.a libpgm.a libpbm.a
-
-
-2 Description
-  TYPES AND CONSTANTS
-     typedef ... xel;
-     typedef ... xelval;
-     #define PNM_MAXMAXVAL ...
-     extern xelval pnm_pbmmaxval;
-
-     Each xel contains three xelvals, each of which  should  con-
-     tain   only   the   values   between  0  and  PNM_MAXMAXVAL.
-     pnm_pbmmaxval is the maxval used when a PNM program reads  a
-     PBM  file.   Normally it is 1; however, for some programs, a
-     larger value gives better results.
-
-  XEL MANIPULATIONS
-     xelval PNM_GET1( xel x )
-
-     This macro extracts a single value from  an  xel,  when  you
-     know  it's  from  a  PBM  or PGM file.  When it's from a PPM
-     file, use PPM_GETR(), PPM_GETG(), and PPM_GETB().
-
-     void PNM_ASSIGN1( xel x, xelval v )
-
-     This macro assigns a single value to an xel, when  you  know
-     it's from a PBM or PGM file.  When it's from a PPM file, use
-     PPM_ASSIGN().
-
-     int PNM_EQUAL( xel x, xel y )
-
-     This macro checks two xels for equality.
-
-     int PNM_FORMAT_TYPE( int format )
-
-     For distinguishing different file types.
-
-  INITIALIZATION
-     void pnm_init( int* argcP, char* argv[] )
-
-     All PNM programs must call this routine.
-
-  MEMORY MANAGEMENT
-     xel** pnm_allocarray( int cols, int rows )
-
-     Allocate an array of xels.
-     xel* pnm_allocrow( int cols )
-
-     Allocate a row of the given number of xels.
-
-     void pnm_freearray( xel** xels, int rows )
-
-     Free the array allocated  with  pnmllocarray()  containing
-     the given number of rows.
-
-     void pnm_freerow( xel* xelrow )
-
-     Free a row of xels.
-
-  READING FILES
-     void pnm_readpnminit( FILE* fp, int* colsP, int* rowsP, xelval* maxvalP, int* formatP )
-
-     Read the header from a PNM file, filling in the rows,  cols,
-     maxval and format variables.
-
-     void pnm_readpnmrow( FILE* fp, xel* xelrow, int cols, xelval maxval, int format )
-
-     Read a row of xels into the xelrow array.  Format, cols, and
-     maxval were filled in by pnm_readpnminit().
-
-     xel** pnm_readpnm( FILE* fp, int* colsP, int* rowsP, xelval* maxvalP, int* formatP )
-
-     Read an entire anymap file into memory, returning the  allo-
-     cated  array and filling in the rows, cols, maxval, and for-
-     mat variables.  This  function  combines  pnm_readpnminit(),
-     pnm_allocarray()    and    pnm_readpnmrow().    Unlike   the
-     equivalent functions in PBM, PGM, and PPM,  it  returns  the
-     format so you can tell what type the file is.
-
-  WRITING FILES
-     void pnm_writepnminit( FILE* fp, int cols, int rows, xelval maxval, int format, int forceplain )
-
-     Write the header for a portable  anymap  file.   Unlike  the
-     equivalent  functions  in  PBM,  PGM,  and  PPM, you have to
-     specify the output  type.   The  forceplain  flag  forces  a
-     plain-format  file to be written, as opposed to a raw-format
-     one.
-
-     void pnm_writepnmrow( FILE* fp, xel* xelrow, int cols, xelval maxval, int format, int forceplain )
-
-     Write a row from a portable anymap.
-
-     void pnm_writepnm( FILE* fp, xel** xels, int cols, int rows, xelval maxval, int format, int forceplain )
-
-     Write the header and all data for a portable  anymap.   This
-     function combines pnm_writepnminit() and pnm_writepnmrow().
-
-  FORMAT PROMOTION
-     void pnm_promoteformatrow( xel* xelrow, int cols, xelval maxval, int format, xelval newmaxval, int newformat )
-
-     Promote a row of xels from one maxval and format  to  a  new
-     set.   Used  when  combining  multiple  anymaps of different
-     types - just take the max of the maxvals and the max of  the
-     formats, and promote them all to that.
-
-     void pnm_promoteformat( xel** xels, int cols, int rows, xelval maxval, int format, xelval newmaxval, int newformat )
-
-     Promote an entire anymap.
-
-  XEL MANIPULATION
-     xel pnm_whitexel( xelval maxval, int format )
-     xel pnm_blackxel( xelval maxval, int format )
-
-     Return a white or black xel for the given maxval and format.
-
-     void pnm_invertxel( xel* x, xelval maxval, int format )
-
-     Invert an xel.
-
-     xel pnm_backgroundxelrow( xel* xelrow, int cols, xelval maxval, int format )
-
-     Figure out an appropriate background xel based on this row.
-
-     xel pnm_backgroundxel( xel** xels, int cols, int rows, xelval maxval, int format )
-
-     Figure out a background xel based on an entire anymap.  This
-     can do a slightly better job than pnm_backgroundxelrow().
-
-2 See_Also
-     pbm, pgm, ppm
-
-2 Author
-     Copyright (C) 1989, 1991 by Tony Hansen and Jef Poskanzer.
-
-1 pnm
-     pnm - portable anymap file format
-
-2 Description
-     The pnm programs operate on portable bitmaps, graymaps,  and
-     pixmaps,  produced by the pbm, pgm, and ppm segments.  There
-     is no file format associated with pnm itself.
-
-2 See_Also
-    anytopnm,    rasttopnm,  tifftopnm,  xwdtopnm,  pnmtops,  pnmtorast,
-    pnmtotiff, pnmtoxwd, pnmar- ith, pnmcat, pnmconvol, pnmcrop, pnmcut,
-    pnmdepth,  pnmenlarge,    pnmfile,    pnmflip,  pnmgamma,  pnmindex,
-    pnminvert,  pnmmargin,  pnmnoraw,   pnmpaste,  pnmrotate,  pnmscale,
-    pnmshear, pnmsmooth, pnmtile, ppm, pgm, pbm
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-1 bmptoppm
-     bmptoppm - convert a BMP file into a portable pixmap
-
-2 Synopsis
-     bmptoppm [bmpfile]
-
-2 Description
-     Reads a Microsoft Windows or OS/2 BMP file as  input.   Pro-
-     duces a portable pixmap as output.
-
-2 See_Also
-     ppmtobmp, ppm
-
-2 Author
-     Copyright (C) 1992 by David W. Sanderson.
-
-1 gouldtoppm
-     gouldtoppm - convert Gould scanner file into a portable pix-
-     map
-
-2 Synopsis
-     gouldtoppm [gouldfile]
-
-2 Description
-     Reads a file produced by the Gould scanner as  input.   Pro-
-     duces a portable pixmap as output.
-
-2 See_Also
-     ppm
-
-2 Author
-     Copyright(C) 1990 by Stephen Paul Lesniewski
-
-1 ilbmtoppm
-     ilbmtoppm - convert an ILBM file into a portable pixmap
-
-2 Synopsis
-     ilbmtoppm [-verbose] [ILBMfile]
-
-2 Description
-     Reads an IFF ILBM file as input.  Produces a portable pixmap
-     as output.  Supported ILBM types are:
-
-     Normal ILBMs with 1-16 planes.
-
-     Amiga Extra-Halfbrite (EHB)
-
-     Amiga Hold-and-modify (HAM) with 3-16 planes.
-
-     24 bit.
-
-     Color map (BMHD + CMAP chunk only, nPlanes = 0).
-
-     Unofficial direct color.
-          1-16 planes for each color component.
-
-     Chunks used:
-          BMHD, CMAP, CAMG (only HAM  &  EHB  flags  used),  BODY
-          unofficial DCOL chunk to identify direct color ILBM
-
-     Chunks ignored:
-          GRAB, DEST, SPRT, CRNG, CCRT, CLUT, DPPV, DRNG, EPSF
-
-     Other chunks (ignored but displayed in verbose mode):
-          NAME, AUTH, (c), ANNO, DPI
-
-     Unknown chunks are skipped.
-
-2 Options
-     -verbose
-          Give some informaton about the ILBM file.
-
-2 Bugs
-     Probably.
-
-2 References
-     Amiga ROM Kernel Reference Manual - Devices (3rd Ed.)
-     Addison Wesley, ISBN 0-201-56775-X
-
-2 See_Also
-     ppm(5), ppmtoilbm(1)
-
-2 Authors
-     Copyright (C) 1989 by Jef Poskanzer.
-     Modified June 1993 by Ingo Wilken
-     (Ingo.Wilken@informatik.uni-oldenburg.de)
-
-1 imgtoppm
-     imgtoppm - convert an Img-whatnot file into a portable  pix-
-     map
-
-2 Synopsis
-     imgtoppm [imgfile]
-
-2 Description
-     Reads an Img-whatnot file as  input.   Produces  a  portable
-     pixmap  as output.  The Img-whatnot toolkit is available for
-     FTP on venera.isi.edu, along with numerous  images  in  this
-     format.
-
-2 See_Also
-     ppm
-
-2 Author
-     Based on a simple conversion program posted to comp.graphics
-     by Ed Falk.
-
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 mtvtoppm
-     mtvtoppm - convert output from the MTV or  PRT  ray  tracers
-     into a portable pixmap
-
-2 Synopsis
-     mtvtoppm [mtvfile]
-
-2 Description
-     Reads an input  file  from  Mark  VanDeWettering's  MTV  ray
-     tracer.  Produces a portable pixmap as output.
-
-     The PRT raytracer also produces this format.
-
-2 See_Also
-     ppm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 pcxtoppm
-     pcxtoppm - convert a PCX file into a portable pixmap
-
-2 Synopsis
-     pcxtoppm [pcxfile]
-
-2 Description
-     Reads a PCX file as input.  Produces a  portable  pixmap  as
-     output.
-
-2 See_Also
-     ppmtopcx, ppm
-
-2 Author
-     Copyright (C) 1990 by Michael Davidson.
-
-1 pgmtoppm
-     pgmtoppm - colorize a portable graymap into a portable  pix-
-     map
-
-2 Synopsis
-     pgmtoppm colorspec [pgmfile]
-     pgmtoppm colorspec1-colorspec2 [pgmfile]
-     pgmtoppm -map mapfile [pgmfile]
-
-2 Description
-     Reads a portable graymap as input.  Colorizes it  by  multi-
-     plying the the gray values by specified color or colors, and
-     produces a portable pixmap as output.
-
-     If only one color is specified, black in the pgm file  stays
-     black  and  white  in  the pgm file turns into the specified
-     color in the ppm file.  If two colors (separated by a  dash)
-     are specified, then black gets mapped to the first color and
-     white gets mapped to the second.
-
-     The color can be specified in five ways:
-
-     o    A name, assuming that a pointer to an  X11-style  color
-          names file was compiled in.
-
-     o    An X11-style hexadecimal specifier: rgb:r/g/b, where  r
-          g and b are each 1- to 4-digit hexadecimal numbers.
-
-     o    An X11-style decimal specifier: rgbi:r/g/b, where  r  g
-          and b are floating point numbers between 0 and 1.
-
-     o    For backwards compatibility, an  old-X11-style  hexade-
-          cimal    number:    #rgb,   #rrggbb,   #rrrgggbbb,   or
-          #rrrrggggbbbb.
-
-     o    For  backwards  compatibility,  a  triplet  of  numbers
-          separated  by commas: r,g,b, where r g and b are float-
-          ing point numbers between 0 and  1.   (This  style  was
-          added before MIT came up with the similar rgbi style.)
-
-     Also, the -map flag lets you specify an entire  colormap  to
-     be  used.   The  mapfile  is  just a ppm file; it can be any
-     shape, all that matters is the colors in it and their order.
-     In  this case, black gets mapped into the first color in the
-     map file, and white gets mapped to the last.
-
-2 See_Also
-     rgb3toppm, ppmtopgm, ppmtorgb3, ppm, pgm
-
-2 Author
-     Copyright (C) 1991 by Jef Poskanzer.
-
-1 pi1toppm
-     pi1toppm - convert an Atari Degas .pi1 into a portable  pix-
-     map
-
-2 Synopsis
-     pi1toppm [pi1file]
-
-2 Description
-     Reads an Atari Degas .pi1 file as input.  Produces  a  port-
-     able pixmap as output.
-
-2 See_Also
-     ppmtopi1, ppm, pi3topbm, pbmtopi3
-
-2 Author
-     Copyright (C) 1991 by Steve Belczyk (seb3@gte.com)  and  Jef
-     Poskanzer.
-
-1 picttoppm
-     picttoppm - convert a Macintosh PICT file  into  a  portable
-     pixmap
-
-2 Synopsis
-     picttoppm [-verbose] [-fullres] [-noheader] [pictfile]
-
-2 Description
-     Reads a PICT file (version 1 or 2) and  outputs  a  portable
-     pixmap.   Useful  as  the first step in converting a scanned
-     image to something that can be displayed on Unix.
-
-2 Options
-     -fullres
-          Force any images in the PICT file to be output with  at
-          least  their full resolution.  A PICT file may indicate
-          that a contained image is to be scaled down before out-
-          put.   This  option forces images to retain their sizes
-          and prevent information loss.
-
-     -noheader
-          Do not skip the 512 byte header that is present on  all
-          PICT  files.   This  is  useful when you have PICT data
-          that was not stored in the data fork of a PICT file.
-
-     -verbose
-          Turns on verbose mode which prints a a whole  bunch  of
-          information  that  only  picttoppm  hackers really care
-          about.
-
-2 Bugs
-     The PICT file format is a general drawing format.  picttoppm
-     only  supports a small subset of its operations but is still
-     very useful for files produced  by  scanning  software.   In
-     particular,  text  added to a scanned image will be silently
-     ignored.
-
-2 See_Also
-     Inside Macintosh volume 5, ppmtopict, ppm
-
-2 Author
-     Copyright 1989 George Phillips
-
-1 pjtoppm
-     pjtoppm - convert an HP PaintJet file to a portable pixmap
-
-2 Synopsis
-     pjtoppm [paintjet]
-
-2 Description
-     Reads an HP PaintJet file as input and converts  it  into  a
-     portable  pixmap.  This was a quick hack to save some trees,
-     and it only handles a small subset of the paintjet commands.
-     In  particular,  it will only handle enough commands to con-
-     vert most raster image files.
-
-REFERENCES
-     HP PaintJet XL Color Graphics Printer User's Guide
-
-2 See_Also
-     ppmtopj
-
-2 Author
-     Copyright (C) 1991 by Christos Zoulas.
-
-1 ppm3d
-     ppm3d - convert two  portable  pixmap  into  a  red/blue  3d
-     glasses pixmap
-
-2 Synopsis
-     ppm3d leftppmfile rightppmfile [horizontal offset]
-
-2 Description
-     Reads two portable pixmaps as input.   Produces  a  portable
-     pixmap   as   output,   with   the   images  overlapping  by
-     horizontal offset
-
-     pixels in blue/red format.
-
-     horizontal offset defaults to 30 pixels.   Pixmaps  MUST  be
-     the same size.
-
-2 See_Also
-     ppm(5)
-
-2 Author
-     Copyright (C) 1993 by David K. Drum.
-
-1 ppmbrighten
-     ppmbrighten - change an images Saturation and Value from  an
-     HSV map
-
-2 Synopsis
-     ppmbrighten  [-n]  [-s  <+-  saturation>]  [-v  <+-  value>]
-     <ppmfile>
-
-2 Description
-     Reads a portable pixmap as input.  Converts the  image  from
-     RGB  space  to HSV space and changes the Value by <+- value>
-     as a percentage.  Likewise with  the  Saturation.   Doubling
-     the Value would involve
-
-     ppmbrighten -v 100
-
-     to add 100 percent to the Value.
-
-     The 'n' option normalizes the Value to exist between 0 and 1
-     (normalized).
-
-2 See_Also
-     pgmnorm, ppm
-
-2 Notes
-     This program does not change the number of colors.
-
-2 Author
-     Copyright (C) 1990 by Brian Moffet 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 docu-
-     mentation.   This  software  is  provided  "as  is"  without
-     express or implied warranty.
-
-1 ppmchange
-     ppmchange -  change  all  pixels  of  one  color  to another in a
-     portable pixmap
-
-2 Synopsis
-     ppmchange colorspec1 colorspec2 [ppmfile]
-
-2 Description
-     Reads  a portable  pixmap  as  input.    Changes  all  pixels  of
-     colorspec1 to colorspec2, leaving all others unchanged.
-
-     The color can be specified in five ways:
-
-     o    A name, assuming that a pointer to an  X11-style  color
-          names file was compiled in.
-
-     o    An X11-style hexadecimal specifier: rgb:r/g/b, where  r
-          g and b are each 1- to 4-digit hexadecimal numbers.
-
-     o    An X11-style decimal specifier: rgbi:r/g/b, where  r  g
-          and b are floating point numbers between 0 and 1.
-
-     o    For backwards compatibility, an  old-X11-style  hexade-
-          cimal    number:    #rgb,   #rrggbb,   #rrrgggbbb,   or
-          #rrrrggggbbbb.
-
-     o    For  backwards  compatibility,  a  triplet  of  numbers
-          separated  by commas: r,g,b, where r g and b are float-
-          ing point numbers between 0 and  1.   (This  style  was
-          added before MIT came up with the similar rgbi style.)
-
-2 See_Also
-     pgmtoppm(1), ppm(5)
-
-2 Author
-     Wilson H. Bent. Jr. (whb@usc.edu)
-
-1 ppmdim
-     ppmdim - dim a portable pixmap down to total blackness
-
-2 Synopsis
-     ppmdim dimfactor [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.    Diminishes its brightness by
-     the specified dimfactor down to total blackness.    The dimfactor
-     may be in the range from 0.0 (total  blackness, deep night, nada,
-     null, nothing) to 1.0 (original picture's brightness).
-
-     As pnmgamma does not do the brightness correction in  the  way  I
-     wanted it, this small program was written.
-
-     ppmdim is similar to ppmbrighten , but not exactly the same.
-
-2 See_Also
-     ppm(5), ppmflash(1), pnmgamma(1), ppmbrighten(1)
-
-2 Author
-     Copyright (C) 1993 by Frank Neumann
-
-1 ppmdist
-     ppmdist -  simplistic grayscale assignment for machine generated,
-     color images
-
-2 Synopsis
-     ppmdist [-intensity|-frequency] [ppmfile]
-
-2 Description
-     Reads a portable pixmap  as  input,  performs  a  simplistic
-     grayscale assignment intended for use with grayscale or bit-
-     map printers.
-
-     Often conversion from ppm to pgm will yield  an  image  with
-     contrast  too low for good printer output.  The program max-
-     imizes contrast between the gray levels output.
-
-     A ppm input of n colors is read, and a pgm of n gray  levels
-     is  written.   The  gray  levels  take on the values 0..n-1,
-     while maxval takes on n-1.
-
-     The mapping from color to stepped grayscale can be performed
-     in  order of input pixel intensity, or input pixel frequency
-     (number of repetitions).
-
-2 Options
-     Helpful only for images with a very small number of  colors.
-     Perhaps should have been an option to ppmtopgm.
-
-2 See_Also
-     ppmtopgm, ppmhist, ppm
-
-2 Author
-     Copyright (C) 1993 by Dan Stromberg.
-
-1 ppmdither
-     ppmdither - ordered dither for color images
-
-2 Synopsis
-     ppmdither [-dim dimension] [-red shades] [-green shades]  [-
-     blue shades] [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input, and applies  dithering  to
-     it to reduce the number of colors used down to the specified
-     number of shades for each primary.  The  default  number  of
-     shades is red=5, green=9, blue=5, for a total of 225 colors.
-     To convert the image to a binary  rgb  format  suitable  for
-     color  printers,  use  -red 2 -green 2 -blue 2.  The maximum
-     number of colors that can be used is 256 and can be computed
-     as the product of the number of red, green and blue shades.
-
-2 Options
-     -dim dimension
-                   The size of the dithering matrix.  Must  be  a
-                   power of 2.
-
-     -red shades   The number of red shades to be  used;  minimum
-                   of 2.
-
-     -green shades The number of green shades to be used; minimum
-                   of 2.
-
-     -blue shades  The number of blue shades to be used;  minimum
-                   of 2.
-
-2 See_Also
-     pnmdepth, ppmquant, ppm
-
-2 Author
-     Copyright (C) 1991 by Christos Zoulas.
-
-1 ppmflash
-     ppmflash - brighten a picture up to complete white-out
-
-2 Synopsis
-     ppmflash flashfactor [ppmfile]
-
-2 Description
-     Reads  a portable pixmap as input.  Increases its  brightness  by
-     the  specified  flashfactor  up to a total white-out image.   The
-     flashfactor  may  be  in  the  range from 0.0 (original picture's
-     brightness) to 1.0 (full white-out, The Second After).
-
-     As pnmgamma  does  not  do the brightness correction in the way I
-     wanted it, this small program was written.
-
-     This program is similar to ppmbrighten, but not exactly the same.
-
-2 See_Also
-     ppm(5), ppmdim(1), pnmgamma(1), ppmbrighten(1)
-
-2 Author
-     Copyright (C) 1993 by Frank Neumann
-
-1 ppmhist
-     ppmhist - print a histogram of a portable pixmap
-
-2 Synopsis
-     ppmhist [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Generates a histogram  of
-     the colors in the pixmap.
-
-2 See_Also
-     ppm, pgmhist
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 ppmmake
-     ppmmake - create a pixmap of a specified size and color
-
-2 Synopsis
-     ppmmake color width height
-
-2 Description
-     Produces a portable pixmap of the  specified  color,  width,
-     and height.
-
-     The color can be specified in five ways:
-
-     o    A name, assuming that a pointer to an  X11-style  color
-          names file was compiled in.
-
-     o    An X11-style hexadecimal specifier: rgb:r/g/b, where  r
-          g and b are each 1- to 4-digit hexadecimal numbers.
-
-     o    An X11-style decimal specifier: rgbi:r/g/b, where  r  g
-          and b are floating point numbers between 0 and 1.
-
-     o    For backwards compatibility, an  old-X11-style  hexade-
-          cimal    number:    #rgb,   #rrggbb,   #rrrgggbbb,   or
-          #rrrrggggbbbb.
-
-     o    For  backwards  compatibility,  a  triplet  of  numbers
-          separated  by commas: r,g,b, where r g and b are float-
-          ing point numbers between 0 and  1.   (This  style  was
-          added before MIT came up with the similar rgbi style.)
-
-2 See_Also
-     ppm, pbmmake
-
-2 Author
-     Copyright (C) 1991 by Jef Poskanzer.
-
-1 ppmmix
-     ppmmix - blend together two portable pixmaps
-
-2 Synopsis
-     ppmmix fadefactor ppmfile1 ppmfile2
-
-2 Description
-     Reads two portable pixmaps  as  input.  Mixes them together using
-     the specified fade factor.   The  fade factor may be in the range
-     from 0.0 (only ppmfile1's image data)  to  1.0  (only  ppmfile2's
-     image data).  Anything in between gains  a  smooth  blend between
-     the two images.
-
-     The two pixmaps must have the same size.
-
-2 See_Also
-     ppm(5)
-
-2 Author
-     Copyright (C) 1993 by Frank Neumann
-
-1 ppmquant
-     ppmquant - quantize the colors in  a  portable  pixmap  down to a
-     specified number
-
-2 Synopsis
-     ppmquant [-floyd|-fs] ncolors [ppmfile]
-     ppmquant [-floyd|-fs] -map mapfile [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Chooses ncolors colors to
-     best  represent  the  image, maps the existing colors to the
-     new ones, and writes a portable pixmap as output.
-
-     The quantization method is Heckbert's "median cut".
-
-     Alternately, you can skip the color-choosing step by  speci-
-     fying  your  own set of colors with the -map flag.  The map-
-     file is just a ppm file; it  can  be  any  shape,  all  that
-     matters is the colors in it.  For instance, to quantize down
-     to the 8-color IBM TTL color set, you might use:
-         P3
-         8 1
-         255
-           0   0   0
-         255   0   0
-           0 255   0
-           0   0 255
-         255 255   0
-         255   0 255
-           0 255 255
-         255 255 255
-     If you want to quantize one pixmap  to  use  the  colors  in
-     another  one,  just  use the second one as the mapfile.  You
-     don't have to reduce it down  to  only  one  pixel  of  each
-     color, just use it as is.
-
-     The -floyd/-fs flag enables a Floyd-Steinberg  error  diffu-
-     sion  step.   Floyd-Steinberg gives vastly better results on
-     images where the  unmodified  quantization  has  banding  or
-     other  artifacts, especially when going to a small number of
-     colors such as the above IBM set.   However,  it  does  take
-     substantially more CPU time, so the default is off.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 References
-     "Color Image Quantization for Frame Buffer Display" by  Paul
-     Heckbert, SIGGRAPH '82 Proceedings, page 297.
-
-2 See_Also
-     ppmquantall, pnmdepth, ppmdither, ppm
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 ppmrelief
-     ppmrelief - run a Laplacian relief filter on a portable pixmap
-
-2 Synopsis
-     ppmrelief [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Does a  Laplacian  relief
-     filter, and writes a portable pixmap as output.
-
-     The Laplacian relief filter is described in "Beyond  Photog-
-     raphy"  by  Holzmann,  equation  3.19.  It's a sort of edge-
-     detection.
-
-2 See_Also
-     pgmbentley, pgmoil, ppm
-
-2 Author
-     Copyright (C) 1990 by Wilson Bent (whb@hoh-2.att.com)
-
-1 ppmshift
-     ppmshift - shift lines of  a  portable  pixmap left or right by a
-     random amount
-
-2 Synopsis
-     ppmshift shift [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Shifts every row of image data
-     to the left or right by a certain  amount.  The 'shift' parameter
-     determines by how many pixels a row is to be shifted at most.
-
-     Another one of those effects I intended to use  for  MPEG  tests.
-     Unfortunately,  this  program will not help me here - it  creates
-     too  random patterns to be used for animations.  Still, it  might
-     give interesting results on still images.
-
-2 Example
-     Check  this  out:    Save your  favourite  model's  picture  from
-     something like alt.binaries.pictures.supermodels (ok, or from any
-     other  picture source), convert it to ppm, and  process  it  e.g.
-     like this, assuming the picture is 800x600 pixels:
-     
-       # take the upper half, and leave it like it is
-       pnmcut 0 0 800 300 cs.ppm >upper.ppm
-
-       # take the lower half, flip it upside down,  dim it and distort
-         it a little
-       pnmcut 0 300 800 300 cs.ppm | pnmflip -tb | ppmdim 0.7 |
-          ppmshift 10 >lower.ppm
-
-       # and concatenate the two pieces
-       pnmcat -tb upper.ppm lower.ppm >newpic.ppm  The  resulting
-     picture  looks  like the image being reflected on a water surface
-     with slight ripples.
-
-2 See_Also
-     ppm(5), pnmcut(1), pnmflip(1), ppmdim(1), pnmcat(1)
-
-2 Author
-     Copyright (C) 1993 by Frank Neumann
-
-1 ppmspread
-     ppmspread - displace  a  portable  pixmap's  pixels by  a  random
-     amount
-
-2 Synopsis
-     ppmspread amount [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Moves every pixel around a bit
-     relative to its original position.  amount determines by how many
-     pixels a pixel is to be moved around at most.
-
-     Pictures processed with this  filter  will  seem  to  be somewhat
-     dissolved or unfocussed (although they  appear  more  coarse than
-     images processed by something like pnmconvol ).
-
-2 See_Also
-     ppm(5), pnmconvol(1)
-
-2 Author
-     Copyright (C) 1993 by Frank Neumann
-
-1 ppmtoacad
-     ppmtoacad - convert portable pixmap to AutoCAD database or slide
-
-2 Synopsis
-     ppmtoacad [-dxb] [-poly] [-background colour] [-white] [-
-               aspect ratio] [-8] [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces an AutoCAD(Reg.)
-     slide  file or binary database import (.dxb) file as output.
-     If no ppmfile is specified,  input  is  read  from  standard
-     input.
-
-2 Options
-     -dxb An AutoCAD binary database import (.dxb) file is  writ-
-          ten.   This  file  is  read with the DXBIN command and,
-          once loaded, becomes part of  the  AutoCAD  geometrical
-          database  and  can  be viewed and edited like any other
-          object.  Each sequence of identical  pixels  becomes  a
-          separate  object  in  the  database; this can result in
-          very large AutoCAD drawing files.  However, if you want
-          to trace over a bitmap, it lets you zoom and pan around
-          the bitmap as you wish.
-
-     -poly
-          If the -dxb option is  not  specified,  the  output  of
-          ppmtoacad  is an AutoCAD slide file.  Normally each row
-          of pixels is represented by an AutoCAD line entity.  If
-          -poly  is  selected,  the pixels are rendered as filled
-          polygons.  If the slide is viewed  on  a  display  with
-          higher  resolution  than  the  source pixmap, this will
-          cause the pixels to  expand  instead  of  appearing  as
-          discrete  lines  against  the screen background colour.
-          Regrettably, this  representation  yields  slide  files
-          which  occupy  more  disc  space  and  take  longer  to
-          display.
-
-     -background colour
-          Most AutoCAD display drivers can be configured  to  use
-          any  available  colour  as the screen background.  Some
-          users perfer a black screen background,  others  white,
-          while splinter groups advocate burnt ocher, tawny puce,
-          and shocking grey.   Discarding  pixels  whose  closest
-          AutoCAD  colour  representation  is  equal to the back-
-          ground colour can substantially reduce the size of  the
-          AutoCAD  database  or  slide file needed to represent a
-          bitmap.  If no -background  colour  is  specified,  the
-          screen  background  colour is assumed to be black.  Any
-          AutoCAD colour number may be specified  as  the  screen
-          background;  colour  numbers are assumed to specify the
-          hues  defined  in  the  standard  AutoCAD  256   colour
-          palette.
-
-     -white
-          Since many AutoCAD users choose a  white  screen  back-
-          ground, this option is provided as a short-cut.  Speci-
-          fying -white is identical in effect to -background 7.
-
-     -aspect ratio
-          If the source pixmap had non-square pixels,  the  ratio
-          of  the pixel width to pixel height should be specified
-          as ratio.  The resulting slide or  .dxb  file  will  be
-          corrected  so that pixels on the AutoCAD screen will be
-          square.  For example, to correct an image  made  for  a
-          320x200 VGA/MCGA screen, specify -aspect 0.8333.
-
-     -8   Restricts the colours in the output file to the  8  RGB
-          shades.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 Bugs
-     AutoCAD has a fixed  palette  of  256  colours,  distributed
-     along  the  hue,  lightness,  and  saturation axes.  Pixmaps
-     which contain many nearly-identical colours, or colours  not
-     closely  approximated  by  AutoCAD's  palette, may be poorly
-     rendered.
-
-     ppmtoacad works best if the  system  displaying  its  output
-     supports the full 256 colour AutoCAD palette.  Monochrome, 8
-     colour, and 16 colour configurations will produce less  than
-     optimal results.
-
-     When creating a .dxb file or a slide  file  with  the  -poly
-     option, ppmtoacad finds both vertical and horizontal runs of
-     identical pixels  and  consolidates  them  into  rectangular
-     regions  to  reduce  the  size  of the output file.  This is
-     effective for images with large areas of constant colour but
-     it's no substitute for true raster to vector conversion.  In
-     particular, thin diagonal lines are not optimised at all  by
-     this process.
-
-     Output files can be huge.
-
-2 See_Also
-     AutoCAD Reference Manual: Slide File Format and Binary Draw-
-     ing Interchange (DXB) Files, ppm
-
-2 Author
-          John Walker
-          Autodesk SA
-          Avenue des Champs-Montants 14b
-          CH-2074 MARIN
-          Suisse/Schweiz/Svizzera/Svizra/Switzerland
-          Usenet:  kelvin@Autodesk.com
-          Fax:     038/33 88 15
-          Voice:   038/33 76 33
-
-     Permission  to  use,  copy,  modify,  and  distribute   this
-     software  and  its documentation for any purpose and without
-     fee is hereby granted, without any  conditions  or  restric-
-     tions.   This software is provided ``as is'' without express
-     or implied warranty.
-
-     AutoCAD and Autodesk are registered trademarks of  Autodesk,
-     Inc.
-
-1 ppmtobmp
-     ppmtobmp - convert a portable pixmap into a BMP file
-
-2 Synopsis
-     ppmtobmp [-windows] [-os2] [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces a Microsoft Win-
-     dows or OS/2 BMP file as output.
-
-2 Options
-     -windows
-          Tells the program to produce a  Microsoft  Windows  BMP
-          file.
-
-     -os2 Tells the program to produce an OS/2 BMP  file.   (This
-          is the default.)
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     bmptoppm, ppm
-
-2 Author
-     Copyright (C) 1992 by David W. Sanderson.
-
-1 ppmtogif
-     ppmtogif - convert a portable pixmap into a GIF file
-
-2 Synopsis
-     ppmtogif [-interlace] [-sort] [-map mapfile ] [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces a  GIF  file  as
-     output.
-
-2 Options
-     -interlace
-          Tells the program to produce an interlaced GIF file.
-
-     -sort
-          Produces a GIF file with a sorted color map.
-
-     -map mapfile
-
-          Uses the colors found in  the  mapfile  to  create  the
-          colormap  in  the  GIF file, instead of the colors from
-          ppmfile. The mapfile can be  any  ppm  file;  all  that
-          matters  is  the colors in it. If the colors in ppmfile
-          do not match those in mapfile , they are matched  to  a
-          "best match". A (much) better result can be obtained by
-          using the following filter in advance:
-
-          ppmquant -floyd -map mapfile
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     giftopnm, ppmquant, ppm
-
-2 Author
-     Based      on      GIFENCOD      by       David       Rowley
-     <mgardi@watdcsu.waterloo.edu>.  Lempel-Ziv compression based
-     on "compress".
-
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 ppmtoicr
-     ppmtoicr - convert a portable pixmap into NCSA ICR format
-
-2 Synopsis
-     ppmtoicr  [-windowname  name]  [-expand  expand]   [-display
-     display] [-rle] [ppmfile]
-
-2 Description
-     Reads a portable pixmap file as  input.   Produces  an  NCSA
-     Telnet  Interactive Color Raster graphic file as output.  If
-     ppmfile is not supplied, ppmtoicr will  read  from  standard
-     input.
-
-     Interactive Color Raster (ICR) is a protocol for  displaying
-     raster  graphics  on  workstation  screens.  The protocol is
-     implemented in NCSA Telnet for the  Macintosh  version  2.3.
-     The  ICR  protocol  shares  characteristics of the Tektronix
-     graphics terminal emulation protocol.  For  example,  escape
-     sequences are used to control the display.
-
-     ppmtoicr will output the appropriate sequences to  create  a
-     window  of  the  dimensions  of  the  input pixmap, create a
-     colormap of up to 256 colors on the display, then  load  the
-     picture data into the window.
-
-     Note that there is no icrtoppm tool - this transformation is
-     one way.
-
-2 Options
-     -windownamename
-                   Output will be displayed in name  (Default  is
-                   to use ppmfile or "untitled" if standard input
-                   is read.)
-
-     -expandexpand Output will be expanded on display  by  factor
-                   expand  (For  example, a value of 2 will cause
-                   four pixels to be displayed  for  every  input
-                   pixel.)
-
-     -displaydisplay
-                   Output will be displayed  on  screen  numbered
-                   display
-
-     -rle          Use run-length  encoded  format  for  display.
-                   (This  will  nearly always result in a quicker
-                   display, but may skew the colormap.)
-
-2 Examples
-     To display a ppm file using the protocol:
-         ppmtoicr ppmfile
-     This will create a window named ppmfile on the display  with
-     the  correct  dimensions  for ppmfile, create and download a
-     colormap of up to 256 colors, and download the picture  into
-     the window. The same effect may be achieved by the following
-     sequence:
-         ppmtoicr ppmfile > filename
-         cat filename
-     To display a GIF file using the protocol in a window  titled
-     after  the  input file, zoom the displayed image by a factor
-     of 2, and run-length encode the data:
-         giftopnm giffile | ppmtoicr -w giffile -r -e 2
-
-2 Bugs
-     The protocol uses frequent fflush calls to speed up display.
-     If  the output is saved to a file for later display via cat,
-     drawing will be much slower. In either case, increasing  the
-     Blocksize  limit  on  the display will speed up transmission
-     substantially.
-
-2 See_Also
-     ppm
-
-     NCSA Telnet for the Macintosh,  University  of  Illinois  at
-     Urbana-Champaign (1989)
-
-2 Author
-     Copyright     (C)      1990      by      Kanthan      Pillay
-     (svpillay@Princeton.EDU), Princeton University Computing and
-     Information Technology.
-
-1 ppmtoilbm
-     ppmtoilbm - convert a portable pixmap into an ILBM file
-
-2 Synopsis
-     ppmtoilbm [-maxplanes|-mp  N]  [-fixplanes|-fp  N]  [-ham6|-
-     ham8]   [-dcbits|-dcplanesrg   [-normal|-hamif|-hamforce   -
-     dcif|-dcforce|-cmaponly] [-ecs|-aga] [-mapppmfile] [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces an ILBM file  as
-     output.  Supported ILBM types are:
-
-     Normal ILBMs with 1-16 planes.
-
-     Amiga Hold-and-modify (HAM) with 3-16 planes.
-
-     24 bit.
-
-     Color map (BMHD + CMAP chunk only, nPlanes = 0).
-
-     Unofficial direct color.
-          1-16 planes for each color component.
-
-     Chunks written:
-          BMHD, CMAP, CAMG (only for HAM), BODY (not for colormap
-          files) unofficial DCOL chunk for direct color ILBM
-
-2 Options
-     Options marked with (*) can be prefixed with  a  "no",  e.g.
-     "-nohamif". All options can be abbreviated to their shortest
-     unique prefix.
-
-     -maxplanes | -mp n
-          (default 5, minimum 1, maximum 16)  Maximum  planes  to
-          write  in  a  normal  ILBM.  If the pixmap does not fit
-          into <n> planes, ppmtoilbm writes a HAM file (if -hamif
-          is  used),  a 24bit file (if -24if is used) or a direct
-          color file (if -dcif is used) or aborts with an error.
-
-     -fixplanes | -fp n
-          (min 1, max 16) If a normal ILBM is  written,  it  will
-          have exactly <n> planes.
-
-     -hambits | -hamplanes n
-          (default 6, min 3, max 16) Select number of planes  for
-          HAM picture.  The current Amiga hardware supports 6 and
-          8 planes, so for now you should only use this values.
-
-     -normal (default)
-          Turns  off   -hamif/-24if/-dcif,   -hamforce/-24force/-
-          dcforce and -cmaponly.
-
-     -hamif (*)
-
-     -24if (*)
-
-     -dcif (*)
-          Write a HAM/24bit/direct color file if the pixmap  does
-          not fit into <maxplanes> planes.
-
-     -hamforce (*)
-
-     -24force (*)
-
-     -dcforce (*)
-          Write a HAM/24bit/direct color file.
-
-     -dcbits | -dcplanes r g b
-          (default 5, min 1, max 16).  Select number of bits  for
-          red, green & blue in a direct color ILBM.
-
-     -ecs (default)
-          Shortcut for: -hamplanes 6 -maxplanes 5
-
-     -aga
-
-     Shortcut for: -hamplanes 8 -maxplanes 8
-
-     -ham6
-
-     Shortcut for: -hamplanes 6 -hamforce
-
-     -ham8
-          Shortcut for: -hamplanes 8 -hamforce
-
-     -map ppmfile
-          Write a normal ILBM using the colors  in  <ppmfile>  as
-          the  colormap.  The  colormap  file also determines the
-          number of planes, a -maxplanes or -fixplanes option  is
-          ignored.
-
-     -cmaponly
-          Write a colormap file: only BMHD and  CMAP  chunks,  no
-          BODY chunk, nPlanes = 0.
-
-2 Bugs
-     Needs a real colormap selection algorithm for HAM  pictures,
-     instead of using a grayscale colormap.
-
-2 References
-     Amiga ROM Kernel Reference Manual - Devices (3rd Ed.)
-     Addison Wesley, ISBN 0-201-56775-X
-
-2 See_Also
-     ppm(5), ilbmtoppm(1)
-
-2 Authors
-     Copyright (C) 1989 by Jef Poskanzer.
-     Modified August 1993 by Ingo Wilken
-    (Ingo.Wilken@informatik.uni-oldenburg.de)
-
-1 ppmtomitsu
-     ppmtomitsu - convert a portable pixmap to a Mitsubishi S340-10 file
-
-2 Synopsis
-     ppmtomitsu [-sharpness val] [-enlarge val] [-media string]
-                [-copy val] [-dpi300] [-tiny] [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input and  converts  it  into  a  format
-     suitable  to  be printed by a Mitsubishi S340-10  printer,  or  any
-     other Mitsubishi color sublimation printer.
-
-     The  Mitsubishi  S340-10  Color  Sublimation printer supports 24bit
-     color.  Images of the available sizes take so long to transfer that
-     there is a  fast  method,  employing a lookuptable, that ppmtomitsu
-     will use if there  is  a  maximum  of  256  colors  in  the pixmap.
-     ppmtomitsu will try to position  your  image  to  the center of the
-     paper, and will rotate your image  for  you if xsize is larger than
-     ysize.  If your image is larger  than  the media allows, ppmtomitsu
-     will quit with an error message.  (We  decided  that the media were
-     too expensive to have careless users produce misprints.) Once  data
-     transmission  has started, the job can't be stopped in a  sane  way
-     without resetting the  printer.    The  printer understands putting
-     together images in the printers memory;  ppmtomitsu doesn't utilize
-     this as pnmcat etc provide  the same functionality and let you view
-     the  result  on-screen, too.  The  S340-10  is  the  lowest  common
-     denominator  printer;  for higher resolution printers  there's  the
-     dpi300 option.  The other printers also support  higher  values for
-     enlarge eg., but I don't think that's essential enough to warrant a
-     change in the program.
-
-     -sharpness 1-4
-          'sharpness' designation.    Default  is  to  use  the  current
-          sharpness.
-
-     -enlarge 1-3
-          Enlarge by a factor; Default is 1 (no enlarge)
-
-     -media A, A4, AS, A4S
-          Designate the media  you're  using.    Default is 1184 x 1350,
-          which will fit on any media.  A  is  1216 x 1350, A4 is 1184 x
-          1452, AS is 1216 x 1650 and A4S is 1184 x 1754.    A  warning:
-          If you specify a  different  media  than the printer currently
-          has, the printer will wait until  you put in the correct media
-          or switch it off.
-
-     -copy 1-9
-          The number of copies to produce. Default is 1.
-
-     -dpi300
-          Double the number of allowed pixels for  a S3600-30 Printer in
-          S340-10 compatibility mode.  (The S3600-30 has 300 dpi).
-
-     -tiny
-          Memory-safing, but always slow.  The printer will get the data
-          line-by-line in 24bit.  It's probably a good idea to  use this
-          if your machine starts paging a lot without this option.
-
-2 References
-     Mitsubishi Sublimation Full Color Printer S340-10 Specifications of
-     Parallel Interface LSP-F0232F
-
-2 See_Also
-     ppmquant(1), pnmscale(1), ppm(5)
-
-2 Bugs
-     We didn't find any - yet.  (Besides, they're called features anyway
-     :-) If you should find one, my email-adress is below.
-
-2 Author
-     Copyright (C) 1992, 93  by  S.Petra  Zeidler,  MPIfR Bonn, Germany.
-     (spz@specklec.mpifr-bonn.mpg.de)
-
-1 ppmtopcx
-     ppmtopcx - convert a portable pixmap into a PCX file
-
-2 Synopsis
-     ppmtopcx [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces a  PCX  file  as
-     output.
-
-2 See_Also
-     pcxtoppm, ppm
-
-2 Author
-     Copyright (C) 1990 by Michael Davidson.
-
-1 ppmtopgm
-     ppmtopgm - convert a portable pixmap into a portable graymap
-
-2 Synopsis
-     ppmtopgm [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces a portable gray-
-     map  as  output.   The quantization formula used is .299 r +
-     .587 g + .114 b.
-
-     Note that although there is a pgmtoppm program,  it  is  not
-     necessary  for  simple  conversions from pgm to ppm, because
-     any ppm program can read pgm (and pbm ) files automagically.
-     pgmtoppm  is for colorizing a pgm file.  Also, see ppmtorgb3
-     for a different way of converting color to gray.
-
-2 QUOTE
-     Cold-hearted orb that rules the night
-     Removes the colors from our sight
-     Red is gray, and yellow white
-     But we decide which is right
-     And which is a quantization error.
-
-2 See_Also
-     pgmtoppm, ppmtorgb3, rgb3toppm, ppm, pgm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 ppmtopi1
-     ppmtopi1 - convert a portable pixmap  into  an  Atari  Degas
-     .pi1 file
-
-2 Synopsis
-     ppmtopi1 [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces an  Atari  Degas
-     .pi1 file as output.
-
-2 See_Also
-     pi1toppm, ppm, pbmtopi3, pi3topbm
-
-2 Author
-     Copyright (C) 1991 by Steve Belczyk (seb3@gte.com)  and  Jef
-     Poskanzer.
-
-1 ppmtopict
-     ppmtopict - convert a portable pixmap into a Macintosh  PICT
-     file
-
-2 Synopsis
-     ppmtopict [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces a Macintosh PICT
-     file as output.
-
-     The generated file is only the data fork of a picture.   You
-     will  need  a program such as mcvert to generate a Macbinary
-     or a BinHex file that contains the necessary information  to
-     identify the file as a PICT file to MacOS.
-
-     Even though PICT supports 2 and 4 bits per pixel,  ppmtopict
-     always generates an 8 bits per pixel file.
-
-2 Bugs
-     The picture size field is only correct if the output is to a
-     file  since  writing  into this field requires seeking back-
-     wards on a file.  However the PICT  documentation  seems  to
-     suggest  that  this field is not critical anyway since it is
-     only the lower 16 bits of the picture size.
-
-2 See_Also
-     picttoppm, ppm, mcvert
-
-2 Author
-     Copyright (C) 1990 by Ken Yap <ken@cs.rocester.edu>.
-
-1 ppmtopj
-     ppmtopj - convert a portable pixmap to an HP PaintJet file
-
-2 Synopsis
-     ppmtopj  [-gamma  val]  [-xpos  val]  [-ypos   val]   [-back
-     dark|lite]         [-rle]         [-center]         [-render
-     none|snap|bw|dither|diffuse|monodither|monodiffuse|clusterdither|monoclusterdither]
-     [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input and converts it into a for-
-     mat suitable to be printed by an HP PaintJet printer.
-
-     For best results, the input file should be  in  8-color  RGB
-     form;  i.e. it should have only the 8 binary combinations of
-     full-on and full-off primaries.  You could get this by send-
-     ing  the  input  file  through ppmquant -map with a map file
-     such as:
-         P3
-         8 1
-         255
-         0 0 0      255 0 0    0 255 0    0 0 255
-         255 255 0  255 0 255  0 255 255  255 255 255
-     Or else you could use use ppmdither -red 2 -green 2 -blue
-
-2 Options
-     -rle          Run length encode the image.  (This can result
-                   in larger images)
-
-     -back         Enhance the foreground by  indicating  if  the
-                   background  is  light  or dark compated to the
-                   foreground.
-
-     -render alg   Use an internal rendering  algorithm  (default
-                   dither).
-
-     -gamma int    Gamma correct  the  image  using  the  integet
-                   parameter as a gamma (default 0).
-
-     -center       Center the image to an 8.5 by 11 page
-
-     -xpos pos     Move by pos pixels in the x direction.
-
-     -ypos pos     Move by pos pixels in the y direction.
-
-2 References
-     HP PaintJet XL Color Graphics Printer User's Guide
-
-2 See_Also
-     pnmdepth, ppmquant, ppmdither, ppm
-
-2 Bugs
-     Most of the options have not  been  tested  because  of  the
-     price of the paper.
-
-2 Author
-     Copyright (C) 1991 by Christos Zoulas.
-
-1 ppmtopuzz
-     ppmtopuzz - convert a portable pixmap into an  X11  "puzzle"
-     file
-
-2 Synopsis
-     ppmtopuzz [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces an X11  "puzzle"
-     file  as output.  A "puzzle" file is for use with the puzzle
-     program included with the  X11  distribution  -  puzzle's  -
-     picture flag lets you specify an image file.
-
-2 See_Also
-     ppm, puzzle
-
-2 Author
-     Copyright (C) 1991 by Jef Poskanzer.
-
-1 ppmtorgb3
-     ppmtorgb3 - separate a portable pixmap into  three  portable
-     graymaps
-
-2 Synopsis
-     ppmtorgb3 [ppmfile]
-
-2 Description
-     Reads a portable pixmap as  input.   Writes  three  portable
-     graymaps as output, one each for red, green, and blue.
-
-     The output filenames are constructed  by  taking  the  input
-     filename, stripping off any extension, and appending ".red",
-     ".grn", and ".blu".  For example, separating lenna.ppm would
-     result in lenna.red, lenna.grn, and lenna.blu.  If the input
-     comes from stdin, the names are noname.red, noname.grn,  and
-     noname.blu.
-
-2 See_Also
-     rgb3toppm, ppmtopgm, pgmtoppm, ppm, pgm
-
-2 Author
-     Copyright (C) 1991 by Jef Poskanzer.
-
-1 ppmtosixel
-     ppmtosixel - convert a portable pixmap into DEC sixel format
-
-2 Synopsis
-     ppmtosixel [-raw] [-margin] [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces  sixel  commands
-     (SIX)  as  output.  The output is formatted for color print-
-     ing, e.g. for a DEC LJ250 color inkjet printer.
-
-     If RGB values from the PPM file do not have maxval=100,  the
-     RGB  values  are  rescaled.   A printer control header and a
-     color assignment table begin the SIX file.   Image  data  is
-     written  in  a compressed format by default.  A printer con-
-     trol footer ends the image file.
-
-2 Options
-     -raw If specified, each pixel will be  explicitly  described
-          in  the  image  file.  If -raw is not specified, output
-          will default to compressed format  in  which  identical
-          adjacent  pixels  are  replaced  by "repeat pixel" com-
-          mands.  A raw file  is  often  an  order  of  magnitude
-          larger than a compressed file and prints much slower.
-
-     -margin
-          If -margin is not specified, the image will be start at
-          the  left  margin  (of the window, paper, or whatever).
-          If -margin is specified, a 1.5 inch  left  margin  will
-          offset the image.
-
-2 Printing
-     Generally, sixel files must reach  the  printer  unfiltered.
-     Use the lpr -x option or cat filename > /dev/tty0?.
-
-2 Bugs
-     Upon rescaling, truncation of the least significant bits  of
-     RGB values may result in poor color conversion.  If the ori-
-     ginal PPM  maxval  was  greater  than  100,  rescaling  also
-     reduces  the  image depth.  While the actual RGB values from
-     the ppm file are more or less retained, the color palette of
-     the  LJ250  may  not  match the colors on your screen.  This
-     seems to be a printer limitation.
-
-2 See_Also
-     ppm
-
-2 Author
-     Copyright (C) 1991 by Rick Vinci.
-
-1 ppmtotga
-     ppmtotga - convert portable pixmap into a  TrueVision  Targa
-     file
-
-2 Synopsis
-     ppmtotga [-mono|-cmap|-rgb] [-norle] [ppmfile]
-
-2 Description
-     Reads a portable pixmap as  input.   Produces  a  TrueVision
-     Targa file as output.
-
-2 Options
-     -mono
-          Forces Targa file to  be  of  type  8  bit  monochrome.
-          Input must be a portable bitmap or a portable graymap.
-
-     -cmap
-          Forces Targa file to be of  type  24  bit  colormapped.
-          Input  must be a portable bitmap, a portable graymap or
-          a portable pixmap containing no more than 256  distinct
-          colors.
-
-     -rgb Forces Targa file to be of type 24 bit unmapped color.
-
-     -norle
-          Disables run-length encoding, in case you have a  Targa
-          reader which can't read run-length encoded files.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.   If  no  file  type  is specified the most highly con-
-     stained compatible type is used, where  monochrome  is  more
-     constained than colormapped which is in turn more constained
-     than unmapped.
-
-2 Bugs
-     Does not support all  possible  Targa  file  types.   Should
-     really be in PNM, not PPM.
-
-2 See_Also
-     tgatoppm, ppm
-
-2 Author
-     Copyright (C) 1989, 1991 by Mark Shand and Jef Poskanzer.
-
-1 ppmtouil
-     ppmtouil - convert a portable pixmap into a Motif  UIL  icon
-     file
-
-2 Synopsis
-     ppmtouil [-name uilname] [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces a Motif UIL icon
-     file as output.
-
-     If the program was compiled with an rgb database  specified,
-     and  a RGB value from the ppm input matches a RGB value from
-     the database, then the corresponding color name mnemonic  is
-     printed  in the UIL's colormap.  If no rgb database was com-
-     piled in, or if the RGB values don't match, then  the  color
-     will  be  printed  with  the  #RGB,  #RRGGBB, #RRRGGGBBB, or
-     #RRRRGGGGBBBB hexadecimal format.
-
-2 Options
-     -name
-          Allows you  to  specify  the  prefix  string  which  is
-          printed in the resulting UIL output.  If not specified,
-          will default to the filename (without extension) of the
-          ppmfile  argument.   If  -name  is not specified and no
-          ppmfile is specified (i.e.  piped  input),  the  prefix
-          string will default to the string "noname".
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 See_Also
-     ppm
-
-2 Author
-     Converted by Jef Poskanzer from ppmtoxpm.c, which  is  Copy-
-     right (C) 1990 by Mark W. Snitily
-
-1 ppmtoxpm
-     ppmtoxpm - convert a portable pixmap into an X11 pixmap
-
-2 Synopsis
-     ppmtoxpm [-name xpmname] [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces X11 pixmap (XPM)
-     as output.
-
-     If the program was compiled with an rgb database  specified,
-     and  a RGB value from the ppm input matches a RGB value from
-     the database, then the corresponding color name mnemonic  is
-     printed  in the XPM's colormap.  If no rgb database was com-
-     piled in, or if the RGB values don't match, then  the  color
-     will  be  printed  with  the  #RGB,  #RRGGBB, #RRRGGGBBB, or
-     #RRRRGGGGBBBB hexadecimal format.
-
-2 Options
-     -name
-          Allows you  to  specify  the  prefix  string  which  is
-          printed in the resulting XPM output.  If not specified,
-          will default to the filename (without extension) of the
-          ppmfile  argument.   If  -name  is not specified and no
-          ppmfile is specified (i.e.  piped  input),  the  prefix
-          string will default to the string "noname".
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 Example
-     To     convert     the     file     "dot"     (found      in
-     /usr/include/X11/bitmaps), from xbm to xpm one could specify
-
-          xbmtopbm dot | ppmtoxpm -name dot
-
-2 Bugs
-     An option to match the closest  (rather  than  exact)  color
-     name  mnemonic  from  the  rgb  text  would  be  a desirable
-     enhancement.
-
-     Truncation of the least significant bits of a RGB value  may
-     result  in  nonexact  matches  when  performing  color  name
-     mnemonic lookups.
-
-2 See_Also
-     xpmtoppm, ppm
-
-2 Author
-     Copyright (C) 1990 by Mark W. Snitily.
-
-1 ppmtoyuv
-     ppmtoyuv - convert a portable pixmap into an Abekas YUV file
-
-2 Synopsis
-     ppmtoyuv [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces  an  Abekas  YUV
-     file as output.
-
-2 See_Also
-     yuvtoppm, ppm
-
-2 Author
-     Marc Boucher <marc@PostImage.COM>, based on Example  Conver-
-     sion  Program,  A60/A64 Digital Video Interface Manual, page
-     69.
-
-     Copyright (C) 1991 by DHD PostImage Inc.
-
-     Copyright (C) 1987 by Abekas Video Systems Inc.
-
-1 ppmtoyuvsplit
-     ppmtoyuvsplit - convert a portable pixmap into 3  subsampled
-     raw YUV files
-
-2 Synopsis
-     ppmtoyuvsplit basename [ppmfile]
-
-2 Description
-     Reads a portable pixmap as  input.   Produces  3  raw  files
-     basename.Y,  basename.U  and  basename.V  as  output.  These
-     files are the subsampled raw YUV representation of the input
-     pixmap,  as required by the Stanford MPEG codec. The subsam-
-     pling is done by arithmetic mean of  4  pixels  colors  into
-     one.  The  YUV  values  are scaled according to CCIR.601, as
-     assumed by MPEG.
-
-2 See_Also
-     mpeg, ppm
-
-2 Author
-     Copyright (C) 1993 by  Andre  Beck.  (Andreeck@IRS.Inf.TU-
-     Dresden.de)
-
-     Based on ppmtoyuv.c
-
-1 qrttoppm
-     qrttoppm - convert output from the QRT  ray  tracer  into  a
-     portable pixmap
-
-2 Synopsis
-     qrttoppm [qrtfile]
-
-2 Description
-     Reads a QRT file as input.  Produces a  portable  pixmap  as
-     output.
-
-2 See_Also
-     ppm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 rawtoppm
-     rawtoppm - convert raw RGB bytes into a portable pixmap
-
-2 Synopsis
-     rawtoppm [-headerskip N]  [-rowskip  N]  [-rgb|-rbg|-grb  |-
-     gbr|-brg|-bgr ] [-interpixel|-interrow] width height [image-
-     data]
-
-2 Description
-     Reads raw RGB bytes as input.  Produces a portable pixmap as
-     output.   The  input  file  is  just RGB bytes.  You have to
-     specify the width and height on the command line, since  the
-     program  obviously can't get them from the file.  The maxval
-     is assumed to be 255.  If  the  resulting  image  is  upside
-     down, run it through pnmflip -tb .
-
-2 Options
-     -headerskip
-          If the file has a header, you can use this flag to skip
-          over it.
-
-     -rowskip
-          If there is padding at the ends of the  rows,  you  can
-          skip it with this flag.
-
-     -rgb -rbg -grb -gbr -brg -bgr
-          These flags let you  specify  alternate  color  orders.
-          The default is -rgb.
-
-     -interpixel -interrow
-          These flags let you specify how the colors  are  inter-
-          leaved.   The  default  is  -interpixel, meaning inter-
-          leaved by pixel.  A byte of red, a byte of green, and a
-          byte  of  blue,  or whatever color order you specified.
-          -interrow means interleaved by row - a row  of  red,  a
-          row  of  green,  a  row  of blue, assuming standard rgb
-          color order.  An -interplane flag  - all the  red  pix-
-          els,  then  all the green, then all the blue - would be
-          an obvious extension,  but  is  not  implemented.   You
-          could  get  the  same effect by splitting the file into
-          three parts (perhaps using dd), turning each part  into
-          a  PGM file with rawtopgm, and then combining them with
-          rgb3toppm.
-
-2 See_Also
-     ppm, rawtopgm, rgb3toppm, pnmflip
-
-2 Author
-     Copyright (C) 1991 by Jef Poskanzer.
-
-1 rgb3toppm
-     rgb3toppm - combine three portable graymaps into  one  port-
-     able pixmap
-
-2 Synopsis
-     rgb3toppm redpgmfile greenpgmfile bluepgmfile
-
-2 Description
-     Reads three portable graymaps as input.  Combines  them  and
-     produces one portable pixmap as output.
-
-2 See_Also
-     ppmtorgb3, pgmtoppm, ppmtopgm, ppm, pgm
-
-2 Author
-     Copyright (C) 1991 by Jef Poskanzer.
-
-1 sldtoppm
-     sldtoppm - convert an AutoCAD slide  file  into  a  portable
-     pixmap
-
-2 Synopsis
-     sldtoppm [-adjust] [-dir] [-height|-ysize s] [-info] [-
-              lib|-Lib name] [-scale s] [-verbose] [-width|-xsize
-              s] [slidefile]
-
-2 Description
-     Reads an AutoCAD(Reg.) slide file  and  outputs  a  portable
-     pixmap.   If  no  slidefile is specified, input is read from
-     standard input.  The ppmdraw library is used to convert  the
-     vector  and  polygon information in the slide file to a pix-
-     map; see the file ppmdraw.h for details on this package.
-
-2 Options
-     -adjust
-          If the display on which the slide file was created  had
-          non-square  pixels,  when  the  slide is processed with
-          sldtoppm and the -adjust option  is  not  present,  the
-          following warning will appear:
-            Warning - pixels on source screen were non-square.
-            Specifying -adjust will correct image width  to  com-
-            pensate.
-          Specifying the -adjust option causes sldtoppm to  scale
-          the  width of the image so that pixels in the resulting
-          portable pixmap are square (and hence circles appear as
-          true  circles, not ellipses).  The scaling is performed
-          in  the  vector  domain,  before  scan  converting  the
-          objects.   The  results  are,  therefore,  superior  in
-          appearance to what you'd obtain were you to perform the
-          equivalent  scaling  with pnmscale after the bitmap had
-          been created.
-
-     -dir The input is assumed to be  an  AutoCAD  slide  library
-          file.  A directory listing each slide in the library is
-          printed on standard error.
-
-     -height size
-          Scales the image in the vector domain  so  it  is  size
-          pixels  in  height.   If  no -width or -xsize option is
-          specified, the width will be adjusted to  preserve  the
-          pixel aspect ratio.
-
-     -info
-          Dump the slide file header on standard error,  display-
-          ing  the  original  screen  size and aspect ratio among
-          other information.
-
-     -lib name
-          Extracts the slide with the given name from  the  slide
-          library  given  as  input.   The specified name is con-
-          verted to upper case.
-
-     -Lib name
-          Extracts the slide with the given name from  the  slide
-          library  given  as  input.  The name is used exactly as
-          specified; it is not converted to upper case.
-
-     -scale s
-          Scales the image by factor s, which may be any floating
-          point  value  greater than zero.  Scaling is done after
-          aspect ratio adjustment, if any.  Since scaling is per-
-          formed  in the vector domain, before rasterisation, the
-          results look much better than  running  the  output  of
-          sldtoppm through pnmscale.
-
-     -verbose
-          Dumps the slide file header and lists every vector  and
-          polygon in the file on standard error.
-
-     -width size
-          Scales the image in the vector domain  so  it  is  size
-          pixels  wide.  If no -height or -ysize option is speci-
-          fied, the height will be adjusted to preserve the pixel
-          aspect ratio.
-
-     -xsize size
-          Scales the image in the vector domain  so  it  is  size
-          pixels  wide.  If no -height or -ysize option is speci-
-          fied, the height will be adjusted to preserve the pixel
-          aspect ratio.
-
-     -ysize size
-          Scales the image in the vector domain  so  it  is  size
-          pixels  in  height.   If  no -width or -xsize option is
-          specified, the width will be adjusted to  preserve  the
-          pixel aspect ratio.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 Bugs
-     Only Level 2 slides are converted.  Level 1 format has  been
-     obsolete  since the advent of AutoCAD Release 9 in 1987, and
-     was not portable across machine architectures.
-
-     Slide library items with names containing  8  bit  (such  as
-     ISO)  or  16  bit (Kanji, for example) characters may not be
-     found when chosen with the -lib option unless  sldtoppm  has
-     been built with character set conversion functions appropri-
-     ate to the locale.  You  can  always  retrieve  slides  from
-     libraries  regardless of the character set by using the -Lib
-     option and specifying the precise name  of  library  member.
-     Use  the  -dir  option  to  list  the slides in a library if
-     you're unsure of the exact name.
-
-2 See_Also
-     AutoCAD Reference Manual: Slide  File  Format,  pnmscale,
-     ppm
-
-2 Author
-          John Walker
-          Autodesk SA
-          Avenue des Champs-Montants 14b
-          CH-2074 MARIN
-          Suisse/Schweiz/Svizzera/Svizra/Switzerland
-          Usenet:  kelvin@Autodesk.com
-          Fax:     038/33 88 15
-          Voice:   038/33 76 33
-
-     Permission  to  use,  copy,  modify,  and  distribute   this
-     software  and  its documentation for any purpose and without
-     fee is hereby granted, without any  conditions  or  restric-
-     tions.   This software is provided ``as is'' without express
-     or implied warranty.
-
-     AutoCAD and Autodesk are registered trademarks of  Autodesk,
-     Inc.
-
-1 spctoppm
-     spctoppm - convert an Atari compressed Spectrum file into  a
-     portable pixmap
-
-2 Synopsis
-     spctoppm [spcfile]
-
-2 Description
-     Reads an Atari compressed Spectrum file as input.   Produces
-     a portable pixmap as output.
-
-2 See_Also
-     sputoppm, ppm
-
-2 Author
-     Copyright (C) 1991 by Steve Belczyk (seb3@gte.com)  and  Jef
-     Poskanzer.
-
-1 sputoppm
-     sputoppm - convert an Atari uncompressed Spectrum file  into
-     a portable pixmap
-
-2 Synopsis
-     sputoppm [spufile]
-
-2 Description
-     Reads an Atari uncompressed Spectrum file  as  input.   Pro-
-     duces a portable pixmap as output.
-
-2 See_Also
-     spctoppm, ppm
-
-2 Author
-     Copyright (C) 1991 by Steve Belczyk (seb3@gte.com)  and  Jef
-     Poskanzer.
-
-1 tgatoppm
-     tgatoppm - convert TrueVision Targa  file  into  a  portable
-     pixmap
-
-2 Synopsis
-     tgatoppm [-debug] [tgafile]
-
-2 Description
-     Reads a TrueVision Targa file as input.  Produces a portable
-     pixmap as output.
-
-2 Options
-     -debug
-          Causes the header information to be dumped to stderr.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.  Should really be in PNM, not PPM.
-
-2 See_Also
-     ppmtotga, ppm
-
-2 Author
-     Partially based on tga2rast, version 1.0,  by  Ian  J.  Mac-
-     Phedran.
-
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 ximtoppm
-     ximtoppm - convert an Xim file into a portable pixmap
-
-2 Synopsis
-     ximtoppm [ximfile]
-
-2 Description
-     Reads an Xim file as input.  Produces a portable  pixmap  as
-     output.   The Xim toolkit is included in the contrib tree of
-     the X.V11R4 release.
-
-2 See_Also
-     ppm
-
-2 Author
-     Copyright (C) 1991 by Jef Poskanzer.
-
-1 xpmtoppm
-     xpmtoppm - convert an X11 pixmap into a portable pixmap
-
-2 Synopsis
-     xpmtoppm [xpmfile]
-
-2 Description
-     Reads an X11 pixmap (XPM) as  input.   Produces  a  portable
-     pixmap as output.
-
-2 See_Also
-     ppmtoxpm, ppm
-
-2 Author
-     Copyright (C) 1991 by Jef Poskanzer.
-
-1 yuvtoppm
-     yuvtoppm - convert Abekas YUV bytes into a portable pixmap
-
-2 Synopsis
-     yuvtoppm width height [imagedata]
-
-2 Description
-     Reads raw Abekas YUV bytes as input.   Produces  a  portable
-     pixmap  as  output.   The input file is just YUV bytes.  You
-     have to specify the width and height on  the  command  line,
-     since  the  program  obviously can't get them from the file.
-     The maxval is assumed to be 255.
-
-2 See_Also
-     ppmtoyuv, ppm
-
-2 Author
-     Marc Boucher <marc@PostImage.COM>, based on Example  Conver-
-     sion  Program,  A60/A64 Digital Video Interface Manual, page
-     69.
-
-     Copyright (C) 1991 by DHD PostImage Inc.
-
-     Copyright (C) 1987 by Abekas Video Systems Inc.
-
-1 yuvsplittoppm
-     yuvplittoppm - convert a Y- an U- and a V-file into a  port-
-     able pixmap.
-
-2 Synopsis
-     yuvsplittoppm basename width height [-ccir601]
-
-2 Description
-     Reads three files, containing the YUV components, as  input.
-     These  files  are  basename .Y,  basename.U and basename.V .
-     Produces a portable pixmap on stdout.
-
-     Since the YUV files are raw files, the dimensions width  and
-     height must be specified on the command line.
-
-2 Options
-     -ccir601
-          Assumes that the  YUV  triplets  are  scaled  into  the
-          smaller  range  of  the CCIR 601 (MPEG) standard. Else,
-          the JFIF (JPEG) standard is assumed.
-
-2 See_Also
-     ppmtoyuvsplit, yuvtoppm, ppm
-
-2 Author
-     Marcel    Wijkstra    <wijkstra@fwi.uva.nl>,    based     on
-     ppmtoyuvsplit.
-
-1 ppmforge
-     ppmforge - fractal forgeries of clouds, planets, and  starry
-     skies
-
-2 Synopsis
-     ppmforge [-clouds] [-night] [-dimension dimen] [-hour hour]
-              [-inclination|-tilt angle] [-mesh size] [-power
-              factor] [-glaciers level] [-ice level] [-saturation
-              sat] [-seed seed] [-stars fraction] [-xsize|-width
-              width] [-ysize|-height height]
-
-2 Description
-     ppmforge generates three  kinds  of  ``random  fractal  for-
-     geries,'' the term coined by Richard F. Voss of the IBM Tho-
-     mas J. Watson Research Center for seemingly  realistic  pic-
-     tures  of natural objects generated by simple algorithms em-
-     bodying randomness and fractal self-similarity.   The  tech-
-     niques  used  by  ppmforge  are  essentially  those given by
-     Voss[1], particularly the technique  of  spectral  synthesis
-     explained in more detail by Dietmar Saupe[2].
-
-     The program generates two varieties of pictures: planets and
-     clouds, which are just different renderings of data generat-
-     ed in an identical manner, illustrating  the  unity  of  the
-     fractal  structure of these very different objects.  A third
-     type of picture, a starry sky, is synthesised directly  from
-     pseudorandom numbers.
-
-     The generation of planets or clouds begins with the prepara-
-     tion  of  an  array  of random data in the frequency domain.
-     The size of this array, the ``mesh size,'' can be  set  with
-     the -mesh option; the larger the mesh the more realistic the
-     pictures but the calculation time and memory requirement in-
-     creases  as the square of the mesh size.  The fractal dimen-
-     sion, which you can  specify  with  the  -dimension  option,
-     determines the roughness of the terrain on the planet or the
-     scale of detail in the clouds.  As the fractal dimension  is
-     increased, more high frequency components are added into the
-     random mesh.
-
-     Once the mesh  is  generated,  an  inverse  two  dimensional
-     Fourier  transform  is performed upon it.  This converts the
-     original random frequency domain data  into  spatial  ampli-
-     tudes.   We  scale  the real components that result from the
-     Fourier transform into numbers from 0 to 1  associated  with
-     each  point on the mesh.  You can further modify this number
-     by applying a ``power law scale'' to it with the -power  op-
-     tion.    Unity  scale leaves the numbers unmodified; a power
-     scale of 0.5 takes the square root of  the  numbers  in  the
-     mesh,  while  a power scale of 3 replaces the numbers in the
-     mesh with their cubes.  Power law scaling is best envisioned
-     by  thinking  of  the  data as representing the elevation of
-     terrain; powers less than 1 yield landscapes  with  vertical
-     scarps  that  look  like  glacially-carved  valleys;  powers
-     greater than one make  fairy-castle  spires  (which  require
-     large mesh sizes and high resolution for best results).
-
-     After these calculations, we have a array of  the  specified
-     size containing numbers that range from 0 to 1.  The pixmaps
-     are generated as follows:
-
-     Clouds    A colour map is created that ranges from pure blue
-               to white by increasing admixture (desaturation) of
-               blue  with  white.   Numbers  less  than  0.5  are
-               coloured  blue,  numbers  between  0.5 and 1.0 are
-               coloured with corresponding levels of white,  with
-               1.0 being pure white.
-
-     Planet    The mesh is projected onto a sphere.  Values  less
-               than  0.5  are treated as water and values between
-               0.5 and 1.0 as land.  The water areas are coloured
-               based  upon the water depth, and land based on its
-               elevation.  The random  depth  data  are  used  to
-               create  clouds over the oceans.  An atmosphere ap-
-               proximately like the  Earth's  is  simulated;  its
-               light  absorption  is  calculated to create a blue
-               cast around the limb of the  planet.   A  function
-               that  rises from 0 to 1 based on latitude is modu-
-               lated by the local elevation to generate polar ice
-               caps--high   altitude   terrain  carries  glaciers
-               farther from the pole.  Based on the  position  of
-               the  star  with  respect  to the observer, the ap-
-               parent colour of each pixel of the planet is  cal-
-               culated by ray-tracing from the star to the planet
-               to the observer and applying a lighting model that
-               sums  ambient  light  and  diffuse reflection (for
-               most planets ambient light is zero, as their  pri-
-               mary  star  is  the  only source of illumination).
-               Additional random data are used to generate  stars
-               around the planet.
-
-     Night     A sequence of pseudorandom numbers is used to gen-
-               erate stars with a user specified density.
-
-     Cloud pictures always contain 256 or fewer colours  and  may
-     be  displayed  on most colour mapped devices without further
-     processing.  Planet pictures often contain tens of thousands
-     of  colours  which  must  be  compressed  with  ppmquant  or
-     ppmdither before encoding in a colour mapped format.  If the
-     display  resolution is high enough, ppmdither generally pro-
-     duces better looking  planets.   ppmquant  tends  to  create
-     discrete colour bands, particularly in the oceans, which are
-     unrealistic and distracting.  The number of colours in star-
-     ry  sky pictures generated with the -night option depends on
-     the value specified for -saturation.  Small values limit the
-     colour  temperature distribution of the stars and reduce the
-     number of colours in the image.  If the -saturation  is  set
-     to  0,  none of the stars will be coloured and the resulting
-     image will never contain more than 256 colours.   Night  sky
-     pictures  with  many  different star colours often look best
-     when colour compressed by pnmdepth rather than  ppmquant  or
-     ppmdither.   Try  newmaxval  settings  of 63, 31, or 15 with
-     pnmdepth to reduce the number of colours in the  picture  to
-     256 or fewer.
-
-2 Options
-     -clouds   Generate clouds.  A pixmap of  fractal  clouds  is
-               generated.   Selecting clouds sets the default for
-               fractal dimension to 2.15 and power  scale  factor
-               to 0.75.
-
-     -dimension dimen
-               Sets the fractal dimension to the specified dimen,
-               which  may  be  any floating point value between 0
-               and 3.   Higher  fractal  dimensions  create  more
-               ``chaotic''  images,  which require higher resolu-
-               tion output and a larger FFT  mesh  size  to  look
-               good.   If  no dimension is specified, 2.4 is used
-               when generating planets and 2.15 for clouds.
-
-     -glaciers level
-               The floating point level setting controls the  ex-
-               tent  to which terrain elevation causes ice to ap-
-               pear at lower latitudes.   The  default  value  of
-               0.75  makes the polar caps extend toward the equa-
-               tor across high terrain and forms glaciers in  the
-               highest  mountains,  as  on  Earth.  Higher values
-               make ice sheets that cover more and  more  of  the
-               land  surface,  simulating planets in the midst of
-               an ice age.   Lower  values  tend  to  be  boring,
-               resulting in unrealistic geometrically-precise ice
-               cap boundaries.
-
-     -hour hour
-               When generating a planet,  hour  is  used  as  the
-               ``hour  angle  at  the central meridian.''  If you
-               specify -hour 12, for example, the planet will  be
-               fully  illuminated,  corresponding to high noon at
-               the longitude at the centre of  the  screen.   You
-               can specify any floating point value between 0 and
-               24 for hour, but values which place  most  of  the
-               planet in darkness (0 to 4 and 20 to 24) result in
-               crescents which, while pretty, don't give you many
-               illuminated  pixels  for  the  amount of computing
-               that's required.  If no -hour option is specified,
-               a random hour angle is chosen, biased so that only
-               25% of the images generated will be crescents.
-
-     -ice level
-               Sets the extent of the polar ice caps to the given
-               floating  point  level.   The default level of 0.4
-               produces ice caps similar to those of  the  Earth.
-               Smaller  values  reduce  the  amount of ice, while
-               larger -ice settings  create  more  prominent  ice
-               caps.   Sufficiently  large values, such as 100 or
-               more, in conjunction with  small  settings  for  -
-               glaciers (try 0.1) create ``ice balls'' like Euro-
-               pa.
-
-     -inclination|-tilt angle
-               The inclination angle of the planet with regard to
-               its primary star is set to angle, which can be any
-               floating point value from -90 to 90.  The inclina-
-               tion angle can be thought of as specifying, in de-
-               grees, the ``season'' the planet is presently  ex-
-               periencing  or,  more  precisely,  the latitude at
-               which the star transits the zenith at local  noon.
-               If  0,  the  planet  is  at  equinox;  the star is
-               directly overhead at the equator.  Positive values
-               represent summer in the northern hemisphere, nega-
-               tive values summer  in  the  southern  hemisphere.
-               The  Earth's  inclination  angle,  for example, is
-               about  23.5  at  the  June  solstice,  0  at   the
-               equinoxes in March and September, and -23.5 at the
-               December solstice.  If  no  inclination  angle  is
-               specified,  a  random value between -21.6 and 21.6
-               degrees is chosen.
-
-     -mesh size
-               A mesh of size by size will be used for  the  fast
-               Fourier  transform  (FFT).   Note  that memory re-
-               quirements and computation speed increase  as  the
-               square  of  size; if you double the mesh size, the
-               program will use four times  the  memory  and  run
-               four  times as long.  The default mesh is 256x256,
-               which produces reasonably  good  looking  pictures
-               while  using half a megabyte for the 256x256 array
-               of single precision complex  numbers  required  by
-               the  FFT.  On machines with limited memory capaci-
-               ty, you may have to reduce the mesh size to  avoid
-               running out of RAM.  Increasing the mesh size pro-
-               duces better looking pictures; the difference  be-
-               comes particularly noticeable when generating high
-               resolution images with relatively high fractal di-
-               mensions (between 2.2 and 3).
-
-     -night    A starry sky is generated.  The stars are  created
-               by the same algorithm used for the stars that sur-
-               round planet pictures, but the output consists ex-
-               clusively of stars.
-
-     -power factor
-               Sets the ``power factor'' used to scale elevations
-               synthesised  from  the FFT to factor, which can be
-               any floating point number greater than  zero.   If
-               no factor is specified a default of 1.2 is used if
-               a planet is being generated, or 0.75 if clouds are
-               selected by the -clouds option.  The result of the
-               FFT image  synthesis  is  an  array  of  elevation
-               values  between 0 and 1.  A non-unity power factor
-               exponentiates each  of  these  elevations  to  the
-               specified power.  For example, a power factor of 2
-               squares each value, while a power  factor  of  0.5
-               replaces  each  with  its square root.  (Note that
-               exponentiating  values  between  0  and  1  yields
-               values that remain within that range.)  Power fac-
-               tors less than 1 emphasise  large-scale  elevation
-               changes at the expense of small variations.  Power
-               factors greater than 1 increase the  roughness  of
-               the terrain and, like high fractal dimensions, may
-               require a  larger  FFT  mesh  size  and/or  higher
-               screen resolution to look good.
-
-     -saturation sat
-               Controls the degree of colour  saturation  of  the
-               stars that surround planet pictures and fill star-
-               ry skies created with the -night option.  The  de-
-               fault  value  of  125 creates stars which resemble
-               the sky as seen by the human eye from Earth's sur-
-               face.   Stars are dim; only the brightest activate
-               the cones in the human retina, causing  colour  to
-               be  perceived.   Higher  values of sat approximate
-               the appearance of stars from  Earth  orbit,  where
-               better  dark  adaptation,  absence of skyglow, and
-               the concentration of light from a given star  onto
-               a smaller area of the retina thanks to the lack of
-               atmospheric turbulence enhances the perception  of
-               colour.   Values greater than 250 create ``science
-               fiction'' skies that, while pretty, don't occur in
-               this universe.
-
-               Thanks to the inverse  square  law  combined  with
-               Nature's  love of mediocrity, there are many, many
-               dim stars for every bright one.   This  population
-               relationship  is accurately reflected in the skies
-               created by ppmforge.  Dim,  low  mass  stars  live
-               much longer than bright massive stars, consequent-
-               ly there are many reddish stars for every blue gi-
-               ant.   This relationship is preserved by ppmforge.
-               You can reverse the proportion, simulating the sky
-               as  seen  in  a  starburst galaxy, by specifying a
-               negative sat value.
-
-     -seed num Sets the seed for the random number  generator  to
-               the  integer  num.   The  seed used to create each
-               picture is displayed on  standard  output  (unless
-               suppressed with the -quiet option).  Pictures gen-
-               erated with the same seed will be  identical.   If
-               no  -seed is specified, a random seed derived from
-               the date and time will be chosen.   Specifying  an
-               explicit  seed  allows  you to re-render a picture
-               you particularly like at a  higher  resolution  or
-               with different viewing parameters.
-
-     -stars fraction
-               Specifies the percentage of pixels, in tenths of a
-               percent,  which  will appear as stars, either sur-
-               rounding a planet or filling the entire  frame  if
-               -night is specified.  The default fraction is 100.
-
-     -xsize|-width width
-               Sets the width of the  generated  image  to  width
-               pixels.   The default width is 256 pixels.  Images
-               must be at least as wide as they are  high;  if  a
-               width  less  than the height is specified, it will
-               be increased to equal the  height.   If  you  must
-               have  a long skinny pixmap, make a square one with
-               ppmforge, then use pnmcut to extract a portion  of
-               the shape and size you require.
-
-     -ysize|-height height
-               Sets the height of the generated image  to  height
-               pixels.  The default height is 256 pixels.  If the
-               height specified exceeds the width, the width will
-               be increased to equal the height.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 Bugs
-     The algorithms require the output pixmap to be at  least  as
-     wide  as  it  is high, and the width to be an even number of
-     pixels.  These constraints are enforced  by  increasing  the
-     size of the requested pixmap if necessary.
-
-     You may have to reduce the FFT mesh size on machines with 16
-     bit integers and segmented pointer architectures.
-
-2 See_Also
-     pnmcut, pnmdepth, ppmdither, ppmquant, ppm
-
-     [1]  Voss, Richard  F.,  ``Random  Fractal  Forgeries,''  in
-          Earnshaw  et.  al., Fundamental Algorithms for Computer
-          Graphics, Berlin: Springer-Verlag, 1985.
-
-     [2]  Peitgen, H.-O., and Saupe,  D.  eds.,  The  Science  Of
-          Fractal Images, New York: Springer Verlag, 1988.
-
-2 Author
-          John Walker
-          Autodesk SA
-          Avenue des Champs-Montants 14b
-          CH-2074 MARIN
-          Suisse/Schweiz/Svizzera/Svizra/Switzerland
-          Usenet:  kelvin@Autodesk.com
-          Fax:     038/33 88 15
-          Voice:   038/33 76 33
-
-     Permission  to  use,  copy,  modify,  and  distribute   this
-     software  and  its documentation for any purpose and without
-     fee is hereby granted, without any  conditions  or  restric-
-     tions.   This software is provided ``as is'' without express
-     or implied warranty.
-
-     PLUGWARE! If you like this kind of stuff, you may also enjoy
-     ``James Gleick's Chaos--The Software'' for MS-DOS, available
-     for $59.95 from your local software store or  directly  from
-     Autodesk,  Inc.,  Attn:  Science Series, 2320 Marinship Way,
-     Sausalito, CA 94965, USA.  Telephone: (800)  688-2344  toll-
-     free  or,  outside  the  U.S. (415) 332-2344 Ext 4886.  Fax:
-     (415) 289-4718.  ``Chaos--The  Software''  includes  a  more
-     comprehensive   fractal   forgery  generator  which  creates
-     three-dimensional landscapes as well as clouds and  planets,
-     plus five more modules which explore other aspects of Chaos.
-     The user guide of more than 200 pages includes an  introduc-
-     tion by James Gleick and detailed explanations by Rudy Ruck-
-     er of the mathematics and algorithms used by each program.
-
-1 ppmpat
-     ppmpat - make a pretty pixmap
-
-2 Synopsis
-     ppmpat   -gingham2|-g2|-gingham3|   -g3|-madras|-tartan|   -
-     poles|-squig|-camo| -anticamo width height
-
-2 Description
-     Produces a  portable  pixmap  of  the  specified  width  and
-     height, with a pattern in it.
-
-     This program is mainly to demonstrate  use  of  the  ppmdraw
-     routines,  a  simple  but powerful drawing library.  See the
-     ppmdraw.h include file for more info  on  using  these  rou-
-     tines.   Still,  some  of the patterns can be rather pretty.
-     If you have  a  color  workstation,  something  like  ppmpat
-     -squig  300  300 | ppmquant 128 should generate a nice back-
-     ground.
-
-2 Options
-     The different flags specify various different pattern types:
-
-     -gingham2
-          A gingham check pattern.  Can be tiled.
-
-     -gingham3
-          A slightly more complicated gingham.  Can be tiled.
-
-     -madras
-          A madras plaid.  Can be tiled.
-
-     -tartan
-          A tartan plaid.  Can be tiled.
-
-     -poles
-          Color gradients centered on randomly-placed poles.  May
-          need to be run through ppmquant.
-
-     -squig
-          Squiggley tubular pattern.  Can be tiled.  May need  to
-          be run through ppmquant.
-
-     -camo
-          Camouflage  pattern.   May  need  to  be  run   through
-          ppmquant.
-
-     -anticamo
-          Anti-camouflage pattern - like -camo, but  ultra-bright
-          colors.  May need to be run through ppmquant.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 References
-     Some of the patterns are from "Designer's Guide to Color  3"
-     by Jeanne Allen.
-
-2 See_Also
-     pnmtile, ppmquant, ppm
-
-2 Author
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 ppmqvga
-     ppmqvga - 8 plane quantization
-
-2 Synopsis
-     ppmqvga [ options ] [ input file ]
-
-2 Description
-     ppmqvga quantizes PPM  files  to  8  planes,  with  optional
-     Floyd-Steinberg  dithering.   Input  is  a PPM file from the
-     file named, or standard input of no file is provided.
-
-2 Options
-     -d dither. Apply Floyd-Steinberg dithering to the data
-
-     -q quiet. Produces no progress reporting,  and  no  terminal
-     output unless and error occurs.
-
-     -v verbose. Produces additional output describing the number
-     of  colors found, and some information on the resulting map-
-     ping. May be repeated to generate loads  of  internal  table
-     output, but generally only useful once.
-
-2 Examples
-     ppmqvga -d mymage.ppm | ppmtogif >mymage.gif
-
-     tgatoppm zombie.tga | ppmqvga | ppmtotif > zombie.tif
-
-2 See_Also
-     ppmquant
-
-2 Diagnostics
-     Error messages if problems, various levels of optional  pro-
-     gress reporting.
-
-2 Limitations
-     none known.
-
-2 Author
-     Original by Lyle Rains (lrains@netcom.com)  as  ppmq256  and
-     ppmq256fs  combined, documented, and enhanced by Bill David-
-     sen (davidsen@crd.ge.com)
-
-     Copyright 1991,1992 by Bill Davidsen, all  rights  reserved.
-     The  program  and documentation may be freely distributed by
-     anyone in source or binary format. Please clearly  note  any
-     changes.
-
-1 ppmtomap
-     ppmtomap - extract all colors from a portable pixmap
-
-2 Synopsis
-     ppmtomap [-sort] [-square] [ppmfile]
-
-2 Description
-     Reads a portable pixmap as input.  Produces a portable  pix-
-     map  as  output, representing a color map of the input file.
-     All N different colors found are put in an Nx1 portable pix-
-     map.   This  color  map  file  can  be used as a mapfile for
-     ppmquant or ppmtogif.
-
-2 Options
-     -sort
-          Produces a portable pixmap  with  the  colors  in  some
-          sorted order.
-
-     -square
-          Produces a (more or less) square output  file,  instead
-          of putting all colors on the top row.
-
-     All flags can be abbreviated to their shortest  unique  pre-
-     fix.
-
-2 WARNING
-     If you want to use the output file as a mapfile  for  ppmto-
-     gif,  you first have to do a ppmquant 256, since ppmtomap is
-     not limited to 256 colors (but to 65536).
-
-2 See_Also
-     ppmtogif, ppmquant, ppm
-
-2 Author
-     Marcel Wijkstra (wijkstra@fwi.uva.nl).
-
-     Copyright (C) 1989 by Jef Poskanzer.
-
-1 ppmtopjxl
-     ppmtopjxl - convert a portable pixmap into an HP PaintJet XL
-     PCL file
-
-2 Synopsis
-     ppmtopjxl [-nopack] [-gamma <n>  ]  [-presentation]  [-dark]
-     [-diffuse]  [-cluster] [-dither] [-xshift <s> ] [-yshift <s>
-     ] [-xshift <s> ] [-yshift <s> ] [-xsize|-width|-xscale <s> ]
-     [-ysize|-height|-yscale <s> ] [ppmfile]
-
-
-2 Description
-     Reads a portable pixmap as input.  Produces a PCL file suit-
-     able for printing on an HP PaintJet XL printer as output.
-
-     The generated file is not suitable for printing on a  normal
-     PrintJet printer.  The -nopack option generates a file which
-     does not use the normal TIFF 4.0  compression  method.  This
-     file might be printable on a normal PaintJet printer (not an
-     XL).
-
-     The -gamma option sets the gamma correction for  the  image.
-     The useful range for the PaintJet XL is approximately 0.6 to
-     1.5.
-
-     The rendering algorithm used for images can be altered  with
-     the  -dither,  -cluster, and -diffuse options. These options
-     select ordered dithering, clustered  ordered  dithering,  or
-     error  diffusion respectively.  The -dark option can be used
-     to enhance images with  a  dark  background  when  they  are
-     reduced  in size.  The -presentation option turns on presen-
-     tation mode, in which two passes are made over the paper  to
-     increase  ink  density.  This should be used only for images
-     where quality is critical.
-
-
-     The image can be resized by setting the  -xsize  and  -ysize
-     options.  The parameter to either of these options is inter-
-     preted as the number of dots to set the width or height  to,
-     but  an  optional  dimension  of  `pt' (points), `dp' (deci-
-     points),  `in'  (inches),  or  `cm'  (centimetres)  may   be
-     appended.   If  only  one  dimension is specified, the other
-     will be scaled appropriately.
-
-     The options -width and -height are synonyms  of  -xsize  and
-     -ysize.
-
-     The -xscale and -yscale options can alternatively be used to
-     scale the image by a simple factor.
-
-     The image can be shifted on the page by  using  the  -xshift
-     and  -yshift  options.  These  move  the image the specified
-     dimensions right and down.
-
-
-2 See_Also
-     ppm
-
-2 Author
-     Angus Duggan
-
-1 libppm
-     libppm - functions to support portable pixmap programs
-
-2 Synopsis
-     #include <ppm.h>
-     cc ... libppm.a libpgm.a libpbm.a
-
-
-2 Description
-  TYPES AND CONSTANTS
-     typedef ... pixel;
-     typedef ... pixval;
-     #define PPM_MAXMAXVAL ...
-     extern pixval ppm_pbmmaxval;
-
-     Each pixel contains three pixvals, each of which should con-
-     tain   only   the   values   between  0  and  PPM_MAXMAXVAL.
-     ppm_pbmmaxval is the maxval used when a PPM program reads  a
-     PBM  file.   Normally it is 1; however, for some programs, a
-     larger value gives better results.
-
-     #define PPM_FORMAT ...
-     #define RPPM_FORMAT ...
-     #define PPM_TYPE PPM_FORMAT
-     int PPM_FORMAT_TYPE( int format )
-
-     For distinguishing different file formats and types.
-
-     pixval PPM_GETR( pixel p )
-     pixval PPM_GETG( pixel p )
-     pixval PPM_GETB( pixel p )
-
-     These three macros retrieve the red,  green  or  blue  value
-     from the given pixel.
-
-     void PPM_ASSIGN( pixel p, pixval red, pixval grn, pixval blu )
-
-     This macro assigns the given red, green and blue  values  to
-     the pixel.
-
-     int PPM_EQUAL( pixel p, pixel q )
-
-     This macro checks two pixels for equality.
-
-     void PPM_DEPTH( pixel newp, pixel p, pixval oldmaxval, pixval newmaxval )
-
-     This macro scales the colors of pixel p  according  the  old
-     and  new  maximum values and assigns the new values to newp.
-     It is intended to make writing ppmtowhatever easier.
-
-     float PPM_LUMIN( pixel p )
-     This macro determines the luminance of the pixel p.
-
-  MEMORY MANAGEMENT
-     pixel** ppm_allocarray( int cols, int rows )
-
-     Allocate an array of pixels.
-
-     pixel* ppm_allocrow( int cols )
-
-     Allocate a row of the given number of pixels.
-
-     void ppm_freearray( pixel** pixels, int rows )
-
-     Free the array allocated  with  ppm_allocarray()  containing
-     the given number of rows.
-
-     void pbm_freerow( pixel* pixelrow )
-
-     Free a row of pixels.
-
-  READING PBM FILES
-     void ppm_readppminit( FILE* fp, int* colsP, int* rowsP, pixval* maxvalP, int* formatP )
-
-     Read the header from a PPM file, filling in the rows,  cols,
-     maxval and format variables.
-
-     void ppm_readppmrow( FILE* fp, pixel* pixelrow, int cols, pixval maxval, int format )
-
-     Read a row of pixels into the pixelrow array.  Format, cols,
-     and maxval were filled in by ppm_readppminit().
-
-     pixel** ppm_readppm( FILE* fp, int* colsP, int* rowsP, pixval* maxvalP )
-
-     Read an entire pixmap file into memory, returning the  allo-
-     cated  array  and filling in the rows, cols and maxval vari-
-     ables.    This    function    combines    ppm_readppminit(),
-     ppm_allocarray() and ppm_readppmrow().
-
-  WRITING FILES
-     void ppm_writeppminit( FILE* fp, int cols, int rows, pixval maxval, int forceplain )
-
-     Write the header for a portable pixmap file.  The forceplain
-     flag forces a plain-format file to be written, as opposed to
-     a raw-format one.
-
-     void ppm_writeppmrow( FILE* fp, pixel* pixelrow, int cols, pixval maxval, int forceplain )
-
-     Write a row from a portable pixmap.
-
-     void ppm_writeppm( FILE* fp, pixel** pixels, int cols, int rows, pixval maxval, int forceplain )
-
-     Write the header and all data for a portable  pixmap.   This
-     function combines ppm_writeppminit() and ppm_writeppmrow().
-
-  COLOR NAMES
-     pixel ppm_parsecolor( char* colorname, pixval maxval )
-
-     Parses an ASCII color name into a pixel.  The color  can  be
-     specified  in  three  ways.  One, as a name, assuming that a
-     pointer to an X11-style color names file  was  compiled  in.
-     Two,  as  an  X11-style  hexadecimal  number: #rgb, #rrggbb,
-     #rrrgggbbb,  or  #rrrrggggbbbb.   Three,  as  a  triplet  of
-     decimal   floating   point   numbers  separated  by  commas:
-     r.r,g.g,b.b.
-
-     char* ppm_colorname( pixel* colorP, pixval maxval, int hexok )
-
-     Returns a pointer to a string describing  the  given  color.
-     If  the  X11  color  names  file  is available and the color
-     appears in it, that name is  returned.   Otherwise,  if  the
-     hexok flag is true then a hexadecimal colorspec is returned;
-     if hexok is false and the X11 color names file is available,
-     then the closest matching color is returned; otherwise, it's
-     an error.
-
-2 See_Also
-     pbm, pgm
-
-2 Author
-     Copyright (C) 1989, 1991 by Tony Hansen and Jef Poskanzer.
-
-1 ppm
-     ppm - portable pixmap file format
-
-2 Description
-     The portable pixmap format is a  lowest  common  denominator
-     color image file format.  The definition is as follows:
-
-     - A "magic number" for identifying the  file  type.   A  ppm
-       file's magic number is the two characters "P3".
-
-     - Whitespace (blanks, TABs, CRs, LFs).
-
-     - A width, formatted as ASCII characters in decimal.
-
-     - Whitespace.
-
-     - A height, again in ASCII decimal.
-
-     - Whitespace.
-
-     - The maximum color-component value, again in ASCII decimal.
-
-     - Whitespace.
-
-     - Width * height pixels, each  three  ASCII  decimal  values
-       between 0 and the specified maximum value, starting at the
-       top-left  corner  of  the  pixmap,  proceeding  in  normal
-       English  reading  order.   The three values for each pixel
-       represent red, green, and blue, respectively; a value of 0
-       means  that color is off, and the maximum value means that
-       color is maxxed out.
-
-     - Characters from a "#" to the next end-of-line are  ignored
-       (comments).
-
-     - No line should be longer than 70 characters.
-
-     Here is an example of a small pixmap in this format:
-     P3
-     # feep.ppm
-     4 4
-     15
-      0  0  0    0  0  0    0  0  0   15  0 15
-      0  0  0    0 15  7    0  0  0    0  0  0
-      0  0  0    0  0  0    0 15  7    0  0  0
-     15  0 15    0  0  0    0  0  0    0  0  0
-
-     Programs that read this format should be as lenient as  pos-
-     sible, accepting anything that looks remotely like a pixmap.
-
-     There is also a variant on the format, available by  setting
-     the  RAWBITS  option  at  compile  time.   This  variant  is
-     different in the following ways:
-
-     - The "magic number" is "P6" instead of "P3".
-
-     - The pixel values are stored as  plain  bytes,  instead  of
-       ASCII decimal.
-
-     - Whitespace is not allowed in the pixels area, and  only  a
-       single  character  of  whitespace (typically a newline) is
-       allowed after the maxval.
-
-     - The files are smaller and many times faster  to  read  and
-       write.
-
-     Note that this raw format can only be used for maxvals  less
-     than or equal to 255.  If you use the ppm library and try to
-     write a file with a larger  maxval,  it  will  automatically
-     fall back on the slower but more general plain format.
-
-2 See_Also
-     giftopnm, gouldtoppm, ilbmtoppm, imgtoppm, mtvtoppm, pcxtoppm,
-     pgmtoppm, pi1toppm, picttoppm, pjtoppm, qrttoppm, rawtoppm,
-     rgb3toppm, sldtoppm, spctoppm, sputoppm, tgatoppm, ximtoppm,
-     xpmtoppm, yuvtoppm, ppmtoacad, ppmtogif, ppmtoicr, ppmtoilbm,
-     ppmtopcx, ppmtopgm, ppmtopi1, ppmtopict, ppmtopj, ppmtopuzz,
-     ppmtorgb3, ppmtosixel, ppmtotga, ppmtouil, ppmtoxpm, ppmtoyuv,
-     ppmdither, ppmforge, ppmhist, ppmmake, ppmpat, ppmquant,
-     ppmquantall, ppmrelief, pnm, pgm, pbm
-
-2 Author
-     Copyright (C) 1989, 1991 by Jef Poskanzer.
-
-1 pgmkernel
-        pgmkernel - generate a convolution kernel
-
-2 Synopis
-        pgmkernel [-weight w] width [height]
-
-2 Description
-        Generates a portable graymap array of size width x height  (or
-    width  x  width  if  height  is  not  specified) to be used  as  a
-    convolution file  by pnmconvol.  The data in the convolution array
-    K are computed according to the formula:
-
-    K(i,j) = 1 / ( 1 + w * sqrt((i-width/2)\^{}2 + (j-height/2)\^{}2)) 
-
-    where w is a coefficient specified via the -weight flag, and width
-    and height are the X and Y filter sizes.
-
-        The output PGM file is always written out in ASCII format.
-
-2 Options
-        The optional -weight flag should be a real number greater than
-    -1.  The default value is 6.0.
-
-2 Bugs
-        The computation time is proportional  to width * height.  This
-    increases rapidly with the increase of  the kernel size.  A better
-    approach could be using a FFT in these cases.
-
-2 See_Also
-        pnmconvol(1), pnmsmooth(1)
-
-2 Author
-        Alberto Accomazzi (alberto@cfa.harvard.edu).
-
-1 fitstopnm
-        fitstopnm - convert a FITS file into a portable anymap
-
-2 Synopis
-        fitstopnm [-image N] [-noraw] [-scanmax] [-printmax] [-min f]
-                  [-max f] [FITSfile]
-
-2 Description
-        Reads a FITS file as input.  Produces a portable pixmap if the
-    FITS file consists of 3 image planes (NAXIS = 3 and NAXIS3 = 3), a
-    portable  graymap  if  the  FITS file consists of 2  image  planes
-    (NAXIS  =  2),  or  whenever  the -image flag is specified.    The
-    results may need to be flipped top for bottom;  if  so,  just pipe
-    the output through pnmflip -tb.
-
-2 Options
-        The  -image  option  is  for  FITS files with three axes.  The
-    assumption is that the third axis is for multiple images, and this
-    option lets you select which one you want.
-
-        Flags -min and  -max  can  be used to override the min and max
-    values as read from  the  FITS  header  or  the  image  data if no
-    DATAMIN and DATAMAX keywords are found.  Flag -scanmax can be used
-    to  force the program to scan  the  data  even  when  DATAMIN  and
-    DATAMAX are found in the header.   If  -printmax is specified, the
-    program will just print the min and max  values  and  quit.   Flag
-    -noraw  can  be  used  to force the program to  produce  an  ASCII
-    portable anymap.
-
-        The  program  will tell what kind of anymap is writing.    All
-    flags can be abbreviated to their shortest unique prefix.
-
-2 References
-        FITS stands for  Flexible  Image  Transport  System.    A full
-    description can be found  in  Astronomy  & Astrophysics Supplement
-    Series 44 (1981), page 363.
-
-2 See_Also
-        pnmtofits(1), pgm(5), pnmflip(1)
-
-2 Author
-        Copyright (C) 1989 by Jef  Poskanzer,  with  modifications  by
-    Daniel    Briggs    (dbriggs@nrao.edu)    and   Alberto  Accomazzi
-    (alberto@cfa.harvard.edu).
-
-1 pnmalias
-        pnmalias - antialias a portable anyumap.
-
-2 Synopis
-        pnmalias  [-bgcolor  color] [-fgcolor color] [-bonly] [-fonly]
-                  [-balias] [-falias] [-weight w] [pnmfile]
-
-2 Description
-        Reads a portable anymap as input, and applies anti-aliasing to
-    background and foreground pixels.  If the input file is a portable
-    bitmap,  the  output  anti-aliased image is promoted to a graymap,
-    and  a  message  is  printed  informing the user of the change  in
-    format.
-
-2 Options
-        -bgcolor colorb,
-        -fgcolor colorf
-        set the  background  color  to  colorb,  and the foreground to
-    color to colorf.    Pixels with these values will be anti-aliased.
-    by  default, the background  color  is  taken  to  be  black,  and
-    foreground color is assumed to  be  white.    The  colors  can  be
-    specified in five ways:
-
-            o A name,  assuming  that  a pointer to an X11-style color
-            names file was compiled in.
-
-            o An X11-style hexadecimal  specifier:  rgb:r/g/b, where r
-            g and b are each 1- to 4-digit hexadecimal numbers.
-
-            o An X11-style decimal specifier:    rgbi:r/g/b, where r g
-            and b are floating point numbers between 0 and 1.
-
-            o    For    backwards    compatibility,  an  old-X11-style
-            hexadecimal    number:    #rgb,  #rrggbb,  #rrrgggbbb,  or
-            #rrrrggggbbbb.
-
-            o  For  backwards  compatibility,  a  triplet  of  numbers
-            separated by commas:   r,g,b, where r g and b are floating
-            point numbers between 0  and  1.    (This  style was added
-            before MIT came up with the similar rgbi style.)
-
-        Note  that  even when dealing with  graymaps,  background  and
-    foreground colors need to be specified in  the  fashion  described
-    above.  In this case, background and foreground  pixel  values are
-    taken to be the value of the red component for the given color.
-
-        -bonly,
-        -fonly
-        Apply anti-aliasing only to background (-bonly), or foreground
-    (-fonly) pixels.
-
-        -balias,
-        -falias 
-        Apply  anti-aliasing  to  all  pixels  surrounding  background
-    (-balias),  or  foreground    (-falias)    pixels.    By  default,
-    anti-aliasing takes place only  among  neighboring  background and
-    foreground pixels.
-
-        -weight w
-        Use w as the central  weight  for the aliasing filter.  W must
-    be a real number in the range 0 < w < 1.  The lower the value of w
-    is, the "blurrier" the output image is.  The default is w = 1/3.
-
-2 See_Also
-        pbmtext(1), pnmsmooth(1), pnm(5)
-
-2 Author
-        Copyright    (C)    1992  by  Alberto  Accomazzi,  Smithsonian
-    Astrophysical Observatory.
-
-1 pnmtofits
-        pnmtofits - convert a portable anymap into FITS format
-
-2 Synopis
-        pnmtofits [-max f] [-min f] [pnmfile]
-
-2 Description
-        Reads a portable anymap as input.  Produces  a  FITS (Flexible
-    Image  Transport  System) file as output.  The resolution  of  the
-    output file is either 8 bits/pixel, or 16 bits/pixel, depending on
-    the value of maxval in the input file.  If the  input  file  is  a
-    portable bitmap or a portable graymap, the output file consists of
-    a single plane image (NAXIS = 2).   If instead the input file is a
-    portable  pixmap,  the output file will consist of  a  three-plane
-    image (NAXIS = 3, NAXIS3 = 3).  A  full  description  of  the FITS
-    format  can be found in Astronomy & Astrophysics Supplement Series
-    44 (1981), page 363.
-
-2 Options
-        Flags  -min  and  -max  can  be  used to set DATAMAX, DATAMIN,
-    BSCALE and BZERO in the FITS header, but do not cause the  data to
-    be rescaled.
-
-2 See_Also
-        fitstopnm(1), pgm(5)
-
-2 Author
-        Copyright (C) 1989 by Wilson  H.    Bent  (whb@hoh-2.att.com),
-    with modifications by Alberto Accomazzi (alberto@cfa.harvard.edu).
-
-1 ppmchange
-        ppmchange -  change  all  pixels  of one color to another in a
-    portable pixmap
-
-2 Synopis
-        ppmchange oldcolor newcolor [...] [ppmfile]
-
-2 Description
-        Reads  a portable pixmap as input.    Changes  all  pixels  of
-    oldcolor to newcolor, leaving all others unchanged.    Up  to  256
-    colors  may  be replaced by specifying couples of  colors  on  the
-    command line.  
-
-        The colors can be specified in five ways:
-
-            o A name, assuming that a  pointer  to  an X11-style color
-            names file was compiled in.
-
-            o An X11-style hexadecimal specifier:  rgb:r/g/b,  where r
-            g and b are each 1- to 4-digit hexadecimal numbers.
-
-            o An X11-style decimal specifier:  rgbi:r/g/b, where  r  g
-            and b are floating point numbers between 0 and 1.
-
-            o    For    backwards  compatibility,  an    old-X11-style
-            hexadecimal  number:    #rgb,  #rrggbb,  #rrrgggbbb,    or
-            #rrrrggggbbbb.
-
-            o  For  backwards  compatibility,  a  triplet  of  numbers
-            separated by commas:  r,g,b, where r g  and b are floating
-            point  numbers  between  0 and 1.  (This style  was  added
-            before MIT came up with the similar rgbi style.)
-
-2 See_Also
-        pgmtoppm(1), ppm(5)
-
-2 Author
-        Wilson  H.    Bent.   Jr.  (whb@usc.edu) with modifications by
-    Alberto Accomazzi (alberto@cfa.harvard.edu)
-
-1 xvminitoppm
-        xvminitoppm - convert a XV "thumbnail" picture to PPM
-
-2 Synopis
-        xvminitoppm [xvminipic]
-
-2 Description
-        Reads a  XV "thumbnail" picture (a miniature picture generated
-    by the "VisualSchnauzer"  browser)  as input.  Produces a portable
-    pixmap as output.
-
-2 See_Also
-        ppm(5), xv(1)
-
-2 Author
-        Copyright (C) 1993 by Ingo Wilken
-
diff --git a/vms/RGB.txt b/vms/RGB.txt
deleted file mode 100644
index e5f61889..00000000
--- a/vms/RGB.txt
+++ /dev/null
@@ -1,738 +0,0 @@
-255 250 250 snow
-248 248 255 ghost white
-248 248 255 GhostWhite
-245 245 245 white smoke
-245 245 245 WhiteSmoke
-220 220 220 gainsboro
-255 250 240 floral white
-255 250 240 FloralWhite
-253 245 230 old lace
-253 245 230 OldLace
-250 240 230 linen
-250 235 215 antique white
-250 235 215 AntiqueWhite
-255 239 213 papaya whip
-255 239 213 PapayaWhip
-255 235 205 blanched almond
-255 235 205 BlanchedAlmond
-255 228 196 bisque
-255 218 185 peach puff
-255 218 185 PeachPuff
-255 222 173 navajo white
-255 222 173 NavajoWhite
-255 228 181 moccasin
-255 248 220 cornsilk
-255 255 240 ivory
-255 250 205 lemon chiffon
-255 250 205 LemonChiffon
-255 245 238 seashell
-240 255 240 honeydew
-245 255 250 mint cream
-245 255 250 MintCream
-240 255 255 azure
-240 248 255 alice blue
-240 248 255 AliceBlue
-230 230 250 lavender
-255 240 245 lavender blush
-255 240 245 LavenderBlush
-255 228 225 misty rose
-255 228 225 MistyRose
-255 255 255 white
-  0   0   0 black
- 47  79  79 dark slate gray
- 47  79  79 DarkSlateGray
- 47  79  79 dark slate grey
- 47  79  79 DarkSlateGrey
-105 105 105 dim gray
-105 105 105 DimGray
-105 105 105 dim grey
-105 105 105 DimGrey
-112 128 144 slate gray
-112 128 144 SlateGray
-112 128 144 slate grey
-112 128 144 SlateGrey
-119 136 153 light slate gray
-119 136 153 LightSlateGray
-119 136 153 light slate grey
-119 136 153 LightSlateGrey
-190 190 190 gray
-190 190 190 grey
-211 211 211 light grey
-211 211 211 LightGrey
-211 211 211 light gray
-211 211 211 LightGray
- 25  25 112 midnight blue
- 25  25 112 MidnightBlue
-  0   0 128 navy
-  0   0 128 navy blue
-  0   0 128 NavyBlue
-100 149 237 cornflower blue
-100 149 237 CornflowerBlue
- 72  61 139 dark slate blue
- 72  61 139 DarkSlateBlue
-106  90 205 slate blue
-106  90 205 SlateBlue
-123 104 238 medium slate blue
-123 104 238 MediumSlateBlue
-132 112 255 light slate blue
-132 112 255 LightSlateBlue
-  0   0 205 medium blue
-  0   0 205 MediumBlue
- 65 105 225 royal blue
- 65 105 225 RoyalBlue
-  0   0 255 blue
- 30 144 255 dodger blue
- 30 144 255 DodgerBlue
-  0 191 255 deep sky blue
-  0 191 255 DeepSkyBlue
-135 206 235 sky blue
-135 206 235 SkyBlue
-135 206 250 light sky blue
-135 206 250 LightSkyBlue
- 70 130 180 steel blue
- 70 130 180 SteelBlue
-176 196 222 light steel blue
-176 196 222 LightSteelBlue
-173 216 230 light blue
-173 216 230 LightBlue
-176 224 230 powder blue
-176 224 230 PowderBlue
-175 238 238 pale turquoise
-175 238 238 PaleTurquoise
-  0 206 209 dark turquoise
-  0 206 209 DarkTurquoise
- 72 209 204 medium turquoise
- 72 209 204 MediumTurquoise
- 64 224 208 turquoise
-  0 255 255 cyan
-224 255 255 light cyan
-224 255 255 LightCyan
- 95 158 160 cadet blue
- 95 158 160 CadetBlue
-102 205 170 medium aquamarine
-102 205 170 MediumAquamarine
-127 255 212 aquamarine
-  0 100   0 dark green
-  0 100   0 DarkGreen
- 85 107  47 dark olive green
- 85 107  47 DarkOliveGreen
-143 188 143 dark sea green
-143 188 143 DarkSeaGreen
- 46 139  87 sea green
- 46 139  87 SeaGreen
- 60 179 113 medium sea green
- 60 179 113 MediumSeaGreen
- 32 178 170 light sea green
- 32 178 170 LightSeaGreen
-152 251 152 pale green
-152 251 152 PaleGreen
-  0 255 127 spring green
-  0 255 127 SpringGreen
-124 252   0 lawn green
-124 252   0 LawnGreen
-  0 255   0 green
-127 255   0 chartreuse
-  0 250 154 medium spring green
-  0 250 154 MediumSpringGreen
-173 255  47 green yellow
-173 255  47 GreenYellow
- 50 205  50 lime green
- 50 205  50 LimeGreen
-154 205  50 yellow green
-154 205  50 YellowGreen
- 34 139  34 forest green
- 34 139  34 ForestGreen
-107 142  35 olive drab
-107 142  35 OliveDrab
-189 183 107 dark khaki
-189 183 107 DarkKhaki
-240 230 140 khaki
-238 232 170 pale goldenrod
-238 232 170 PaleGoldenrod
-250 250 210 light goldenrod yellow
-250 250 210 LightGoldenrodYellow
-255 255 224 light yellow
-255 255 224 LightYellow
-255 255   0 yellow
-255 215   0 gold
-238 221 130 light goldenrod
-238 221 130 LightGoldenrod
-218 165  32 goldenrod
-184 134  11 dark goldenrod
-184 134  11 DarkGoldenrod
-188 143 143 rosy brown
-188 143 143 RosyBrown
-205  92  92 indian red
-205  92  92 IndianRed
-139  69  19 saddle brown
-139  69  19 SaddleBrown
-160  82  45 sienna
-205 133  63 peru
-222 184 135 burlywood
-245 245 220 beige
-245 222 179 wheat
-244 164  96 sandy brown
-244 164  96 SandyBrown
-210 180 140 tan
-210 105  30 chocolate
-178  34  34 firebrick
-165  42  42 brown
-233 150 122 dark salmon
-233 150 122 DarkSalmon
-250 128 114 salmon
-255 160 122 light salmon
-255 160 122 LightSalmon
-255 165   0 orange
-255 140   0 dark orange
-255 140   0 DarkOrange
-255 127  80 coral
-240 128 128 light coral
-240 128 128 LightCoral
-255  99  71 tomato
-255  69   0 orange red
-255  69   0 OrangeRed
-255   0   0 red
-255 105 180 hot pink
-255 105 180 HotPink
-255  20 147 deep pink
-255  20 147 DeepPink
-255 192 203 pink
-255 182 193 light pink
-255 182 193 LightPink
-219 112 147 pale violet red
-219 112 147 PaleVioletRed
-176  48  96 maroon
-199  21 133 medium violet red
-199  21 133 MediumVioletRed
-208  32 144 violet red
-208  32 144 VioletRed
-255   0 255 magenta
-238 130 238 violet
-221 160 221 plum
-218 112 214 orchid
-186  85 211 medium orchid
-186  85 211 MediumOrchid
-153  50 204 dark orchid
-153  50 204 DarkOrchid
-148   0 211 dark violet
-148   0 211 DarkViolet
-138  43 226 blue violet
-138  43 226 BlueViolet
-160  32 240 purple
-147 112 219 medium purple
-147 112 219 MediumPurple
-216 191 216 thistle
-255 250 250 snow1
-238 233 233 snow2
-205 201 201 snow3
-139 137 137 snow4
-255 245 238 seashell1
-238 229 222 seashell2
-205 197 191 seashell3
-139 134 130 seashell4
-255 239 219 AntiqueWhite1
-238 223 204 AntiqueWhite2
-205 192 176 AntiqueWhite3
-139 131 120 AntiqueWhite4
-255 228 196 bisque1
-238 213 183 bisque2
-205 183 158 bisque3
-139 125 107 bisque4
-255 218 185 PeachPuff1
-238 203 173 PeachPuff2
-205 175 149 PeachPuff3
-139 119 101 PeachPuff4
-255 222 173 NavajoWhite1
-238 207 161 NavajoWhite2
-205 179 139 NavajoWhite3
-139 121  94 NavajoWhite4
-255 250 205 LemonChiffon1
-238 233 191 LemonChiffon2
-205 201 165 LemonChiffon3
-139 137 112 LemonChiffon4
-255 248 220 cornsilk1
-238 232 205 cornsilk2
-205 200 177 cornsilk3
-139 136 120 cornsilk4
-255 255 240 ivory1
-238 238 224 ivory2
-205 205 193 ivory3
-139 139 131 ivory4
-240 255 240 honeydew1
-224 238 224 honeydew2
-193 205 193 honeydew3
-131 139 131 honeydew4
-255 240 245 LavenderBlush1
-238 224 229 LavenderBlush2
-205 193 197 LavenderBlush3
-139 131 134 LavenderBlush4
-255 228 225 MistyRose1
-238 213 210 MistyRose2
-205 183 181 MistyRose3
-139 125 123 MistyRose4
-240 255 255 azure1
-224 238 238 azure2
-193 205 205 azure3
-131 139 139 azure4
-131 111 255 SlateBlue1
-122 103 238 SlateBlue2
-105  89 205 SlateBlue3
- 71  60 139 SlateBlue4
- 72 118 255 RoyalBlue1
- 67 110 238 RoyalBlue2
- 58  95 205 RoyalBlue3
- 39  64 139 RoyalBlue4
-  0   0 255 blue1
-  0   0 238 blue2
-  0   0 205 blue3
-  0   0 139 blue4
- 30 144 255 DodgerBlue1
- 28 134 238 DodgerBlue2
- 24 116 205 DodgerBlue3
- 16  78 139 DodgerBlue4
- 99 184 255 SteelBlue1
- 92 172 238 SteelBlue2
- 79 148 205 SteelBlue3
- 54 100 139 SteelBlue4
-  0 191 255 DeepSkyBlue1
-  0 178 238 DeepSkyBlue2
-  0 154 205 DeepSkyBlue3
-  0 104 139 DeepSkyBlue4
-135 206 255 SkyBlue1
-126 192 238 SkyBlue2
-108 166 205 SkyBlue3
- 74 112 139 SkyBlue4
-176 226 255 LightSkyBlue1
-164 211 238 LightSkyBlue2
-141 182 205 LightSkyBlue3
- 96 123 139 LightSkyBlue4
-198 226 255 SlateGray1
-185 211 238 SlateGray2
-159 182 205 SlateGray3
-108 123 139 SlateGray4
-202 225 255 LightSteelBlue1
-188 210 238 LightSteelBlue2
-162 181 205 LightSteelBlue3
-110 123 139 LightSteelBlue4
-191 239 255 LightBlue1
-178 223 238 LightBlue2
-154 192 205 LightBlue3
-104 131 139 LightBlue4
-224 255 255 LightCyan1
-209 238 238 LightCyan2
-180 205 205 LightCyan3
-122 139 139 LightCyan4
-187 255 255 PaleTurquoise1
-174 238 238 PaleTurquoise2
-150 205 205 PaleTurquoise3
-102 139 139 PaleTurquoise4
-152 245 255 CadetBlue1
-142 229 238 CadetBlue2
-122 197 205 CadetBlue3
- 83 134 139 CadetBlue4
-  0 245 255 turquoise1
-  0 229 238 turquoise2
-  0 197 205 turquoise3
-  0 134 139 turquoise4
-  0 255 255 cyan1
-  0 238 238 cyan2
-  0 205 205 cyan3
-  0 139 139 cyan4
-151 255 255 DarkSlateGray1
-141 238 238 DarkSlateGray2
-121 205 205 DarkSlateGray3
- 82 139 139 DarkSlateGray4
-127 255 212 aquamarine1
-118 238 198 aquamarine2
-102 205 170 aquamarine3
- 69 139 116 aquamarine4
-193 255 193 DarkSeaGreen1
-180 238 180 DarkSeaGreen2
-155 205 155 DarkSeaGreen3
-105 139 105 DarkSeaGreen4
- 84 255 159 SeaGreen1
- 78 238 148 SeaGreen2
- 67 205 128 SeaGreen3
- 46 139  87 SeaGreen4
-154 255 154 PaleGreen1
-144 238 144 PaleGreen2
-124 205 124 PaleGreen3
- 84 139  84 PaleGreen4
-  0 255 127 SpringGreen1
-  0 238 118 SpringGreen2
-  0 205 102 SpringGreen3
-  0 139  69 SpringGreen4
-  0 255   0 green1
-  0 238   0 green2
-  0 205   0 green3
-  0 139   0 green4
-127 255   0 chartreuse1
-118 238   0 chartreuse2
-102 205   0 chartreuse3
- 69 139   0 chartreuse4
-192 255  62 OliveDrab1
-179 238  58 OliveDrab2
-154 205  50 OliveDrab3
-105 139  34 OliveDrab4
-202 255 112 DarkOliveGreen1
-188 238 104 DarkOliveGreen2
-162 205  90 DarkOliveGreen3
-110 139  61 DarkOliveGreen4
-255 246 143 khaki1
-238 230 133 khaki2
-205 198 115 khaki3
-139 134  78 khaki4
-255 236 139 LightGoldenrod1
-238 220 130 LightGoldenrod2
-205 190 112 LightGoldenrod3
-139 129  76 LightGoldenrod4
-255 255 224 LightYellow1
-238 238 209 LightYellow2
-205 205 180 LightYellow3
-139 139 122 LightYellow4
-255 255   0 yellow1
-238 238   0 yellow2
-205 205   0 yellow3
-139 139   0 yellow4
-255 215   0 gold1
-238 201   0 gold2
-205 173   0 gold3
-139 117   0 gold4
-255 193  37 goldenrod1
-238 180  34 goldenrod2
-205 155  29 goldenrod3
-139 105  20 goldenrod4
-255 185  15 DarkGoldenrod1
-238 173  14 DarkGoldenrod2
-205 149  12 DarkGoldenrod3
-139 101   8 DarkGoldenrod4
-255 193 193 RosyBrown1
-238 180 180 RosyBrown2
-205 155 155 RosyBrown3
-139 105 105 RosyBrown4
-255 106 106 IndianRed1
-238  99  99 IndianRed2
-205  85  85 IndianRed3
-139  58  58 IndianRed4
-255 130  71 sienna1
-238 121  66 sienna2
-205 104  57 sienna3
-139  71  38 sienna4
-255 211 155 burlywood1
-238 197 145 burlywood2
-205 170 125 burlywood3
-139 115  85 burlywood4
-255 231 186 wheat1
-238 216 174 wheat2
-205 186 150 wheat3
-139 126 102 wheat4
-255 165  79 tan1
-238 154  73 tan2
-205 133  63 tan3
-139  90  43 tan4
-255 127  36 chocolate1
-238 118  33 chocolate2
-205 102  29 chocolate3
-139  69  19 chocolate4
-255  48  48 firebrick1
-238  44  44 firebrick2
-205  38  38 firebrick3
-139  26  26 firebrick4
-255  64  64 brown1
-238  59  59 brown2
-205  51  51 brown3
-139  35  35 brown4
-255 140 105 salmon1
-238 130  98 salmon2
-205 112  84 salmon3
-139  76  57 salmon4
-255 160 122 LightSalmon1
-238 149 114 LightSalmon2
-205 129  98 LightSalmon3
-139  87  66 LightSalmon4
-255 165   0 orange1
-238 154   0 orange2
-205 133   0 orange3
-139  90   0 orange4
-255 127   0 DarkOrange1
-238 118   0 DarkOrange2
-205 102   0 DarkOrange3
-139  69   0 DarkOrange4
-255 114  86 coral1
-238 106  80 coral2
-205  91  69 coral3
-139  62  47 coral4
-255  99  71 tomato1
-238  92  66 tomato2
-205  79  57 tomato3
-139  54  38 tomato4
-255  69   0 OrangeRed1
-238  64   0 OrangeRed2
-205  55   0 OrangeRed3
-139  37   0 OrangeRed4
-255   0   0 red1
-238   0   0 red2
-205   0   0 red3
-139   0   0 red4
-255  20 147 DeepPink1
-238  18 137 DeepPink2
-205  16 118 DeepPink3
-139  10  80 DeepPink4
-255 110 180 HotPink1
-238 106 167 HotPink2
-205  96 144 HotPink3
-139  58  98 HotPink4
-255 181 197 pink1
-238 169 184 pink2
-205 145 158 pink3
-139  99 108 pink4
-255 174 185 LightPink1
-238 162 173 LightPink2
-205 140 149 LightPink3
-139  95 101 LightPink4
-255 130 171 PaleVioletRed1
-238 121 159 PaleVioletRed2
-205 104 137 PaleVioletRed3
-139  71  93 PaleVioletRed4
-255  52 179 maroon1
-238  48 167 maroon2
-205  41 144 maroon3
-139  28  98 maroon4
-255  62 150 VioletRed1
-238  58 140 VioletRed2
-205  50 120 VioletRed3
-139  34  82 VioletRed4
-255   0 255 magenta1
-238   0 238 magenta2
-205   0 205 magenta3
-139   0 139 magenta4
-255 131 250 orchid1
-238 122 233 orchid2
-205 105 201 orchid3
-139  71 137 orchid4
-255 187 255 plum1
-238 174 238 plum2
-205 150 205 plum3
-139 102 139 plum4
-224 102 255 MediumOrchid1
-209  95 238 MediumOrchid2
-180  82 205 MediumOrchid3
-122  55 139 MediumOrchid4
-191  62 255 DarkOrchid1
-178  58 238 DarkOrchid2
-154  50 205 DarkOrchid3
-104  34 139 DarkOrchid4
-155  48 255 purple1
-145  44 238 purple2
-125  38 205 purple3
- 85  26 139 purple4
-171 130 255 MediumPurple1
-159 121 238 MediumPurple2
-137 104 205 MediumPurple3
- 93  71 139 MediumPurple4
-255 225 255 thistle1
-238 210 238 thistle2
-205 181 205 thistle3
-139 123 139 thistle4
-  0   0   0 gray0
-  0   0   0 grey0
-  3   3   3 gray1
-  3   3   3 grey1
-  5   5   5 gray2
-  5   5   5 grey2
-  8   8   8 gray3
-  8   8   8 grey3
- 10  10  10 gray4
- 10  10  10 grey4
- 13  13  13 gray5
- 13  13  13 grey5
- 15  15  15 gray6
- 15  15  15 grey6
- 18  18  18 gray7
- 18  18  18 grey7
- 20  20  20 gray8
- 20  20  20 grey8
- 23  23  23 gray9
- 23  23  23 grey9
- 26  26  26 gray10
- 26  26  26 grey10
- 28  28  28 gray11
- 28  28  28 grey11
- 31  31  31 gray12
- 31  31  31 grey12
- 33  33  33 gray13
- 33  33  33 grey13
- 36  36  36 gray14
- 36  36  36 grey14
- 38  38  38 gray15
- 38  38  38 grey15
- 41  41  41 gray16
- 41  41  41 grey16
- 43  43  43 gray17
- 43  43  43 grey17
- 46  46  46 gray18
- 46  46  46 grey18
- 48  48  48 gray19
- 48  48  48 grey19
- 51  51  51 gray20
- 51  51  51 grey20
- 54  54  54 gray21
- 54  54  54 grey21
- 56  56  56 gray22
- 56  56  56 grey22
- 59  59  59 gray23
- 59  59  59 grey23
- 61  61  61 gray24
- 61  61  61 grey24
- 64  64  64 gray25
- 64  64  64 grey25
- 66  66  66 gray26
- 66  66  66 grey26
- 69  69  69 gray27
- 69  69  69 grey27
- 71  71  71 gray28
- 71  71  71 grey28
- 74  74  74 gray29
- 74  74  74 grey29
- 77  77  77 gray30
- 77  77  77 grey30
- 79  79  79 gray31
- 79  79  79 grey31
- 82  82  82 gray32
- 82  82  82 grey32
- 84  84  84 gray33
- 84  84  84 grey33
- 87  87  87 gray34
- 87  87  87 grey34
- 89  89  89 gray35
- 89  89  89 grey35
- 92  92  92 gray36
- 92  92  92 grey36
- 94  94  94 gray37
- 94  94  94 grey37
- 97  97  97 gray38
- 97  97  97 grey38
- 99  99  99 gray39
- 99  99  99 grey39
-102 102 102 gray40
-102 102 102 grey40
-105 105 105 gray41
-105 105 105 grey41
-107 107 107 gray42
-107 107 107 grey42
-110 110 110 gray43
-110 110 110 grey43
-112 112 112 gray44
-112 112 112 grey44
-115 115 115 gray45
-115 115 115 grey45
-117 117 117 gray46
-117 117 117 grey46
-120 120 120 gray47
-120 120 120 grey47
-122 122 122 gray48
-122 122 122 grey48
-125 125 125 gray49
-125 125 125 grey49
-127 127 127 gray50
-127 127 127 grey50
-130 130 130 gray51
-130 130 130 grey51
-133 133 133 gray52
-133 133 133 grey52
-135 135 135 gray53
-135 135 135 grey53
-138 138 138 gray54
-138 138 138 grey54
-140 140 140 gray55
-140 140 140 grey55
-143 143 143 gray56
-143 143 143 grey56
-145 145 145 gray57
-145 145 145 grey57
-148 148 148 gray58
-148 148 148 grey58
-150 150 150 gray59
-150 150 150 grey59
-153 153 153 gray60
-153 153 153 grey60
-156 156 156 gray61
-156 156 156 grey61
-158 158 158 gray62
-158 158 158 grey62
-161 161 161 gray63
-161 161 161 grey63
-163 163 163 gray64
-163 163 163 grey64
-166 166 166 gray65
-166 166 166 grey65
-168 168 168 gray66
-168 168 168 grey66
-171 171 171 gray67
-171 171 171 grey67
-173 173 173 gray68
-173 173 173 grey68
-176 176 176 gray69
-176 176 176 grey69
-179 179 179 gray70
-179 179 179 grey70
-181 181 181 gray71
-181 181 181 grey71
-184 184 184 gray72
-184 184 184 grey72
-186 186 186 gray73
-186 186 186 grey73
-189 189 189 gray74
-189 189 189 grey74
-191 191 191 gray75
-191 191 191 grey75
-194 194 194 gray76
-194 194 194 grey76
-196 196 196 gray77
-196 196 196 grey77
-199 199 199 gray78
-199 199 199 grey78
-201 201 201 gray79
-201 201 201 grey79
-204 204 204 gray80
-204 204 204 grey80
-207 207 207 gray81
-207 207 207 grey81
-209 209 209 gray82
-209 209 209 grey82
-212 212 212 gray83
-212 212 212 grey83
-214 214 214 gray84
-214 214 214 grey84
-217 217 217 gray85
-217 217 217 grey85
-219 219 219 gray86
-219 219 219 grey86
-222 222 222 gray87
-222 222 222 grey87
-224 224 224 gray88
-224 224 224 grey88
-227 227 227 gray89
-227 227 227 grey89
-229 229 229 gray90
-229 229 229 grey90
-232 232 232 gray91
-232 232 232 grey91
-235 235 235 gray92
-235 235 235 grey92
-237 237 237 gray93
-237 237 237 grey93
-240 240 240 gray94
-240 240 240 grey94
-242 242 242 gray95
-242 242 242 grey95
-245 245 245 gray96
-245 245 245 grey96
-247 247 247 gray97
-247 247 247 grey97
-250 250 250 gray98
-250 250 250 grey98
-252 252 252 gray99
-252 252 252 grey99
-255 255 255 gray100
-255 255 255 grey100
diff --git a/vms/SetUp.com b/vms/SetUp.com
deleted file mode 100755
index 077318b5..00000000
--- a/vms/SetUp.com
+++ /dev/null
@@ -1,37 +0,0 @@
-$ VERIFY = F$Verify (0)
-$ On Error Then GoTo EXIT
-$ Write Sys$Output "SETting UP PBMplus (ver netpbm-VMS)..."
-$! 
-$!  Keep this proc in the top directory of the PBMPLUS tree. Execute it from 
-$!  anywhere and it will set up command symbols for all executables in the
-$!  PBMplus_Root:[EXE] directory.
-$!  There is a problem if this directory is located in a "rooted"
-$!  directory structure already.  It is not possible to define a "rooted"
-$!  directory twice, i.e.:
-$! BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD
-$! BAD                                                                 BAD
-$! BAD     Define /Trans=conceal Public Disk$:[Dir.]                   BAD
-$! BAD     Define /Trans=conceal PBMplus_Root Public:[PBMplus.]        BAD
-$! BAD                                                                 BAD
-$! BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD BAD
-$!  THIS WILL NOT WORK!  In this case, you will have to manually define
-$!  PBMplus_Root instead of the autosensing feature below....
-$! 
-$ PBMPLUS_PATH = F$Element (0, "]", F$Environment ("PROCEDURE")) + ".]"
-$ Define /Translation_Attributes = Concealed PBMplus_Root "''PBMPLUS_PATH'"
-$ Define PBMplus_Dir PBMplus_Root:[000000]
-$ Define PBMplusShr PBMplus_Dir:PBMplusShr
-$ NAME = "PBMplus_Root:[Exe]*.EXE"
-$LOOP:
-$   PROG = F$Search (NAME)
-$   If PROG .nes. ""
-$       Then
-$           PROG = PROG - F$Parse (PROG, , , "VERSION")
-$           CMD = F$Parse (PROG, , , "NAME")
-$           'CMD' :== $ 'PROG'
-$       GoTo LOOP
-$   EndIf
-$   @ PBMplus_Dir:ADD_LIST Hlp$Library PBMplus_Dir:PBMPLUS.HLB
-$EXIT:
-$   VERIFY = F$Verify (VERIFY)
-$   Exit
diff --git a/vms/stamp-date.com b/vms/stamp-date.com
deleted file mode 100755
index 349f6626..00000000
--- a/vms/stamp-date.com
+++ /dev/null
@@ -1,25 +0,0 @@
-$!# Copyright (C) 1993 by Oliver Trepte.
-$!#
-$!# 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.
-$!#
-$!# This shell script creates a file called "compile_time.h" which holds
-$!# a define stating the compilation time. This is used for the -version
-$!# flag.
-$!#
-$!# modified to a VMS command procedure by Rick Dyson 29-NOV-1993
-
-$ OUTFILE   := "compile.h"
-$ DATE      := "''F$Time ()'"
-$ USER      := "''F$GetJPI (F$GetJPI ("", "PID"), "USERNAME")'"
-$ Open /Write OUTFILE compile.h
-$ Write OUTFILE "/* compile_time.h  This file tells the package when it was compiled */"
-$ Write OUTFILE "/* DO NOT EDIT - THIS FILE IS MAINTAINED AUTOMATICALLY              */"
-$ Write OUTFILE "#define COMPILE_TIME ""''DATE'"""
-$ Write OUTFILE "#define COMPILED_BY ""''USER'"""
-$ Close OUTFILE
-$ Exit